diff --git a/api/src/main/resources/example.application.yaml b/api/src/main/resources/example.application.yaml index bc9db98c..53102d18 100644 --- a/api/src/main/resources/example.application.yaml +++ b/api/src/main/resources/example.application.yaml @@ -57,13 +57,13 @@ loginsvc: # Set the order of the protocol starting from 1 # Set to 0 to disable or simply exclude the ldap tag from config # NOTE: At least 1 auth protocol needs to be enabled - order: 2 + order: 0 domain: "some.domain.com" url: "ldaps://some.domain.com:636/" search-filter: "(samaccountname={1})" service-account: # The account-pattern is used to format the username of the user to be used to authenticate with the ldap server. - account-pattern: "CN=%s,OU=Users,OU=CORP Accounts,DC=corp,DC=dsarena,DC=com" + account-pattern: "CN=%s,OU=Users,OU=Organization1,DC=my,DC=domain,DC=com" # The in-config-account is used to authenticate with the ldap server. in-config-account: username: "svc-ldap" diff --git a/api/src/main/scala/za/co/absa/loginsvc/rest/AuthManagerConfig.scala b/api/src/main/scala/za/co/absa/loginsvc/rest/AuthManagerConfig.scala index 479fd0d3..42d5be59 100644 --- a/api/src/main/scala/za/co/absa/loginsvc/rest/AuthManagerConfig.scala +++ b/api/src/main/scala/za/co/absa/loginsvc/rest/AuthManagerConfig.scala @@ -33,8 +33,8 @@ import scala.collection.immutable.SortedMap @Configuration class AuthManagerConfig @Autowired()(authConfigProvider: AuthConfigProvider){ - private val usersConfig: UsersConfig = authConfigProvider.getUsersConfig - private val adLDAPConfig: ActiveDirectoryLDAPConfig = authConfigProvider.getLdapConfig + private val usersConfig: Option[UsersConfig] = authConfigProvider.getUsersConfig + private val adLDAPConfig: Option[ActiveDirectoryLDAPConfig] = authConfigProvider.getLdapConfig private val logger = LoggerFactory.getLogger(classOf[AuthManagerConfig]) @@ -42,25 +42,25 @@ class AuthManagerConfig @Autowired()(authConfigProvider: AuthConfigProvider){ def authManager(http: HttpSecurity): AuthenticationManager = { val authenticationManagerBuilder = http.getSharedObject(classOf[AuthenticationManagerBuilder]).parentAuthenticationManager(null) - val configs: Array[DynamicAuthOrder] = Array(usersConfig, adLDAPConfig) + val configs: Array[DynamicAuthOrder] = Array(usersConfig, adLDAPConfig).flatten val orderedProviders = createProviders(configs) if(orderedProviders.isEmpty) throw ConfigValidationException("No authentication method enabled in config") - orderedProviders.foreach( + orderedProviders.zipWithIndex.foreach( auth => { - logger.info(s"Authentication method ${auth._2.getClass.getSimpleName} has been initialized at order ${auth._1}") - authenticationManagerBuilder.authenticationProvider(auth._2) + logger.info(s"Authentication method ${auth._1.getClass.getSimpleName} has been initialized at order ${auth._2 + 1}") + authenticationManagerBuilder.authenticationProvider(auth._1) }) authenticationManagerBuilder.build } - private def createProviders(configs: Array[DynamicAuthOrder]): SortedMap[Int, AuthenticationProvider] = { - SortedMap.empty[Int, AuthenticationProvider] ++ configs.filter(_.order > 0) + private def createProviders(configs: Array[DynamicAuthOrder]): Array[AuthenticationProvider] = { + Array.empty[AuthenticationProvider] ++ configs.filter(_.order > 0).sortBy(_.order) .map { - case c: UsersConfig => (c.order, new ConfigUsersAuthenticationProvider(c)) - case c: ActiveDirectoryLDAPConfig => (c.order, new ActiveDirectoryLDAPAuthenticationProvider(c)) + case c: UsersConfig => new ConfigUsersAuthenticationProvider(c) + case c: ActiveDirectoryLDAPConfig => new ActiveDirectoryLDAPAuthenticationProvider(c) case other => throw new IllegalStateException(s"unsupported config $other") } } diff --git a/api/src/main/scala/za/co/absa/loginsvc/rest/config/auth/ActiveDirectoryLDAPConfig.scala b/api/src/main/scala/za/co/absa/loginsvc/rest/config/auth/ActiveDirectoryLDAPConfig.scala index b346db44..711b2d5c 100644 --- a/api/src/main/scala/za/co/absa/loginsvc/rest/config/auth/ActiveDirectoryLDAPConfig.scala +++ b/api/src/main/scala/za/co/absa/loginsvc/rest/config/auth/ActiveDirectoryLDAPConfig.scala @@ -74,7 +74,7 @@ case class ActiveDirectoryLDAPConfig(domain: String, } case class ServiceAccountConfig(private val accountPattern: String, - private val inConfigAccount: Option[IntegratedLdapUserConfig], + private val inConfigAccount: Option[LdapUserCredentialsConfig], private val awsSecretsManagerAccount: Option[AwsSecretsLdapUserConfig]) { private val ldapUserDetails: LdapUser = (inConfigAccount, awsSecretsManagerAccount) match { @@ -82,22 +82,31 @@ case class ServiceAccountConfig(private val accountPattern: String, throw ConfigValidationException("Both integratedLdapUserConfig and awsSecretsLdapUserConfig exist. Please choose only one.") case (None, None) => - throw ConfigValidationException("Neither integratedLdapUserConfig nor awsSecretsLdapUserConfig exists. One of them should exist.") + throw ConfigValidationException("Neither integratedLdapUserConfig nor awsSecretsLdapUserConfig exists. Exactly one of them should be present.") - case _ => - val ldapConfig: LdapUser = inConfigAccount.orElse(awsSecretsManagerAccount).get - ldapConfig.throwErrors() + case (inConfig@Some(_), None) => + val ldapConfig: LdapUser = inConfig.get + ldapConfig.throwOnErrors() + + ldapConfig + + case (None, awsConfig@Some(_)) => + val ldapConfig: LdapUser = awsConfig.get + ldapConfig.throwOnErrors() ldapConfig + + case _ => + throw ConfigValidationException("Error with current config concerning integratedLdapUserConfig or awsSecretsLdapUserConfig") } val username: String = String.format(accountPattern, ldapUserDetails.username) val password: String = ldapUserDetails.password } -case class IntegratedLdapUserConfig(username: String, password: String) extends LdapUser +case class LdapUserCredentialsConfig (username: String, password: String) extends LdapUser { - def throwErrors(): Unit = this.validate().throwOnErrors() + def throwOnErrors(): Unit = this.validate().throwOnErrors() } case class AwsSecretsLdapUserConfig(private val secretName: String, @@ -108,7 +117,7 @@ case class AwsSecretsLdapUserConfig(private val secretName: String, private val logger = LoggerFactory.getLogger(classOf[LdapUser]) val (username, password) = getUsernameAndPasswordFromSecret - def throwErrors(): Unit = this.validate().throwOnErrors() + def throwOnErrors(): Unit = this.validate().throwOnErrors() override def validate(): ConfigValidationResult = { val results = Seq( Option(secretName) @@ -163,8 +172,7 @@ case class AwsSecretsLdapUserConfig(private val secretName: String, trait LdapUser extends ConfigValidatable { def username: String def password: String - - def throwErrors(): Unit + def throwOnErrors(): Unit override def validate(): ConfigValidationResult = { val results = Seq( diff --git a/api/src/main/scala/za/co/absa/loginsvc/rest/config/provider/AuthConfigProvider.scala b/api/src/main/scala/za/co/absa/loginsvc/rest/config/provider/AuthConfigProvider.scala index 847db2cf..cc0a067f 100644 --- a/api/src/main/scala/za/co/absa/loginsvc/rest/config/provider/AuthConfigProvider.scala +++ b/api/src/main/scala/za/co/absa/loginsvc/rest/config/provider/AuthConfigProvider.scala @@ -19,6 +19,6 @@ package za.co.absa.loginsvc.rest.config.provider import za.co.absa.loginsvc.rest.config.auth._ trait AuthConfigProvider { - def getLdapConfig : ActiveDirectoryLDAPConfig - def getUsersConfig : UsersConfig + def getLdapConfig : Option[ActiveDirectoryLDAPConfig] + def getUsersConfig : Option[UsersConfig] } diff --git a/api/src/main/scala/za/co/absa/loginsvc/rest/config/provider/ConfigProvider.scala b/api/src/main/scala/za/co/absa/loginsvc/rest/config/provider/ConfigProvider.scala index c137240a..0b365baa 100644 --- a/api/src/main/scala/za/co/absa/loginsvc/rest/config/provider/ConfigProvider.scala +++ b/api/src/main/scala/za/co/absa/loginsvc/rest/config/provider/ConfigProvider.scala @@ -58,36 +58,37 @@ class ConfigProvider(@Value("${spring.config.location}") yamlPath: String) case (None, None) => throw ConfigValidationException("Neither inMemoryKeyConfig nor awsSecretsManagerKeyConfig exists. One of them should exist.") - case _ => - val keyConfig: KeyConfig = inMemoryKeyConfig.orElse(awsSecretsManagerKeyConfig).get + case (inMemoryKeyConfig@Some(_), None) => + val keyConfig: KeyConfig = inMemoryKeyConfig.get + keyConfig.throwErrors() + + keyConfig + + case (None, awsSecretsManagerKeyConfig@Some(_)) => + val keyConfig: KeyConfig = awsSecretsManagerKeyConfig.get keyConfig.throwErrors() keyConfig + + case _ => + throw ConfigValidationException("Error with current config concerning inMemoryKeyConfig or awsSecretsManagerKeyConfig") } } - def getLdapConfig : ActiveDirectoryLDAPConfig = { + def getLdapConfig : Option[ActiveDirectoryLDAPConfig] = { val ldapConfigOption = createConfigClass[ActiveDirectoryLDAPConfig]("loginsvc.rest.auth.provider.ldap") if(ldapConfigOption.nonEmpty) - { - val ldapConfig = ldapConfigOption.get - ldapConfig.throwErrors() - ldapConfig - } - else{ - val emptyServiceAccount = ServiceAccountConfig("", Option(IntegratedLdapUserConfig("","")), None) - ActiveDirectoryLDAPConfig("", "", "", 0, emptyServiceAccount, None) - } + ldapConfigOption.get.throwErrors() + + ldapConfigOption } - def getUsersConfig : UsersConfig = { + def getUsersConfig : Option[UsersConfig] = { val userConfigOption = createConfigClass[UsersConfig]("loginsvc.rest.auth.provider.users") - if (userConfigOption.nonEmpty) { - val userConfig = userConfigOption.get - userConfig.throwErrors() - userConfig - } - else UsersConfig(Array.empty[UserConfig], 0) + if (userConfigOption.nonEmpty) + userConfigOption.get.throwErrors() + + userConfigOption } private def getGitConfig: GitConfig = { diff --git a/api/src/main/scala/za/co/absa/loginsvc/rest/controller/TokenController.scala b/api/src/main/scala/za/co/absa/loginsvc/rest/controller/TokenController.scala index ac64ad81..af4be469 100644 --- a/api/src/main/scala/za/co/absa/loginsvc/rest/controller/TokenController.scala +++ b/api/src/main/scala/za/co/absa/loginsvc/rest/controller/TokenController.scala @@ -28,7 +28,7 @@ import org.springframework.security.core.Authentication import org.springframework.web.bind.annotation._ import za.co.absa.loginsvc.model.User import za.co.absa.loginsvc.rest.model.{PublicKey, TokensWrapper} -import za.co.absa.loginsvc.rest.service.JWTService +import za.co.absa.loginsvc.rest.service.jwt.JWTService import za.co.absa.loginsvc.utils.OptionUtils.ImplicitBuilderExt import java.util.concurrent.CompletableFuture diff --git a/api/src/main/scala/za/co/absa/loginsvc/rest/service/JWTService.scala b/api/src/main/scala/za/co/absa/loginsvc/rest/service/jwt/JWTService.scala similarity index 97% rename from api/src/main/scala/za/co/absa/loginsvc/rest/service/JWTService.scala rename to api/src/main/scala/za/co/absa/loginsvc/rest/service/jwt/JWTService.scala index 849befc3..caf6a673 100644 --- a/api/src/main/scala/za/co/absa/loginsvc/rest/service/JWTService.scala +++ b/api/src/main/scala/za/co/absa/loginsvc/rest/service/jwt/JWTService.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.loginsvc.rest.service +package za.co.absa.loginsvc.rest.service.jwt import com.nimbusds.jose.JWSAlgorithm import com.nimbusds.jose.jwk.{JWKSet, KeyUse, RSAKey} @@ -25,7 +25,8 @@ import org.springframework.stereotype.Service import za.co.absa.loginsvc.model.User import za.co.absa.loginsvc.rest.config.provider.JwtConfigProvider import za.co.absa.loginsvc.rest.model.{AccessToken, RefreshToken, Token} -import za.co.absa.loginsvc.rest.service.JWTService.extractUserFrom +import za.co.absa.loginsvc.rest.service.jwt.JWTService.extractUserFrom +import za.co.absa.loginsvc.rest.service.search.AuthSearchService import java.security.interfaces.RSAPublicKey import java.security.{KeyPair, PublicKey} @@ -127,7 +128,6 @@ class JWTService @Autowired()(jwtConfigProvider: JwtConfigProvider, authSearchSe try { val searchedUser = authSearchService.searchUser(userFromOldAccessToken.name) val prefixedGroups = searchedUser.groups.intersect(userFromOldAccessToken.groups) // only keep groups that were in old token - User(searchedUser.name, prefixedGroups, searchedUser.optionalAttributes) } catch { case _: Throwable => throw new UnsupportedJwtException(s"User ${userFromOldAccessToken.name} not found") diff --git a/api/src/main/scala/za/co/absa/loginsvc/rest/service/search/AuthSearchProvider.scala b/api/src/main/scala/za/co/absa/loginsvc/rest/service/search/AuthSearchProvider.scala new file mode 100644 index 00000000..d26a97cc --- /dev/null +++ b/api/src/main/scala/za/co/absa/loginsvc/rest/service/search/AuthSearchProvider.scala @@ -0,0 +1,23 @@ +/* + * Copyright 2023 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.loginsvc.rest.service.search + +import za.co.absa.loginsvc.model.User + +trait AuthSearchProvider { + def searchForUser(username: String): Option[User] +} diff --git a/api/src/main/scala/za/co/absa/loginsvc/rest/service/search/AuthSearchService.scala b/api/src/main/scala/za/co/absa/loginsvc/rest/service/search/AuthSearchService.scala new file mode 100644 index 00000000..1b1fbe89 --- /dev/null +++ b/api/src/main/scala/za/co/absa/loginsvc/rest/service/search/AuthSearchService.scala @@ -0,0 +1,59 @@ +/* + * Copyright 2023 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.loginsvc.rest.service.search + +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Service +import za.co.absa.loginsvc.model.User +import za.co.absa.loginsvc.rest.config.auth.{ActiveDirectoryLDAPConfig, DynamicAuthOrder, UsersConfig} +import za.co.absa.loginsvc.rest.config.provider.AuthConfigProvider +import za.co.absa.loginsvc.rest.config.validation.ConfigValidationException + +@Service +class AuthSearchService @Autowired()(authConfigProvider: AuthConfigProvider) { + + private val logger = LoggerFactory.getLogger(classOf[AuthSearchService]) + + private val usersConfig: Option[DynamicAuthOrder] = authConfigProvider.getUsersConfig + private val adLDAPConfig: Option[DynamicAuthOrder] = authConfigProvider.getLdapConfig + + private val configs: Array[DynamicAuthOrder] = Array(usersConfig, adLDAPConfig).flatten.filter(_.order != 0).sortBy(_.order) + private val orderedProviders = createProviders(configs) + + if (orderedProviders.isEmpty) + throw ConfigValidationException("No authentication method enabled in config") + + def searchUser(username: String): User = { + orderedProviders.foreach { provider => + val user = provider.searchForUser(username) + if (user.isDefined) { + return user.get + } + } + throw new NoSuchElementException(s"Value not found in any object.") + } + + private def createProviders(configs: Array[DynamicAuthOrder]): Array[AuthSearchProvider] = { + Array.empty[AuthSearchProvider] ++ configs.filter(_.order > 0).sortBy(_.order) + .map { + case c: UsersConfig => new ConfigSearchProvider(c) + case c: ActiveDirectoryLDAPConfig => new LdapSearchProvider(c) + case other => throw new IllegalStateException(s"unsupported config $other") + } + } +} diff --git a/api/src/main/scala/za/co/absa/loginsvc/rest/service/search/ConfigSearchProvider.scala b/api/src/main/scala/za/co/absa/loginsvc/rest/service/search/ConfigSearchProvider.scala new file mode 100644 index 00000000..d1b8fe96 --- /dev/null +++ b/api/src/main/scala/za/co/absa/loginsvc/rest/service/search/ConfigSearchProvider.scala @@ -0,0 +1,37 @@ +/* + * Copyright 2023 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.loginsvc.rest.service.search + +import org.slf4j.LoggerFactory +import za.co.absa.loginsvc.model.User +import za.co.absa.loginsvc.rest.config.auth.UsersConfig + +class ConfigSearchProvider(usersConfig: UsersConfig) + extends AuthSearchProvider { + + private val logger = LoggerFactory.getLogger(classOf[ConfigSearchProvider]) + def searchForUser(username: String): Option[User] = { + logger.info(s"Searching for user in config: $username") + usersConfig.knownUsersMap.get(username).flatMap { userConfig => + val optionalAttributes: Map[String, Option[AnyRef]] = userConfig.attributes.getOrElse(Map.empty).map { + case (k, v) => (k, Some(v)) + } + logger.info(s"User found in config: $username") + Option(User(userConfig.username, userConfig.groups.toList, optionalAttributes)) + } + } +} diff --git a/api/src/main/scala/za/co/absa/loginsvc/rest/service/AuthSearchService.scala b/api/src/main/scala/za/co/absa/loginsvc/rest/service/search/LdapSearchProvider.scala similarity index 54% rename from api/src/main/scala/za/co/absa/loginsvc/rest/service/AuthSearchService.scala rename to api/src/main/scala/za/co/absa/loginsvc/rest/service/search/LdapSearchProvider.scala index 74620544..f7cdc967 100644 --- a/api/src/main/scala/za/co/absa/loginsvc/rest/service/AuthSearchService.scala +++ b/api/src/main/scala/za/co/absa/loginsvc/rest/service/search/LdapSearchProvider.scala @@ -14,15 +14,11 @@ * limitations under the License. */ -package za.co.absa.loginsvc.rest.service +package za.co.absa.loginsvc.rest.service.search import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.stereotype.Service import za.co.absa.loginsvc.model.User -import za.co.absa.loginsvc.rest.config.auth.{ActiveDirectoryLDAPConfig, UsersConfig} -import za.co.absa.loginsvc.rest.config.provider.AuthConfigProvider -import za.co.absa.loginsvc.rest.config.validation.ConfigValidationException +import za.co.absa.loginsvc.rest.config.auth.ActiveDirectoryLDAPConfig import java.util import javax.naming.Context @@ -30,66 +26,39 @@ import javax.naming.directory.{Attributes, DirContext, SearchControls, SearchRes import javax.naming.ldap.{Control, InitialLdapContext, PagedResultsControl} import scala.collection.JavaConverters.enumerationAsScalaIteratorConverter -@Service -class AuthSearchService @Autowired()(authConfigProvider: AuthConfigProvider) { +class LdapSearchProvider(activeDirectoryLDAPConfig: ActiveDirectoryLDAPConfig) + extends AuthSearchProvider { - private val logger = LoggerFactory.getLogger(classOf[AuthSearchService]) + private val logger = LoggerFactory.getLogger(classOf[LdapSearchProvider]) - private val usersConfig = authConfigProvider.getUsersConfig - private val adLDAPConfig = authConfigProvider.getLdapConfig - - private val configs = Array(usersConfig, adLDAPConfig).filter(_.order != 0).sortBy(_.order) - if (configs.isEmpty) - throw ConfigValidationException("No authentication method enabled in config") - - def searchUser(username: String): User = { - - configs.foreach { - case u: UsersConfig => - val result = searchUsersConfig(username) - if (result.isDefined) return result.get - case l: ActiveDirectoryLDAPConfig => - val result = searchLDAP(username) - if (result.isDefined) return result.get - case _ => //ignore for now - } - throw new NoSuchElementException(s"Value not found in any object.") - } - - private def searchUsersConfig(username: String): Option[User] = { - usersConfig.knownUsersMap.get(username).flatMap { userConfig => - val optionalAttributes: Map[String, Option[AnyRef]] = userConfig.attributes.getOrElse(Map.empty).map { - case (k, v) => (k, Some(v)) - } - Some(User(username, userConfig.groups.toList, optionalAttributes)) - } - } - - private def searchLDAP(username: String): Option[User] = { - val serviceAccount = adLDAPConfig.serviceAccount + def searchForUser(username: String): Option[User] = { + logger.info(s"Searching for user in Ldap: $username") + val serviceAccount = activeDirectoryLDAPConfig.serviceAccount val context = getDirContext(serviceAccount.username, serviceAccount.password) - try { - val users = context - .search(adLDAPConfig.domain.split("\\.").map(part => s"dc=$part").mkString(","), - adLDAPConfig.searchFilter.replace("{1}", username), - getSimpleSearchControls) - .asScala.filter(filterSearchResults).map(resultToUserEntry).toList - - if (users.nonEmpty) { - println("User found in LDAP") - Option(users.head) - } - else - throw new Exception(s"$username not found in LDAP") - } finally { - context.close() + try { + val users = context + .search(activeDirectoryLDAPConfig.domain.split("\\.").map(part => s"dc=$part").mkString(","), + activeDirectoryLDAPConfig.searchFilter.replace("{1}", username), + getSimpleSearchControls) + .asScala.filter(filterSearchResults).map(resultToUserEntry).toList + + if (users.nonEmpty) { + logger.info(s"User found in Ldap: $username") + Option(users.head) + } + else { + logger.info(s"User could not be found in Ldap: $username") + None } + } finally { + context.close() + } } private def getDirContext(principal: String, credential: String): DirContext = { - logger.info(String.format("principal: %s", principal)) + logger.info(String.format("Service Account Principal: %s", principal)) val env: util.Hashtable[String, String] = new util.Hashtable[String, String] - env.put(Context.PROVIDER_URL, adLDAPConfig.url) + env.put(Context.PROVIDER_URL, activeDirectoryLDAPConfig.url) env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory") env.put(Context.SECURITY_AUTHENTICATION, "simple") env.put(Context.SECURITY_PRINCIPAL, principal) @@ -124,7 +93,7 @@ class AuthSearchService @Autowired()(authConfigProvider: AuthConfigProvider) { private def resultToUserEntry(result: SearchResult): User = { val attrs: Attributes = result.getAttributes - val optionalAttr= adLDAPConfig.attributes.getOrElse(Map.empty) + val optionalAttr = activeDirectoryLDAPConfig.attributes.getOrElse(Map.empty) val username = attrs.get("sAMAccountName").get.toString val groups = attrs.get("memberOf").getAll.asScala.map(group => { diff --git a/api/src/test/resources/application.yaml b/api/src/test/resources/application.yaml index 7286d54b..c281ed35 100644 --- a/api/src/test/resources/application.yaml +++ b/api/src/test/resources/application.yaml @@ -19,7 +19,7 @@ loginsvc: url: "ldaps://some.domain.com:636/" search-filter: "(samaccountname={1})" service-account: - account-pattern: "CN=%s,OU=Users,OU=CORP Accounts,DC=corp,DC=dsarena,DC=com" + account-pattern: "CN=%s,OU=Users,OU=Organization1,DC=my,DC=domain,DC=com" in-config-account: username: "svc-ldap" password: "password" diff --git a/api/src/test/scala/za/co/absa/loginsvc/rest/config/auth/ActiveDirectoryLDAPConfigTest.scala b/api/src/test/scala/za/co/absa/loginsvc/rest/config/auth/ActiveDirectoryLDAPConfigTest.scala index 97fa2c8b..d342b391 100644 --- a/api/src/test/scala/za/co/absa/loginsvc/rest/config/auth/ActiveDirectoryLDAPConfigTest.scala +++ b/api/src/test/scala/za/co/absa/loginsvc/rest/config/auth/ActiveDirectoryLDAPConfigTest.scala @@ -23,7 +23,7 @@ import za.co.absa.loginsvc.rest.config.validation.ConfigValidationResult.{Config class ActiveDirectoryLDAPConfigTest extends AnyFlatSpec with Matchers { - private val integratedCfg = IntegratedLdapUserConfig("svc-ldap", "password") + private val integratedCfg = LdapUserCredentialsConfig("svc-ldap", "password") private val serviceAccountCfg = ServiceAccountConfig( "CN=%s,OU=Users,OU=CORP Accounts,DC=corp,DC=dsarena,DC=com", Option(integratedCfg), diff --git a/api/src/test/scala/za/co/absa/loginsvc/rest/config/provider/ConfigProviderTest.scala b/api/src/test/scala/za/co/absa/loginsvc/rest/config/provider/ConfigProviderTest.scala index 9e4e7469..74c6505f 100644 --- a/api/src/test/scala/za/co/absa/loginsvc/rest/config/provider/ConfigProviderTest.scala +++ b/api/src/test/scala/za/co/absa/loginsvc/rest/config/provider/ConfigProviderTest.scala @@ -46,7 +46,7 @@ class ConfigProviderTest extends AnyFlatSpec with Matchers { } "The ldapConfig properties" should "Match" in { - val activeDirectoryLDAPConfig: ActiveDirectoryLDAPConfig = configProvider.getLdapConfig + val activeDirectoryLDAPConfig: ActiveDirectoryLDAPConfig = configProvider.getLdapConfig.get activeDirectoryLDAPConfig.url shouldBe "ldaps://some.domain.com:636/" activeDirectoryLDAPConfig.domain shouldBe "some.domain.com" activeDirectoryLDAPConfig.searchFilter shouldBe "(samaccountname={1})" @@ -57,7 +57,7 @@ class ConfigProviderTest extends AnyFlatSpec with Matchers { } "The usersConfig properties" should "be loaded correctly" in { - val usersConfig: UsersConfig = configProvider.getUsersConfig + val usersConfig: UsersConfig = configProvider.getUsersConfig.get usersConfig.order shouldBe 1 usersConfig.knownUsers(0).groups(0) shouldBe "group1" diff --git a/api/src/test/scala/za/co/absa/loginsvc/rest/controller/TokenControllerTest.scala b/api/src/test/scala/za/co/absa/loginsvc/rest/controller/TokenControllerTest.scala index b792f95c..840dc0f3 100644 --- a/api/src/test/scala/za/co/absa/loginsvc/rest/controller/TokenControllerTest.scala +++ b/api/src/test/scala/za/co/absa/loginsvc/rest/controller/TokenControllerTest.scala @@ -29,7 +29,7 @@ import org.springframework.context.annotation.Import import org.springframework.test.web.servlet.MockMvc import za.co.absa.loginsvc.model.User import za.co.absa.loginsvc.rest.model.{AccessToken, RefreshToken} -import za.co.absa.loginsvc.rest.service.JWTService +import za.co.absa.loginsvc.rest.service.jwt.JWTService import za.co.absa.loginsvc.rest.{FakeAuthentication, RestResponseEntityExceptionHandler, SecurityConfig} import java.security.interfaces.RSAPublicKey @@ -55,9 +55,9 @@ class TokenControllerTest extends AnyFlatSpec behavior of "generateToken" - val fakeAccessJwt = AccessToken("abc.fakeJWTToken.abc") - val fakeRefreshJwt = RefreshToken("ab.fakeJWTToken.cd") - val refreshDuration = 10.minutes + val fakeAccessJwt: AccessToken = AccessToken("abc.fakeJWTToken.abc") + val fakeRefreshJwt: RefreshToken = RefreshToken("ab.fakeJWTToken.cd") + val refreshDuration: FiniteDuration = 10.minutes it should "return tokens generated by mocked JWTService for the authenticated user" in { when(jwtService.generateAccessToken(FakeAuthentication.fakeUser)).thenReturn(fakeAccessJwt) diff --git a/api/src/test/scala/za/co/absa/loginsvc/rest/service/AuthSearchServiceTest.scala b/api/src/test/scala/za/co/absa/loginsvc/rest/service/AuthSearchServiceTest.scala index e4e43ab8..ed46222a 100644 --- a/api/src/test/scala/za/co/absa/loginsvc/rest/service/AuthSearchServiceTest.scala +++ b/api/src/test/scala/za/co/absa/loginsvc/rest/service/AuthSearchServiceTest.scala @@ -20,18 +20,19 @@ import org.scalatest.BeforeAndAfterEach import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import za.co.absa.loginsvc.model.User -import za.co.absa.loginsvc.rest.config.auth.{ActiveDirectoryLDAPConfig, IntegratedLdapUserConfig, ServiceAccountConfig, UserConfig, UsersConfig} -import za.co.absa.loginsvc.rest.config.provider.{AuthConfigProvider, ConfigProvider, JwtConfigProvider} +import za.co.absa.loginsvc.rest.config.auth.{ActiveDirectoryLDAPConfig, LdapUserCredentialsConfig, ServiceAccountConfig, UserConfig, UsersConfig} +import za.co.absa.loginsvc.rest.config.provider.{AuthConfigProvider, ConfigProvider} import za.co.absa.loginsvc.rest.config.validation.ConfigValidationException +import za.co.absa.loginsvc.rest.service.search.AuthSearchService class AuthSearchServiceTest extends AnyFlatSpec with BeforeAndAfterEach with Matchers { private val testConfig: ConfigProvider = new ConfigProvider("api/src/test/resources/application.yaml") - private val emptyServiceAccount = ServiceAccountConfig("", Option(IntegratedLdapUserConfig("", "")), None) + private val emptyServiceAccount = ServiceAccountConfig("", Option(LdapUserCredentialsConfig("", "")), None) private val authConfigProvider: AuthConfigProvider = new AuthConfigProvider { - override def getLdapConfig: ActiveDirectoryLDAPConfig = ActiveDirectoryLDAPConfig("", "", "", 0, emptyServiceAccount, None) - override def getUsersConfig: UsersConfig = testConfig.getUsersConfig + override def getLdapConfig: Option[ActiveDirectoryLDAPConfig] = Option(ActiveDirectoryLDAPConfig("", "", "", 0, emptyServiceAccount, None)) + override def getUsersConfig: Option[UsersConfig] = testConfig.getUsersConfig } private val authSearchService: AuthSearchService = new AuthSearchService(authConfigProvider) @@ -56,8 +57,8 @@ class AuthSearchServiceTest extends AnyFlatSpec with BeforeAndAfterEach with Mat it should "fail if no auth config is provided" in { val emptyAuthConfigProvider: AuthConfigProvider = new AuthConfigProvider { - override def getLdapConfig: ActiveDirectoryLDAPConfig = ActiveDirectoryLDAPConfig("", "", "", 0, emptyServiceAccount, None) - override def getUsersConfig: UsersConfig = UsersConfig(Array.empty[UserConfig], 0) + override def getLdapConfig: Option[ActiveDirectoryLDAPConfig] = Option(ActiveDirectoryLDAPConfig("", "", "", 0, emptyServiceAccount, None)) + override def getUsersConfig: Option[UsersConfig] = Option(UsersConfig(Array.empty[UserConfig], 0)) } an [ConfigValidationException] should be thrownBy { diff --git a/api/src/test/scala/za/co/absa/loginsvc/rest/service/JWTServiceTest.scala b/api/src/test/scala/za/co/absa/loginsvc/rest/service/JWTServiceTest.scala index e7226518..308a2b36 100644 --- a/api/src/test/scala/za/co/absa/loginsvc/rest/service/JWTServiceTest.scala +++ b/api/src/test/scala/za/co/absa/loginsvc/rest/service/JWTServiceTest.scala @@ -26,6 +26,8 @@ import za.co.absa.loginsvc.model.User import za.co.absa.loginsvc.rest.config.jwt.{InMemoryKeyConfig, KeyConfig} import za.co.absa.loginsvc.rest.config.provider.{ConfigProvider, JwtConfigProvider} import za.co.absa.loginsvc.rest.model.{AccessToken, RefreshToken, Token} +import za.co.absa.loginsvc.rest.service.jwt.JWTService +import za.co.absa.loginsvc.rest.service.search.AuthSearchService import java.security.PublicKey import java.util