-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added Test LdapProvider just to test kerberosLdapSearch
- Loading branch information
1 parent
69cab58
commit 0f11ae1
Showing
5 changed files
with
145 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
api/src/main/scala/za/co/absa/loginsvc/rest/provider/kerberos/KerberosLdapUserSearch.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package za.co.absa.loginsvc.rest.provider.kerberos | ||
|
||
import org.springframework.ldap.core.DirContextOperations | ||
import org.springframework.ldap.core.support.BaseLdapPathContextSource | ||
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch | ||
|
||
class KerberosLdapUserSearch (searchBase: String, searchFilter: String, contextSource: BaseLdapPathContextSource) | ||
extends FilterBasedLdapUserSearch(searchBase, searchFilter, contextSource) { | ||
|
||
override def searchForUser(username: String): DirContextOperations = { | ||
val user = if (username.contains("@")) { | ||
username.split("@").head | ||
} else { | ||
username | ||
} | ||
super.searchForUser(user) | ||
} | ||
} |
File renamed without changes.
100 changes: 100 additions & 0 deletions
100
...la/za/co/absa/loginsvc/rest/provider/kerberos/RestApiKerberosAuthenticationProvider.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package za.co.absa.loginsvc.rest.provider.kerberos | ||
|
||
import org.slf4j.LoggerFactory | ||
import org.springframework.security.authentication.{AuthenticationProvider, BadCredentialsException, UsernamePasswordAuthenticationToken} | ||
import org.springframework.security.core.Authentication | ||
import org.springframework.security.ldap.userdetails.LdapUserDetailsService | ||
import sun.security.krb5.KrbException | ||
|
||
import java.net.{SocketTimeoutException, UnknownHostException} | ||
import java.security.PrivilegedActionException | ||
import javax.security.auth.Subject | ||
import javax.security.auth.callback.{Callback, CallbackHandler, NameCallback, PasswordCallback} | ||
import javax.security.auth.login.{AppConfigurationEntry, Configuration, LoginContext, LoginException} | ||
import scala.util.control.NonFatal | ||
|
||
class RestApiKerberosAuthenticationProvider (adServer: String, searchFilter: String, baseDN: String) | ||
extends AuthenticationProvider { | ||
|
||
case class RestApiKerberosLoginResult(loginContext: LoginContext, verifiedName: String) | ||
|
||
private val logger = LoggerFactory.getLogger(this.getClass) | ||
|
||
override def authenticate(authentication: Authentication): Authentication = { | ||
val output = try { | ||
val auth = authentication.asInstanceOf[UsernamePasswordAuthenticationToken] | ||
val loginResult = login(auth.getName, auth.getCredentials.toString) | ||
val userDetailsService = getUserDetailService(loginResult.loginContext.getSubject) | ||
val userDetails = userDetailsService.loadUserByUsername(loginResult.verifiedName) | ||
loginResult.loginContext.logout() | ||
new UsernamePasswordAuthenticationToken(userDetails, auth.getCredentials, userDetails.getAuthorities) | ||
} catch { | ||
case ex: LoginException => | ||
ex.getCause match { | ||
// This is thrown when there is an issue contacting a KDC specified in krb5.conf for the realm | ||
case nestedException: SocketTimeoutException => | ||
throw new Exception(s"Timeout host: ${nestedException.getMessage}", ex) | ||
case nestedException: UnknownHostException => | ||
throw new Exception(s"Unknown authentication host: ${nestedException.getMessage}", ex) | ||
case nestedException: KrbException => | ||
throw new BadCredentialsException(s"Invalid credentials: ${nestedException.getMessage}", ex) | ||
case NonFatal(_) => | ||
throw ex | ||
} | ||
// This is thrown when there is an issue contacting an LDAP server specified in REST API configuration | ||
case ex: PrivilegedActionException => | ||
throw new Exception(ex.toString, ex) | ||
case NonFatal(ex) => | ||
throw ex | ||
} | ||
output.setDetails(authentication.getDetails) | ||
output | ||
} | ||
|
||
override def supports(authentication: Class[_]): Boolean = { | ||
classOf[UsernamePasswordAuthenticationToken].isAssignableFrom(authentication) | ||
} | ||
|
||
private def login(username: String, password: String): RestApiKerberosLoginResult = { | ||
val loginContext = new LoginContext("", null, getSpringCBHandler(username, password), getLoginConfig) // scalastyle:ignore null | ||
loginContext.login() | ||
val loggedInUser = loginContext.getSubject.getPrincipals.iterator.next.toString | ||
logger.debug(s"Logged In User: $loggedInUser") | ||
RestApiKerberosLoginResult(loginContext, loggedInUser) | ||
} | ||
|
||
private def getSpringCBHandler(username: String, password: String) = { | ||
new CallbackHandler() { | ||
def handle(callbacks: Array[Callback]): Unit = { | ||
callbacks.foreach({ | ||
case ncb: NameCallback => ncb.setName(username) | ||
case pwdcb: PasswordCallback => pwdcb.setPassword(password.toCharArray) | ||
}) | ||
} | ||
} | ||
} | ||
|
||
private def getUserDetailService(subject: Subject) = { | ||
val contextSource = new RestApiKerberosLdapContextSource(adServer, subject) | ||
contextSource.afterPropertiesSet() | ||
val userSearch = new KerberosLdapUserSearch(baseDN, searchFilter, contextSource) | ||
new LdapUserDetailsService(userSearch, new ActiveDirectoryLdapAuthoritiesPopulator()) | ||
} | ||
|
||
private def getLoginConfig: Configuration = { | ||
import scala.collection.JavaConverters._ | ||
|
||
new Configuration { | ||
override def getAppConfigurationEntry(name: String): Array[AppConfigurationEntry] = { | ||
val opts = Map( | ||
"storeKey" -> "true", | ||
"refreshKrb5Config" -> "true", | ||
"isInitiator" -> "true", | ||
"debug" -> "true") | ||
Array(new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", | ||
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, opts.asJava)) | ||
} | ||
} | ||
} | ||
} | ||
|
23 changes: 23 additions & 0 deletions
23
...n/scala/za/co/absa/loginsvc/rest/provider/kerberos/RestApiKerberosLdapContextSource.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package za.co.absa.loginsvc.rest.provider.kerberos | ||
|
||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource | ||
|
||
import java.security.PrivilegedAction | ||
import javax.naming.Context | ||
import javax.naming.directory.DirContext | ||
import javax.security.auth.Subject | ||
|
||
class RestApiKerberosLdapContextSource (url: String, subject: Subject) extends DefaultSpringSecurityContextSource(url) { | ||
override def getDirContextInstance(environment: java.util.Hashtable[String, Object]): DirContext = { | ||
|
||
environment.put(Context.SECURITY_AUTHENTICATION, "GSSAPI") | ||
val sup = super.getDirContextInstance _ | ||
|
||
logger.debug(s"Trying to authenticate to LDAP as ${subject.getPrincipals}") | ||
Subject.doAs(subject, new PrivilegedAction[DirContext]() { | ||
override def run(): DirContext = { | ||
sup(environment) | ||
} | ||
}) | ||
} | ||
} |