This is an automated email from the ASF dual-hosted git repository. bhliva pushed a commit to branch ldab_refactored in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git
commit 9260408088fd9a46f0377055c21a329ccc78b6e7 Author: bhliva <[email protected]> AuthorDate: Tue Jan 29 17:16:13 2019 +0200 EPMCDLAB-000 refactored ldap authentication --- .../main/java/com/epam/dlab/auth/UserInfoDAO.java | 11 +- .../epam/dlab/auth/UserVerificationService.java | 2 +- .../aws/service/AwsUserVerificationService.java | 16 +- .../auth/azure/AzureAuthenticationResource.java | 10 +- services/security-service/pom.xml | 6 + services/security-service/security.yml | 112 +++-------- .../dlab/auth/SecurityServiceConfiguration.java | 37 +++- .../epam/dlab/auth/core/CacheableReference.java | 53 ----- .../epam/dlab/auth/core/DlabLdapConnection.java | 2 +- .../dlab/auth/core/DlabLdapConnectionFactory.java | 48 +++++ .../com/epam/dlab/auth/core/LdapFilterCache.java | 78 ------- .../java/com/epam/dlab/auth/core/LoginCache.java | 82 -------- .../com/epam/dlab/auth/core/LoginConveyor.java | 66 ------ .../java/com/epam/dlab/auth/core/LoginStep.java | 46 ----- .../com/epam/dlab/auth/core/UserInfoBuilder.java | 196 ------------------ .../java/com/epam/dlab/auth/dao/LdapUserDAO.java | 223 ++------------------- .../com/epam/dlab/auth/dao/LdapUserDAOImpl.java | 164 +++++++++++++++ .../main/java/com/epam/dlab/auth/dao/Request.java | 115 ++--------- .../epam/dlab/auth/dao/SearchRequestBuilder.java | 25 --- .../epam/dlab/auth/dao/UserInfoDAODumbImpl.java | 6 +- .../epam/dlab/auth/dao/UserInfoDAOMongoImpl.java | 82 ++++---- .../dlab/auth/dao/filter/SearchResultMapper.java | 30 --- .../auth/dao/filter/SearchResultProcessor.java | 67 ------- .../com/epam/dlab/auth/dao/script/DeepMap.java | 56 ------ .../epam/dlab/auth/dao/script/ScriptHolder.java | 58 ------ .../dao/script/SearchResultToDictionaryMapper.java | 97 --------- .../auth/modules/AwsSecurityServiceModule.java | 4 +- .../auth/modules/AzureSecurityServiceModule.java | 4 +- .../auth/modules/GcpSecurityServiceModule.java | 4 +- .../dlab/auth/modules/SecurityServiceModule.java | 13 +- .../SynchronousLdapAuthenticationResource.java | 77 +++++++ .../SynchronousLdapAuthenticationService.java | 183 ----------------- .../dlab/auth/service/AuthenticationService.java | 33 +++ .../service/impl/LdapAuthenticationService.java | 82 ++++++++ .../test/java/com/epam/dlab/auth/aws/AwsTest.java | 77 ------- .../com/epam/dlab/auth/core/LoginConveyorTest.java | 130 ------------ .../dlab/auth/dao/script/ScriptHolderTest.java | 64 ------ .../java/com/epam/dlab/auth/ldap/AuthTest.java | 28 --- .../java/com/epam/dlab/auth/ldap/BasicTest.java | 115 ----------- .../java/com/epam/dlab/auth/ldap/JsonTest.java | 83 -------- .../java/com/epam/dlab/auth/ldap/ScriptList.java | 75 ------- .../impl/LdapAuthenticationServiceTest.java | 138 +++++++++++++ 42 files changed, 717 insertions(+), 2081 deletions(-) diff --git a/services/dlab-auth-common/src/main/java/com/epam/dlab/auth/UserInfoDAO.java b/services/dlab-auth-common/src/main/java/com/epam/dlab/auth/UserInfoDAO.java index d7c0bbb..931a050 100644 --- a/services/dlab-auth-common/src/main/java/com/epam/dlab/auth/UserInfoDAO.java +++ b/services/dlab-auth-common/src/main/java/com/epam/dlab/auth/UserInfoDAO.java @@ -13,17 +13,18 @@ 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 com.epam.dlab.auth; +import java.util.Optional; + public interface UserInfoDAO { - UserInfo getUserInfoByAccessToken(String accessToken); + Optional<UserInfo> getUserInfoByAccessToken(String accessToken); - void updateUserInfoTTL(String accessToken, UserInfo ui); + void updateUserInfoTTL(String accessToken, UserInfo ui); - void deleteUserInfo(String accessToken); + void deleteUserInfo(String accessToken); - void saveUserInfo(UserInfo ui); + void saveUserInfo(UserInfo ui); } diff --git a/services/dlab-auth-common/src/main/java/com/epam/dlab/auth/UserVerificationService.java b/services/dlab-auth-common/src/main/java/com/epam/dlab/auth/UserVerificationService.java index 0c9daac..7ceccae 100644 --- a/services/dlab-auth-common/src/main/java/com/epam/dlab/auth/UserVerificationService.java +++ b/services/dlab-auth-common/src/main/java/com/epam/dlab/auth/UserVerificationService.java @@ -23,5 +23,5 @@ package com.epam.dlab.auth; @FunctionalInterface public interface UserVerificationService { - void verify(String username, UserInfo userInfo); + void verify(UserInfo userInfo); } diff --git a/services/security-aws/src/main/java/com/epam/dlab/auth/aws/service/AwsUserVerificationService.java b/services/security-aws/src/main/java/com/epam/dlab/auth/aws/service/AwsUserVerificationService.java index 9224755..c66f1f8 100644 --- a/services/security-aws/src/main/java/com/epam/dlab/auth/aws/service/AwsUserVerificationService.java +++ b/services/security-aws/src/main/java/com/epam/dlab/auth/aws/service/AwsUserVerificationService.java @@ -38,15 +38,15 @@ public class AwsUserVerificationService implements UserVerificationService { } @Override - public void verify(String username, UserInfo userInfo) { - verifyAwsUser(username, userInfo); - verifyAwsKeys(username, userInfo); + public void verify(UserInfo userInfo) { + verifyAwsUser(userInfo); + verifyAwsKeys(userInfo); } - private User verifyAwsUser(String username, UserInfo userInfo) { + private User verifyAwsUser(UserInfo userInfo) { try { - User awsUser = awsUserDAO.getAwsUser(username); + User awsUser = awsUserDAO.getAwsUser(userInfo.getName()); if (awsUser != null) { userInfo.setAwsUser(true); return awsUser; @@ -58,16 +58,16 @@ public class AwsUserVerificationService implements UserVerificationService { } } - private List<AccessKeyMetadata> verifyAwsKeys(String username, UserInfo userInfo) { + private List<AccessKeyMetadata> verifyAwsKeys(UserInfo userInfo) { userInfo.getKeys().clear(); try { - List<AccessKeyMetadata> keys = awsUserDAO.getAwsAccessKeys(username); + List<AccessKeyMetadata> keys = awsUserDAO.getAwsAccessKeys(userInfo.getName()); if (keys == null || keys.isEmpty() || keys.stream().noneMatch(k -> "Active".equalsIgnoreCase(k.getStatus()))) { - throw new DlabException("Cannot get aws access key for user " + username); + throw new DlabException("Cannot get aws access key for user " + userInfo.getName()); } keys.forEach(e -> userInfo.addKey(e.getAccessKeyId(), e.getStatus())); diff --git a/services/security-azure/src/main/java/com/epam/dlab/auth/azure/AzureAuthenticationResource.java b/services/security-azure/src/main/java/com/epam/dlab/auth/azure/AzureAuthenticationResource.java index db58e07..622d432 100644 --- a/services/security-azure/src/main/java/com/epam/dlab/auth/azure/AzureAuthenticationResource.java +++ b/services/security-azure/src/main/java/com/epam/dlab/auth/azure/AzureAuthenticationResource.java @@ -44,6 +44,7 @@ import java.io.IOException; import java.net.URI; import java.util.HashMap; import java.util.Map; +import java.util.Optional; /** * Used to authenticate users against Azure Active Directory @@ -105,16 +106,15 @@ public class AzureAuthenticationResource<C extends Configuration> extends Abstra public UserInfo getUserInfo(String accessToken, @Context HttpServletRequest request) { String remoteIp = request.getRemoteAddr(); - UserInfo ui = userInfoDao.getUserInfoByAccessToken(accessToken); + final Optional<UserInfo> ui = userInfoDao.getUserInfoByAccessToken(accessToken); - if (ui != null) { - ui = ui.withToken(accessToken); - userInfoDao.updateUserInfoTTL(accessToken, ui); + if (ui.isPresent()) { + userInfoDao.updateUserInfoTTL(accessToken, ui.get().withToken(accessToken)); log.debug("restored UserInfo from DB {}", ui); } log.debug("Authorized {} {} {}", accessToken, ui, remoteIp); - return ui; + return ui.get().withToken(accessToken); } /** diff --git a/services/security-service/pom.xml b/services/security-service/pom.xml index 468c324..e68370b 100644 --- a/services/security-service/pom.xml +++ b/services/security-service/pom.xml @@ -123,6 +123,12 @@ limitations under the License. <artifactId>dlab-utils</artifactId> <version>${project.parent.version}</version> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>${org.mockito.version}</version> + <scope>test</scope> + </dependency> </dependencies> <build> diff --git a/services/security-service/security.yml b/services/security-service/security.yml index aa294e3..c83ca77 100644 --- a/services/security-service/security.yml +++ b/services/security-service/security.yml @@ -30,95 +30,29 @@ useLdapBindTemplate: true ldapBindTemplate: uid=%s,LDAP_OU,LDAP_DN ldapBindAttribute: uid ldapSearchAttribute: uid -ldapSearch: - - name: userLookUp - cache: true - expirationTimeMsec: 600000 - scope: SUBTREE - attributes: - - cn - - mail - - uid - - gidNumber - timeLimit: 0 - base: LDAP_DN - filter: "(&(objectClass=inetOrgPerson)(uid=%uid%))" - - name: userInfo - cache: true - expirationTimeMsec: 600000 - scope: SUBTREE - attributes: - - cn - - gidNumber - timeLimit: 0 - base: LDAP_DN - filter: "(&(objectClass=inetOrgPerson)(uid=%uid%))" - searchResultProcessor: - language: python -# path: c:\tmp\enrich.py - code: | - def enrichUserInfo(ui,context): - name = ui.getName() - key = context['key'].lower() - userInfo=context['userInfo'] - if not key in userInfo: - raise Exception('Python LDAP UserInfo not found for '+key) - uid= userInfo[key] - cn = context['userInfo'][key]['cn'].split(' ') - ui.setFirstName(cn[0]) - ui.setLastName(cn[1]) - return ui - - name: groupInfo - cache: true - expirationTimeMsec: 600000 - scope: SUBTREE - attributes: - - cn - - mail - - gidNumber - - memberUid - timeLimit: 0 - base: LDAP_DN - filter: "(&(objectClass=posixGroup))" - searchResultProcessor: - language: javascript -# path: c:\tmp\enrich.js - code: | - var enrichUserInfo = function(ui,context) { - name = ui.getName(); - key = context['key'].toLowerCase(); - userInfo=context['userInfo']; - if( userInfo[key] == undefined ) { - throw 'JavaScript LDAP UserInfo not found for '+key; - } - uid= userInfo[key]; - userGid = uid['gidnumber']; - groupInfo=context['groupInfo']; - for( dn in groupInfo ) { - group = groupInfo[dn]; - if( userGid == group['gidnumber']) { - ui.addRole(group['cn']); - } else { - grMembers = group['memberuid']; - if (grMembers != undefined) { - index = grMembers.split(","); - members = new Array(); - for(i in index) { - members[i] = grMembers.split(",")[i]; - } - for (member in members) { - if (members[member] != undefined) { - if (members[member].toLowerCase() == name.toLowerCase()) { - ui.addRole(group['cn']); - } - } - } - } - } - } - return ui; - } - +ldapGroupAttribute: memberUid +ldapGroupNameAttribute: cn +ldapGroupUserAttribute: uid +ldapSearchRequest: + expirationTimeMsec: 600000 + scope: SUBTREE + attributes: + - cn + - mail + - uid + - gidNumber + timeLimit: 0 + base: LDAP_DN + filter: "(&(objectClass=inetOrgPerson)(uid=$LDAP_SEARCH_ATTRIBUTE))" +ldapGroupSearchRequest: + expirationTimeMsec: 600000 + scope: SUBTREE + attributes: + - cn + - memberUid + timeLimit: 0 + base: LDAP_DN + filter: "(&(objectClass=posixGroup))" server: requestLog: appenders: diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/SecurityServiceConfiguration.java b/services/security-service/src/main/java/com/epam/dlab/auth/SecurityServiceConfiguration.java index 5593978..3ec3f69 100644 --- a/services/security-service/src/main/java/com/epam/dlab/auth/SecurityServiceConfiguration.java +++ b/services/security-service/src/main/java/com/epam/dlab/auth/SecurityServiceConfiguration.java @@ -26,7 +26,6 @@ import org.apache.directory.ldap.client.api.LdapConnectionConfig; import javax.validation.constraints.Min; import java.util.HashMap; -import java.util.List; import java.util.Map; public class SecurityServiceConfiguration extends ServiceConfiguration { @@ -40,8 +39,6 @@ public class SecurityServiceConfiguration extends ServiceConfiguration { @Min(5) private int loginAuthenticationTimeout = 10; @JsonProperty - private List<Request> ldapSearch; - @JsonProperty private String ldapBindTemplate; @JsonProperty private String ldapBindAttribute; @@ -58,16 +55,38 @@ public class SecurityServiceConfiguration extends ServiceConfiguration { private LdapConnectionConfig ldapConfiguration; + private String ldapGroupAttribute; + private String ldapGroupNameAttribute; + private String ldapGroupUserAttribute; + + @JsonProperty + private Request ldapSearchRequest; + + @JsonProperty + private Request ldapGroupSearchRequest; + public SecurityServiceConfiguration() { super(); } - public boolean isUserInfoPersistenceEnabled() { - return userInfoPersistenceEnabled; + public String getLdapGroupUserAttribute() { + return ldapGroupUserAttribute; + } + + public String getLdapGroupAttribute() { + return ldapGroupAttribute; + } + + public String getLdapGroupNameAttribute() { + return ldapGroupNameAttribute; } - public List<Request> getLdapSearch() { - return ldapSearch; + public Request getLdapGroupSearchRequest() { + return ldapGroupSearchRequest; + } + + public boolean isUserInfoPersistenceEnabled() { + return userInfoPersistenceEnabled; } public LdapConnectionConfig getLdapConnectionConfig() { @@ -117,4 +136,8 @@ public class SecurityServiceConfiguration extends ServiceConfiguration { public GcpLoginConfiguration getGcpLoginConfiguration() { return gcpLoginConfiguration; } + + public Request getLdapSearchRequest() { + return ldapSearchRequest; + } } diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/core/CacheableReference.java b/services/security-service/src/main/java/com/epam/dlab/auth/core/CacheableReference.java deleted file mode 100644 index 60ff0d3..0000000 --- a/services/security-service/src/main/java/com/epam/dlab/auth/core/CacheableReference.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2016, EPAM SYSTEMS INC - * - * 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 com.epam.dlab.auth.core; - -import com.aegisql.conveyor.BuilderSupplier; - -import java.util.function.Supplier; - -public class CacheableReference<T> implements Supplier<T> { - - /** The reference. */ - private final T reference; - - /** - * Instantiates a new immutable reference. - * - * @param ref the ref - */ - private CacheableReference(T ref) { - this.reference = ref; - } - - /* (non-Javadoc) - * @see java.util.function.Supplier#get() - */ - @Override - public T get() { - return reference; - } - - public static <T> BuilderSupplier<T> newInstance(T ref) { - return () -> new CacheableReference<>(ref); - } - - @Override - public String toString() { - return ""+reference; - } -} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/core/DlabLdapConnection.java b/services/security-service/src/main/java/com/epam/dlab/auth/core/DlabLdapConnection.java index 51ee800..0ca2f97 100644 --- a/services/security-service/src/main/java/com/epam/dlab/auth/core/DlabLdapConnection.java +++ b/services/security-service/src/main/java/com/epam/dlab/auth/core/DlabLdapConnection.java @@ -27,7 +27,7 @@ public abstract class DlabLdapConnection implements Closeable { abstract LdapConnection getConnection() throws Exception; - public LdapConnection connect() throws Exception { + public LdapConnection getBoundConnection() throws Exception { final LdapConnection connection = getConnection(); if (!connection.connect()) { log.error("Cannot establish a connection to LDAP server"); diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/core/DlabLdapConnectionFactory.java b/services/security-service/src/main/java/com/epam/dlab/auth/core/DlabLdapConnectionFactory.java new file mode 100644 index 0000000..0a76b53 --- /dev/null +++ b/services/security-service/src/main/java/com/epam/dlab/auth/core/DlabLdapConnectionFactory.java @@ -0,0 +1,48 @@ +/* + * + * * Copyright (c) 2018, EPAM SYSTEMS INC + * * + * * 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 com.epam.dlab.auth.core; + +import com.epam.dlab.auth.SecurityServiceConfiguration; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import org.apache.directory.ldap.client.api.LdapConnectionConfig; +import org.apache.directory.ldap.client.api.LdapConnectionPool; +import org.apache.directory.ldap.client.api.LdapNetworkConnection; +import org.apache.directory.ldap.client.api.ValidatingPoolableLdapConnectionFactory; + +@Singleton +public class DlabLdapConnectionFactory { + + + private final LdapConnectionConfig connConfig; + private final LdapConnectionPool connectionPool; + private final boolean usePool; + + @Inject + public DlabLdapConnectionFactory(SecurityServiceConfiguration configuration) { + this.connConfig = configuration.getLdapConnectionConfig(); + this.connectionPool = new LdapConnectionPool(new ValidatingPoolableLdapConnectionFactory(connConfig)); + this.usePool = configuration.isLdapUseConnectionPool(); + } + + public DlabLdapConnection newConnection() { + return usePool ? new ReturnableConnection(connectionPool) : + new SimpleConnection(new LdapNetworkConnection(connConfig)); + } +} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/core/LdapFilterCache.java b/services/security-service/src/main/java/com/epam/dlab/auth/core/LdapFilterCache.java deleted file mode 100644 index 7a37dcc..0000000 --- a/services/security-service/src/main/java/com/epam/dlab/auth/core/LdapFilterCache.java +++ /dev/null @@ -1,78 +0,0 @@ -/*************************************************************************** - - Copyright (c) 2016, EPAM SYSTEMS INC - - 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 com.epam.dlab.auth.core; - -import com.aegisql.conveyor.cart.command.CancelCommand; -import com.aegisql.conveyor.utils.caching.CachingConveyor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; - -public class LdapFilterCache extends CachingConveyor<String,String,Map<String,Object>> { - - private final static Logger LOG = LoggerFactory.getLogger(LdapFilterCache.class); - - private final static LdapFilterCache INSTANCE = new LdapFilterCache(); - - public static LdapFilterCache getInstance() { - return INSTANCE; - } - - private LdapFilterCache() { - super(); - this.setName("LdapFilterCache"); - this.setIdleHeartBeat(1, TimeUnit.SECONDS); - this.setDefaultCartConsumer((b,l,s)-> LOG.debug("LdapFilterCache consume {} {}",l,s.get())); - this.setOnTimeoutAction((s)->{ - LOG.trace("LdapFilterCache Timeout {}",s.get()); - }); - this.setScrapConsumer(bin->{ - LOG.debug("LdapFilterCache {}: {}", bin.failureType, bin.scrap); - }); - } - - public void removeLdapFilterInfo(String token) { - this.addCommand(new CancelCommand<>(token)); - } - - public Map<String,Object> getLdapFilterInfo(String token) { - Supplier<? extends Map<String,Object>> s = this.getProductSupplier(token); - if( s == null ) { - return null; - } else { - return s.get(); - } - } - - public void save(String token, Map<String,Object> ldapInfo,long expTimeMsec) { - CompletableFuture<Boolean> cacheFuture = LdapFilterCache.getInstance().createBuild(token, CacheableReference.newInstance(ldapInfo),expTimeMsec,TimeUnit.MILLISECONDS); - try { - if(! cacheFuture.get() ) { - throw new Exception("Cache offer future returned 'false' for "+ldapInfo); - } - } catch (Exception e) { - throw new RuntimeException("Cache offer failed for "+ldapInfo,e); - } - } - -} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/core/LoginCache.java b/services/security-service/src/main/java/com/epam/dlab/auth/core/LoginCache.java deleted file mode 100644 index 4c48172..0000000 --- a/services/security-service/src/main/java/com/epam/dlab/auth/core/LoginCache.java +++ /dev/null @@ -1,82 +0,0 @@ -/*************************************************************************** - - Copyright (c) 2016, EPAM SYSTEMS INC - - 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 com.epam.dlab.auth.core; - -import com.aegisql.conveyor.cart.command.CancelCommand; -import com.aegisql.conveyor.utils.caching.CachingConveyor; -import com.epam.dlab.auth.UserInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; - -public class LoginCache extends CachingConveyor<String,String,UserInfo> { - - private final static Logger LOG = LoggerFactory.getLogger(LoginCache.class); - - private final static LoginCache INSTANCE = new LoginCache(); - - public static LoginCache getInstance() { - return INSTANCE; - } - - private LoginCache() { - super(); - this.setName("UserInfoCache"); - this.setIdleHeartBeat(1, TimeUnit.SECONDS); - this.setDefaultBuilderTimeout(60, TimeUnit.MINUTES); - this.enablePostponeExpirationOnTimeout(false); - this.enablePostponeExpiration(true); - this.setExpirationPostponeTime(60,TimeUnit.MINUTES); - this.setDefaultCartConsumer((b,l,s)-> LOG.debug("UserInfoCache consume {} {}",l,s.get())); - this.setOnTimeoutAction((s)->{ - LOG.trace("UserInfoCache Timeout {}",s.get()); - }); - this.setScrapConsumer(bin->{ - LOG.debug("UserInfoCache {}: {}", bin.failureType, bin.scrap); - }); - } - - public void removeUserInfo(String token) { - this.addCommand(new CancelCommand<>(token)); - } - - public UserInfo getUserInfo(String token) { - Supplier<? extends UserInfo> s = this.getProductSupplier(token); - if( s == null ) { - return null; - } else { - return s.get(); - } - } - - public void save(UserInfo userInfo) { - CompletableFuture<Boolean> cacheFuture = LoginCache.getInstance().createBuild(userInfo.getAccessToken(), CacheableReference.newInstance(userInfo)); - try { - if(! cacheFuture.get() ) { - throw new Exception("Offer future returned 'false' for "+userInfo); - } - } catch (Exception e) { - throw new RuntimeException("User Info cache offer failure for "+userInfo,e); - } - } - -} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/core/LoginConveyor.java b/services/security-service/src/main/java/com/epam/dlab/auth/core/LoginConveyor.java deleted file mode 100644 index 9c903ff..0000000 --- a/services/security-service/src/main/java/com/epam/dlab/auth/core/LoginConveyor.java +++ /dev/null @@ -1,66 +0,0 @@ -/*************************************************************************** - - Copyright (c) 2016, EPAM SYSTEMS INC - - 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 com.epam.dlab.auth.core; - -import com.aegisql.conveyor.utils.parallel.KBalancedParallelConveyor; -import com.epam.dlab.auth.UserInfo; -import com.epam.dlab.auth.UserInfoDAO; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; - -public class LoginConveyor extends KBalancedParallelConveyor<String,LoginStep,UserInfo>{ - - private final static Logger LOG = LoggerFactory.getLogger(LoginConveyor.class); - - private UserInfoDAO userInfoDao; - - public LoginConveyor(long builderTimeout) { - super(4); - this.setName("LoginConveyor"); - this.setIdleHeartBeat(1, TimeUnit.SECONDS); - this.setDefaultBuilderTimeout(builderTimeout, TimeUnit.SECONDS); - this.setResultConsumer(res->{ - LOG.debug("UserInfo Build Success: {}",res); - LoginCache.getInstance().save(res.product); - if(userInfoDao != null) { - userInfoDao.saveUserInfo(res.product); - } else { - LOG.warn("UserInfo Build not saved: {}",res); - } - }); - this.setScrapConsumer(bin-> LOG.error("UserInfo Build Failed: {}",bin)); - } - - public void setUserInfoDao(UserInfoDAO userInfoDao) { - this.userInfoDao = userInfoDao; - } - - public CompletableFuture<UserInfo> startUserInfoBuild(String token, String username) { - LOG.debug("startUserInfoBuild {} {} {}",token,username); - return this.createBuildFuture(token,UserInfoBuilder.supplier(token,username)); - } - - public void cancel(String token, LoginStep step, String errorMessage) { - LOG.debug("Canceling {}: {}",token,errorMessage); - this.add(token,new RuntimeException(errorMessage),step); - } -} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/core/LoginStep.java b/services/security-service/src/main/java/com/epam/dlab/auth/core/LoginStep.java deleted file mode 100644 index 2f6e4fe..0000000 --- a/services/security-service/src/main/java/com/epam/dlab/auth/core/LoginStep.java +++ /dev/null @@ -1,46 +0,0 @@ -/*************************************************************************** - - Copyright (c) 2016, EPAM SYSTEMS INC - - 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 com.epam.dlab.auth.core; - -import com.aegisql.conveyor.SmartLabel; - -import java.util.function.BiConsumer; - -public enum LoginStep implements SmartLabel<UserInfoBuilder> { - LDAP_LOGIN(UserInfoBuilder::ldapLoginPassed), - LDAP_USER_INFO(UserInfoBuilder::ldapUserInfo), - AWS_USER(UserInfoBuilder::awsUser), - AWS_KEYS(UserInfoBuilder::awsKeys), - AWS_KEYS_EMPTY(UserInfoBuilder::awsKeysEmpty), - REMOTE_IP(UserInfoBuilder::remoteIp), - LDAP_USER_INFO_ERROR(UserInfoBuilder::ldapUserInfoError), - LDAP_GROUP_INFO_ERROR(UserInfoBuilder::ldapGroupInfoError), - AWS_USER_ERROR(UserInfoBuilder::awsUserError), - AWS_KEYS_ERROR(UserInfoBuilder::awsKeysError), - ; - BiConsumer<UserInfoBuilder, Object> setter; - @SuppressWarnings("unchecked") - <T extends Object> LoginStep (BiConsumer<UserInfoBuilder,T> setter) { - this.setter = (BiConsumer<UserInfoBuilder, Object>) setter; - } - @Override - public BiConsumer<UserInfoBuilder, Object> get() { - return setter; - } -} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/core/UserInfoBuilder.java b/services/security-service/src/main/java/com/epam/dlab/auth/core/UserInfoBuilder.java deleted file mode 100644 index 8e7b75e..0000000 --- a/services/security-service/src/main/java/com/epam/dlab/auth/core/UserInfoBuilder.java +++ /dev/null @@ -1,196 +0,0 @@ -/*************************************************************************** - - Copyright (c) 2016, EPAM SYSTEMS INC - - 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 com.epam.dlab.auth.core; - -import com.aegisql.conveyor.BuilderSupplier; -import com.aegisql.conveyor.Testing; -import com.amazonaws.services.identitymanagement.model.AccessKeyMetadata; -import com.epam.dlab.auth.UserInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Collection; -import java.util.List; -import java.util.concurrent.atomic.LongAdder; -import java.util.function.Supplier; - -public class UserInfoBuilder implements Supplier<UserInfo>, Testing { - - private final static Logger LOG = LoggerFactory.getLogger(UserInfoBuilder.class); - - private UserInfo userInfo; - - private RuntimeException ldapError = null; - private RuntimeException ldapGroupError = null; - private RuntimeException awsUserError = null; - private RuntimeException awsKeyError = null; - - - private int readinessStatus = 0b00000000; - - public final static int FIRST_NAME = 0b0000001; - public final static int LAST_NAME = 0b0000010; - public final static int AWS_USER_SET = 0b0000100; - public final static int ROLE_SET = 0b0001000; - public final static int REMOTE_IP = 0b0010000; - public final static int AWS_KEYS = 0b0100000; - public final static int LOGIN = 0b1000000; - - public final static int READYNESS_MASK = 0b1111111; - - public static boolean testMask(Supplier<? extends UserInfo> supplier, int mask) { - UserInfoBuilder builder = (UserInfoBuilder) supplier; - LOG.debug("testing {} vs {} = {}",builder.readinessStatus,mask,(builder.readinessStatus & mask) == mask); - return (builder.readinessStatus & mask) == mask; - } - - public void setMask(int mask) { - this.readinessStatus |= mask; - } - - public static BuilderSupplier<UserInfo> supplier(final String token, final String username ) { - LOG.debug("supplier requested {} {}",token, username); - return () -> new UserInfoBuilder(token,username); - } - - public static void ldapLoginPassed(UserInfoBuilder b, Object t) { - b.setMask( LOGIN ); - } - - public static void firstName(UserInfoBuilder b, String firstName) { - LOG.debug("firstName {}",firstName); - - b.userInfo.setFirstName(firstName); - b.setMask( FIRST_NAME ); - } - - public static void lastName(UserInfoBuilder b, String lastName) { - LOG.debug("lastName {}",lastName); - - b.userInfo.setLastName(lastName); - b.setMask( LAST_NAME ); - } - - public static void remoteIp(UserInfoBuilder b, String remoteIp) { - LOG.debug("remoteIp {}",remoteIp); - - b.userInfo.setRemoteIp(remoteIp); - b.setMask( REMOTE_IP ); - } - - public static void awsUser(UserInfoBuilder b, Boolean awsUser) { - LOG.debug("awsUser {}",awsUser); - - b.userInfo.setAwsUser(awsUser); - b.setMask( AWS_USER_SET ); - } - - public static void roles(UserInfoBuilder b, Collection<String> roles) { - LOG.debug("roles {}",roles); - roles.forEach( role -> b.userInfo.addRole(role) ); - b.setMask( ROLE_SET ); - } - - public static void ldapUserInfo(UserInfoBuilder b, UserInfo ui) { - LOG.debug("merge user info{}",ui); - UserInfoBuilder.firstName(b,ui.getFirstName()); - UserInfoBuilder.lastName(b,ui.getLastName()); - UserInfoBuilder.roles(b,ui.getRoles()); - } - - public UserInfoBuilder(String token, String username) { - this.userInfo = new UserInfo(username, token); - } - - public UserInfoBuilder() { - - } - - @Override - public UserInfo get() { - if( ldapError != null ) throw ldapError; - if( ldapGroupError != null ) throw ldapGroupError; - if( awsUserError != null ) throw awsUserError; - if( awsKeyError != null ) throw awsKeyError; - return userInfo; - } - - @Override - public String toString() { - return "UserInfoBuilder{" + - "userInfo=" + userInfo + - ", readinessStatus=" + readinessStatus + - '}'; - } - - @Override - public boolean test() { - return UserInfoBuilder.testMask(this,UserInfoBuilder.READYNESS_MASK); - } - - public static void awsKeys(UserInfoBuilder b, List<AccessKeyMetadata> keyMetadata) { - LOG.debug("AWS Keys {}",keyMetadata); - LongAdder counter = new LongAdder(); - if(keyMetadata != null) { - keyMetadata.forEach(k -> { - String key = k.getAccessKeyId(); - String status = k.getStatus(); - if ("Active".equalsIgnoreCase(status)) { - counter.increment(); - } - b.userInfo.addKey(key, status); - }); - } - - if( counter.intValue() == 0 ) { - b.awsKeyError = new RuntimeException("Please contact AWS administrator to activate your Access Key"); - } - b.setMask( AWS_KEYS ); - } - - public static void awsKeysEmpty(UserInfoBuilder b, List<AccessKeyMetadata> keyMetadata) { - LOG.debug("AWS Keys {}",keyMetadata); - b.setMask( AWS_KEYS ); - } - - public static void ldapUserInfoError(UserInfoBuilder b, RuntimeException t) { - LOG.error("ldapUserInfoError {}", t.getMessage()); - b.ldapError = t; - b.setMask( LOGIN ); - } - - public static void ldapGroupInfoError(UserInfoBuilder b, RuntimeException t) { - LOG.error("ldapGroupInfoError {}", t.getMessage()); - b.ldapGroupError = t; - b.setMask( FIRST_NAME | LAST_NAME | ROLE_SET ); - } - - public static void awsUserError(UserInfoBuilder b, RuntimeException t) { - LOG.error("awsUserError {}", t.getMessage()); - b.awsUserError = t; - b.setMask( AWS_USER_SET ); - } - - public static void awsKeysError(UserInfoBuilder b, RuntimeException t) { - LOG.error("awsKeysError {}", t.getMessage()); - b.awsKeyError = t; - b.setMask( AWS_KEYS ); - } - -} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/dao/LdapUserDAO.java b/services/security-service/src/main/java/com/epam/dlab/auth/dao/LdapUserDAO.java index 0b9fa65..9bf1cdd 100644 --- a/services/security-service/src/main/java/com/epam/dlab/auth/dao/LdapUserDAO.java +++ b/services/security-service/src/main/java/com/epam/dlab/auth/dao/LdapUserDAO.java @@ -1,210 +1,29 @@ -/*************************************************************************** - - Copyright (c) 2016, EPAM SYSTEMS INC - - 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. - - ****************************************************************************/ +/* + * + * * Copyright (c) 2018, EPAM SYSTEMS INC + * * + * * 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 com.epam.dlab.auth.dao; -import com.epam.dlab.auth.SecurityServiceConfiguration; import com.epam.dlab.auth.UserInfo; -import com.epam.dlab.auth.core.DlabLdapConnection; -import com.epam.dlab.auth.core.LdapFilterCache; -import com.epam.dlab.auth.core.ReturnableConnection; -import com.epam.dlab.auth.core.SimpleConnection; -import com.epam.dlab.auth.dao.filter.SearchResultProcessor; -import com.epam.dlab.auth.dao.script.ScriptHolder; -import com.epam.dlab.auth.dao.script.SearchResultToDictionaryMapper; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.pool.PoolableObjectFactory; -import org.apache.directory.api.ldap.model.cursor.SearchCursor; -import org.apache.directory.api.ldap.model.exception.LdapException; -import org.apache.directory.api.ldap.model.message.SearchRequest; -import org.apache.directory.ldap.client.api.*; - -import java.io.IOException; -import java.util.*; -import java.util.regex.Pattern; - -@Slf4j -public class LdapUserDAO { - - // the request from security.yml for user look up by one of the parameters (mail or phone). - // configured in the same request configuration under "filter" key: "(&(objectClass=inetOrgPerson)(mail=%mail%))" - private static final String USER_LOOK_UP = "userLookUp"; - private static final String DISTINGUISH_NAME = "dn"; - private final LdapConnectionConfig connConfig; - private final List<Request> requests; - private final String bindTemplate; - private final String ldapBindAttribute; - private final String ldapSearchAttribute; - private final LdapConnectionPool usersPool; - private final LdapConnectionPool searchPool; - private final ScriptHolder script = new ScriptHolder(); - private final boolean useBindTemplate; - private boolean useCache; - private boolean ldapUseConnectionPool; - - public LdapUserDAO(SecurityServiceConfiguration config, boolean useCache) { - this.connConfig = config.getLdapConnectionConfig(); - this.requests = config.getLdapSearch(); - this.useBindTemplate = config.isUseLdapBindTemplate(); - this.bindTemplate = config.getLdapBindTemplate(); - this.ldapBindAttribute = config.getLdapBindAttribute(); - this.ldapSearchAttribute = "%" + config.getLdapSearchAttribute() + "%"; - PoolableObjectFactory<LdapConnection> userPoolFactory = new ValidatingPoolableLdapConnectionFactory - (connConfig); - this.usersPool = new LdapConnectionPool(userPoolFactory); - PoolableObjectFactory<LdapConnection> searchPoolFactory = new ValidatingPoolableLdapConnectionFactory - (connConfig); - this.searchPool = new LdapConnectionPool(searchPoolFactory); - this.useCache = useCache; - this.ldapUseConnectionPool = config.isLdapUseConnectionPool(); - } - - private DlabLdapConnection getConnection(LdapConnectionPool connectionPool) { - return ldapUseConnectionPool ? new ReturnableConnection(connectionPool) : - new SimpleConnection(new LdapNetworkConnection(connConfig)); - } - - public UserInfo getUserInfo(String username, String password) throws Exception { - Map<String, Object> userAttributes; - - try (DlabLdapConnection connection = getConnection(usersPool)) { - final LdapConnection ldapConnection = connection.connect(); - userAttributes = searchUsersAttributes(username, ldapConnection); - String bindAttribute = userAttributes.get(ldapBindAttribute).toString(); - bindUser(username, password, bindAttribute, ldapConnection, (String) userAttributes.get(DISTINGUISH_NAME)); - - UserInfo userInfo = new UserInfo(username, "******"); - userAttributes.entrySet().forEach(entry -> addAttribute(userInfo, entry)); - - return userInfo; - } catch (Exception e) { - log.error("LDAP getUserInfo authentication error for username '{}': {}", username, e.getMessage(), e); - throw e; - } - } - - private void bindUser(String username, String password, String cn, LdapConnection userCon, String dn) throws - LdapException { - userCon.bind(getBind(cn, dn), password); - userCon.unBind(); - log.debug("User '{}' identified.", username); - } - - private String getBind(String cn, String dn) { - String bind; - if (useBindTemplate) { - log.info("Biding with template : {} and username/cn: {}", bindTemplate, cn); - bind = String.format(bindTemplate, cn); - } else { - log.info("Biding using dn : {}", dn); - bind = dn; - } - return bind; - } - - private Map<String, Object> searchUsersAttributes(final String username, LdapConnection userCon) throws - IOException, LdapException { - Map<String, Object> contextMap; - Map<String, Object> userAttributes = new HashMap<>(); - for (Request request : requests) { - if (request.getName().equalsIgnoreCase(USER_LOOK_UP)) { - log.info("Request: {}", request.getName()); - log.info("Putting user param {} : {}", ldapSearchAttribute, username); - SearchRequest sr = request.buildSearchRequest(Collections. - singletonMap(Pattern.quote(ldapSearchAttribute), username)); - String filter = sr.getFilter().toString(); - contextMap = (useCache) ? LdapFilterCache.getInstance().getLdapFilterInfo(filter) : null; - SearchResultToDictionaryMapper mapper = new SearchResultToDictionaryMapper(request.getName(), - new HashMap<>()); - log.debug("Retrieving new branch {} for {}", request.getName(), filter); - try (SearchCursor cursor = userCon.search(sr)) { - contextMap = mapper.transformSearchResult(cursor); - Iterator<Object> iterator = contextMap.values().iterator(); - if (iterator.hasNext()) { - @SuppressWarnings("unchecked") - Map<String, Object> ua = (Map<String, Object>) iterator.next(); - log.info("User atttr {} ", ua); - userAttributes = ua; - } - } - } - } - log.info("User context is: {}", userAttributes); - return userAttributes; - } - - public UserInfo enrichUserInfo(final UserInfo userInfo) throws Exception { - log.debug("Enriching user info for user: {}", userInfo); - String username = userInfo.getName(); - UserInfo ui = userInfo.withToken("******"); - try (DlabLdapConnection connection = getConnection(searchPool)) { - final LdapConnection ldapConnection = connection.connect(); - Map<String, Object> conextTree = new HashMap<>(); - for (Request req : requests) { - if (req.getName().equalsIgnoreCase(USER_LOOK_UP)) { - addUserAttributes(username, ui, ldapConnection); - } - log.info("Request: {}", req.getName()); - SearchResultProcessor proc = req.getSearchResultProcessor(); - log.info("Putting user param {} : {} for user enriching", ldapSearchAttribute, username); - SearchRequest sr = req.buildSearchRequest(Collections - .singletonMap(Pattern.quote(ldapSearchAttribute), username)); - String filter = sr.getFilter().toString(); - Map<String, Object> contextMap = (useCache) ? LdapFilterCache.getInstance().getLdapFilterInfo(filter) - : null; - SearchResultToDictionaryMapper mapper = new SearchResultToDictionaryMapper(req.getName(), - conextTree); - if (contextMap == null) { - log.debug("Retrieving new branch {} for {}", req.getName(), filter); - try (SearchCursor cursor = ldapConnection.search(sr)) { - contextMap = mapper.transformSearchResult(cursor); - } - if (req.isCache() && useCache) { - LdapFilterCache.getInstance().save(filter, contextMap, req.getExpirationTimeMsec()); - } - } else { - log.debug("Restoring old branch {} for {}: {}", req.getName(), filter, contextMap); - mapper.getBranch().putAll(contextMap); - } - if (proc != null) { - log.debug("Executing: {}", proc.getLanguage()); - conextTree.put("key", ui.getKeys().get("dn")); - ui = script.evalOnce(req.getName(), proc.getLanguage(), proc.getCode()).apply(ui, conextTree); - } - } - } catch (Exception e) { - log.error("LDAP enrichUserInfo authentication error for username '{}': {}", username, e.getMessage(), e); - throw e; - } - return ui; - } +import java.util.Set; - private void addUserAttributes(String username, UserInfo ui, LdapConnection ldapConnection) throws IOException, - LdapException { - Map<String, Object> usersAttributes = searchUsersAttributes(username, ldapConnection); - usersAttributes.entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) - .forEach(attribute -> addAttribute(ui, attribute)); - } +public interface LdapUserDAO { + UserInfo getUserInfo(String username, String password); - private void addAttribute(UserInfo ui, Map.Entry<String, Object> attribute) { - ui.addKey(attribute.getKey().toLowerCase(), attribute.getValue().toString()); - log.debug("Adding attribute {} : {}", attribute.getKey().toLowerCase(), attribute.getValue - ().toString()); - } + Set<String> getUserGroups(UserInfo userInfo); } diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/dao/LdapUserDAOImpl.java b/services/security-service/src/main/java/com/epam/dlab/auth/dao/LdapUserDAOImpl.java new file mode 100644 index 0000000..7c62e64 --- /dev/null +++ b/services/security-service/src/main/java/com/epam/dlab/auth/dao/LdapUserDAOImpl.java @@ -0,0 +1,164 @@ +/* + * + * * Copyright (c) 2018, EPAM SYSTEMS INC + * * + * * 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 com.epam.dlab.auth.dao; + +import com.epam.dlab.auth.SecurityServiceConfiguration; +import com.epam.dlab.auth.UserInfo; +import com.epam.dlab.auth.core.DlabLdapConnection; +import com.epam.dlab.auth.core.DlabLdapConnectionFactory; +import com.epam.dlab.exceptions.DlabException; +import com.google.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import org.apache.directory.api.ldap.model.cursor.SearchCursor; +import org.apache.directory.api.ldap.model.entry.Attribute; +import org.apache.directory.api.ldap.model.entry.Entry; +import org.apache.directory.api.ldap.model.exception.LdapException; +import org.apache.directory.api.ldap.model.message.SearchRequestImpl; +import org.apache.directory.api.ldap.model.message.SearchResultEntry; +import org.apache.directory.api.ldap.model.message.SearchScope; +import org.apache.directory.api.ldap.model.name.Dn; +import org.apache.directory.ldap.client.api.LdapConnection; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +@Slf4j +public class LdapUserDAOImpl implements LdapUserDAO { + private static final String LDAP_SEARCH_ATTRIBUTE = "$LDAP_SEARCH_ATTRIBUTE"; + private static final String COMMON_NAME_ATTRIBUTE = "cn"; + private final DlabLdapConnectionFactory connectionFactory; + private final SecurityServiceConfiguration configuration; + + @Inject + public LdapUserDAOImpl(DlabLdapConnectionFactory connectionFactory, SecurityServiceConfiguration configuration) { + this.connectionFactory = connectionFactory; + this.configuration = configuration; + } + + @Override + public UserInfo getUserInfo(String username, String password) { + + try (DlabLdapConnection connection = connectionFactory.newConnection()) { + return getUserInfo(username, password, connection.getBoundConnection()); + } catch (Exception e) { + log.error("Can not get user info for user {} due to: {}", username, e.getMessage()); + throw new DlabException("Can not get user info due to: " + e.getMessage(), e); + } + } + + @Override + public Set<String> getUserGroups(UserInfo userInfo) { + final String groupUserAttribute = userInfo.getKeys().get(configuration.getLdapGroupUserAttribute()); + try (DlabLdapConnection connection = connectionFactory.newConnection()) { + final LdapConnection ldapConnection = connection.getBoundConnection(); + try (SearchCursor result = ldapConnection.search(getGroupSearchRequest())) { + return StreamSupport.stream(result.spliterator(), false) + .filter(r -> r instanceof SearchResultEntry) + .map(r -> ((SearchResultEntry) r).getEntry()) + .flatMap(e -> groupStream(groupUserAttribute, e)).collect(Collectors.toSet()); + } + } catch (Exception e) { + log.error("Can not get user groups for user {} due to: {}", userInfo.getName(), e.getMessage()); + throw new DlabException("Can not get user groups due to: " + e.getMessage()); + } + } + + private Stream<? extends String> groupStream(String groupUserAttribute, Entry e) { + final Attribute groupAttribute = e.get(configuration.getLdapGroupAttribute()); + return StreamSupport.stream(groupAttribute.spliterator(), false) + .anyMatch(v -> v.toString().equals(groupUserAttribute)) ? + Stream.of(e.get(configuration.getLdapGroupNameAttribute()).get().toString()) : + Stream.empty(); + } + + private UserInfo getUserInfo(String username, String password, LdapConnection ldapConnection) throws Exception { + try (SearchCursor result = ldapConnection.search(getUserSearchRequest(username))) { + return StreamSupport.stream(result.spliterator(), false) + .filter(r -> r instanceof SearchResultEntry) + .map(r -> ((SearchResultEntry) r).getEntry()) + .map(e -> toUserInfo(e, username)) + .peek(u -> bind(ldapConnection, u, password)) + .findAny() + .orElseThrow(() -> new DlabException("User " + username + " not found")); + } + } + + private void bind(LdapConnection ldapConnection, UserInfo u, String password) { + if (configuration.isUseLdapBindTemplate()) { + final String bindTemplate = configuration.getLdapBindTemplate(); + final String ldapBindAttrName = configuration.getLdapBindAttribute(); + final String bindAttrValue = Optional.ofNullable(u.getKeys().get(ldapBindAttrName)) + .orElseThrow(() -> new DlabException("Bind attribute " + ldapBindAttrName + " is not found")); + log.info("Biding with template: {} and attribute {} with value: {}", bindTemplate, ldapBindAttrName, + bindAttrValue); + try { + ldapConnection.bind(String.format(bindTemplate, bindAttrValue), password); + ldapConnection.unBind(); + } catch (LdapException e) { + log.error("Can not bind user due to: {}", e.getMessage()); + throw new DlabException("Can not bind user due to: " + e.getMessage(), e); + } + } + } + + private UserInfo toUserInfo(Entry e, String username) { + final Dn dn = e.getDn(); + log.debug("Entry dn: {}", dn); + final UserInfo userInfo = new UserInfo(username, null); + e.getAttributes() + .forEach(a -> userInfo.addKey(a.getId(), a.get().toString())); + final String cn = userInfo.getKeys().get(COMMON_NAME_ATTRIBUTE); + final String[] splittedCommonName = cn.split(" "); + if (splittedCommonName.length == 2) { + userInfo.setFirstName(splittedCommonName[0]); + userInfo.setLastName(splittedCommonName[1]); + } + + return userInfo; + } + + private SearchRequestImpl getUserSearchRequest(String username) throws LdapException { + final SearchRequestImpl searchRequest = new SearchRequestImpl(); + final Request searchRequestParams = configuration.getLdapSearchRequest(); + searchRequest.setBase(new Dn(searchRequestParams.getBase())); + searchRequest.setFilter(searchRequestParams.getFilter().replace(LDAP_SEARCH_ATTRIBUTE, username)); + searchRequest.setScope(SearchScope.valueOf(searchRequestParams.getScope())); + searchRequest.setTimeLimit(searchRequestParams.getTimeLimit()); + final List<String> attributes = searchRequestParams.getAttributes(); + searchRequest.addAttributes(attributes.toArray(new String[BigDecimal.ZERO.intValue()])); + return searchRequest; + } + + private SearchRequestImpl getGroupSearchRequest() throws LdapException { + final SearchRequestImpl searchRequest = new SearchRequestImpl(); + final Request searchRequestParams = configuration.getLdapGroupSearchRequest(); + searchRequest.setBase(new Dn(searchRequestParams.getBase())); + searchRequest.setFilter(searchRequestParams.getFilter()); + searchRequest.setScope(SearchScope.valueOf(searchRequestParams.getScope())); + searchRequest.setTimeLimit(searchRequestParams.getTimeLimit()); + final List<String> attributes = searchRequestParams.getAttributes(); + searchRequest.addAttributes(attributes.toArray(new String[BigDecimal.ZERO.intValue()])); + return searchRequest; + } +} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/dao/Request.java b/services/security-service/src/main/java/com/epam/dlab/auth/dao/Request.java index 64f0f39..e58a7a6 100644 --- a/services/security-service/src/main/java/com/epam/dlab/auth/dao/Request.java +++ b/services/security-service/src/main/java/com/epam/dlab/auth/dao/Request.java @@ -1,118 +1,33 @@ /*************************************************************************** -Copyright (c) 2016, EPAM SYSTEMS INC + Copyright (c) 2016, EPAM SYSTEMS INC -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 + 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 + 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. - -****************************************************************************/ + 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 com.epam.dlab.auth.dao; -import com.epam.dlab.auth.dao.filter.SearchResultProcessor; -import org.apache.directory.api.ldap.model.message.SearchRequest; -import org.apache.directory.api.ldap.model.message.SearchRequestImpl; -import org.apache.directory.api.ldap.model.message.SearchScope; -import org.apache.directory.api.ldap.model.name.Dn; +import lombok.Data; import java.util.List; -import java.util.Map; +@Data public class Request { -/* -- request: - scope: SUBTREE - attributes: - - "*" - timeLimit: 0 - base: dc=example,dc=com - filter: - * */ - private String name; private String scope; private List<String> attributes; - private int timeLimit = 0; + private int timeLimit; private String base; private String filter = ""; - private SearchResultProcessor searchResultProcessor; - private boolean cache = false; - private long expirationTimeMsec = 600000; //10 minutes - public String getScope() { - return scope; - } - public String[] getAttributes() { - return attributes.toArray(new String[]{}); - } - public int getTimeLimit() { - return timeLimit; - } - public String getBase() { - return base; - } - public String getFilter() { - return filter; - } - - public SearchResultProcessor getSearchResultProcessor() { - return searchResultProcessor; - } - public void setSearchResultProcessor(SearchResultProcessor searchResultProcessor) { - this.searchResultProcessor = searchResultProcessor; - } - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public boolean isCache() { - return cache; - } - public void setCache(boolean cache) { - this.cache = cache; - } - public long getExpirationTimeMsec() { - return expirationTimeMsec; - } - public void setExpirationTimeMsec(long expirationTimeMsec) { - this.expirationTimeMsec = expirationTimeMsec; - } - public SearchRequest buildSearchRequest(Map<String,Object> replace) { - SearchRequest sr = new SearchRequestImpl(); - try { - sr.setBase(new Dn(this.base)); - sr.addAttributes(this.getAttributes()); - if(this.filter != null && ! "".equals(this.filter ) ){ - String f = filter; - for(String key:replace.keySet()) { - f = f.replaceAll(key, replace.get(key).toString()); - } - sr.setFilter(f); - } - sr.setScope(SearchScope.valueOf(this.scope)); - sr.setTimeLimit(this.timeLimit); - } catch (Exception e) { - throw new RuntimeException(e); - } - return sr; - } - - @Override - public String toString() { - return "RequestConfig [scope=" + scope + ", attributes=" + attributes + ", timeLimit=" + timeLimit + ", base=" + base - + ", filter=" + filter + "]"; - } - - - + private long expirationTimeMsec = 600000; } diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/dao/SearchRequestBuilder.java b/services/security-service/src/main/java/com/epam/dlab/auth/dao/SearchRequestBuilder.java deleted file mode 100644 index 3d608ac..0000000 --- a/services/security-service/src/main/java/com/epam/dlab/auth/dao/SearchRequestBuilder.java +++ /dev/null @@ -1,25 +0,0 @@ -/*************************************************************************** - -Copyright (c) 2016, EPAM SYSTEMS INC - -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 com.epam.dlab.auth.dao; - -public class SearchRequestBuilder { - public SearchRequestBuilder() { - - } -} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/dao/UserInfoDAODumbImpl.java b/services/security-service/src/main/java/com/epam/dlab/auth/dao/UserInfoDAODumbImpl.java index 1ecf642..690d93c 100644 --- a/services/security-service/src/main/java/com/epam/dlab/auth/dao/UserInfoDAODumbImpl.java +++ b/services/security-service/src/main/java/com/epam/dlab/auth/dao/UserInfoDAODumbImpl.java @@ -23,14 +23,16 @@ import com.epam.dlab.auth.UserInfoDAO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Optional; + public class UserInfoDAODumbImpl implements UserInfoDAO { private static final Logger LOG = LoggerFactory.getLogger(UserInfoDAODumbImpl.class); @Override - public UserInfo getUserInfoByAccessToken(String accessToken) { + public Optional<UserInfo> getUserInfoByAccessToken(String accessToken) { LOG.debug("UserInfo persistence find unavailable: {}",accessToken); - return null; + return Optional.empty(); } @Override diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/dao/UserInfoDAOMongoImpl.java b/services/security-service/src/main/java/com/epam/dlab/auth/dao/UserInfoDAOMongoImpl.java index 67b28ea..b5c91be 100644 --- a/services/security-service/src/main/java/com/epam/dlab/auth/dao/UserInfoDAOMongoImpl.java +++ b/services/security-service/src/main/java/com/epam/dlab/auth/dao/UserInfoDAOMongoImpl.java @@ -13,7 +13,6 @@ 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 com.epam.dlab.auth.dao; @@ -31,10 +30,13 @@ import com.mongodb.client.MongoCollection; import lombok.extern.slf4j.Slf4j; import java.util.Date; +import java.util.Optional; @Singleton @Slf4j public class UserInfoDAOMongoImpl implements UserInfoDAO { + private static final String EXPIRE_AT_COLUMN = "expireAt"; + private static final String SECURITY_COLLECTION = "security"; private final MongoService ms; private final long inactiveUserTimeoutMsec; @@ -45,52 +47,24 @@ public class UserInfoDAOMongoImpl implements UserInfoDAO { } @Override - public UserInfo getUserInfoByAccessToken(String accessToken) { + public Optional<UserInfo> getUserInfoByAccessToken(String accessToken) { BasicDBObject uiSearchDoc = new BasicDBObject(); uiSearchDoc.put("_id", accessToken); - MongoCollection<BasicDBObject> mc = ms.getCollection("security", BasicDBObject.class); + MongoCollection<BasicDBObject> mc = ms.getCollection(SECURITY_COLLECTION, BasicDBObject.class); FindIterable<BasicDBObject> res = mc.find(uiSearchDoc); BasicDBObject uiDoc = res.first(); - if (uiDoc == null) { - log.warn("UI not found {}", accessToken); - return null; - } - Date lastAccess = uiDoc.getDate("expireAt"); - if (inactiveUserTimeoutMsec < Math.abs(new Date().getTime() - lastAccess.getTime())) { - log.warn("UI for {} expired but were not evicted from DB. Contact MongoDB admin to create expireable " + - "index" + - " on 'expireAt' key.", accessToken); - this.deleteUserInfo(accessToken); - return null; - } - String name = uiDoc.get("name").toString(); - String firstName = uiDoc.getString("firstName", ""); - String lastName = uiDoc.getString("lastName", ""); - String remoteIp = uiDoc.getString("remoteIp", ""); - BasicDBList roles = (BasicDBList) uiDoc.get("roles"); - Boolean awsUser = uiDoc.getBoolean("awsUser", false); - UserInfo ui = new UserInfo(name, accessToken); - ui.setFirstName(firstName); - ui.setLastName(lastName); - ui.setRemoteIp(remoteIp); - ui.setAwsUser(awsUser); - Object awsKeys = uiDoc.get("awsKeys"); - if (awsKeys != null) { - ((BasicDBObject) awsKeys).forEach((key, val) -> ui.addKey(key, val.toString())); - } - roles.forEach(o -> ui.addRole("" + o)); - log.debug("Found persistent {}", ui); - return ui; + return Optional.ofNullable(uiDoc) + .filter(doc -> !isExpired(accessToken, doc.getDate(EXPIRE_AT_COLUMN))) + .map(doc -> toUserInfo(accessToken, doc)); } @Override public void updateUserInfoTTL(String accessToken, UserInfo ui) { - //Update is caleed often, but does not need to be synchronized with the main thread BasicDBObject uiDoc = new BasicDBObject(); uiDoc.put("_id", accessToken); - uiDoc.put("expireAt", new Date(System.currentTimeMillis())); - MongoCollection<BasicDBObject> security = ms.getCollection("security", BasicDBObject.class); + uiDoc.put(EXPIRE_AT_COLUMN, new Date(System.currentTimeMillis())); + MongoCollection<BasicDBObject> security = ms.getCollection(SECURITY_COLLECTION, BasicDBObject.class); security.updateOne(new BasicDBObject("_id", accessToken), new BasicDBObject("$set", uiDoc)); log.debug("Updated persistent {}", accessToken); @@ -101,7 +75,7 @@ public class UserInfoDAOMongoImpl implements UserInfoDAO { //delete used in logout and has to be synchronized BasicDBObject uiDoc = new BasicDBObject(); uiDoc.put("_id", accessToken); - MongoCollection<BasicDBObject> security = ms.getCollection("security", BasicDBObject.class); + MongoCollection<BasicDBObject> security = ms.getCollection(SECURITY_COLLECTION, BasicDBObject.class); security.deleteOne(uiDoc); log.debug("Deleted persistent {}", accessToken); } @@ -119,12 +93,42 @@ public class UserInfoDAOMongoImpl implements UserInfoDAO { uiDoc.put("roles", ui.getRoles()); uiDoc.put("remoteIp", ui.getRemoteIp()); uiDoc.put("awsUser", ui.isAwsUser()); - uiDoc.put("expireAt", new Date(System.currentTimeMillis())); + uiDoc.put(EXPIRE_AT_COLUMN, new Date(System.currentTimeMillis())); uiDoc.put("awsKeys", ui.getKeys()); - MongoCollection<BasicDBObject> security = ms.getCollection("security", BasicDBObject.class); + MongoCollection<BasicDBObject> security = ms.getCollection(SECURITY_COLLECTION, BasicDBObject.class); security.insertOne(uiDoc); log.debug("Saved persistent {}", ui); } + private UserInfo toUserInfo(String accessToken, BasicDBObject uiDoc) { + String name = uiDoc.get("name").toString(); + String firstName = uiDoc.getString("firstName", ""); + String lastName = uiDoc.getString("lastName", ""); + String remoteIp = uiDoc.getString("remoteIp", ""); + BasicDBList roles = (BasicDBList) uiDoc.get("roles"); + boolean awsUser = uiDoc.getBoolean("awsUser", false); + UserInfo ui = new UserInfo(name, accessToken); + ui.setFirstName(firstName); + ui.setLastName(lastName); + ui.setRemoteIp(remoteIp); + ui.setAwsUser(awsUser); + Object awsKeys = uiDoc.get("awsKeys"); + if (awsKeys != null) { + ((BasicDBObject) awsKeys).forEach((key, val) -> ui.addKey(key, val.toString())); + } + roles.forEach(o -> ui.addRole("" + o)); + return ui; + } + + private boolean isExpired(String accessToken, Date lastAccess) { + if (inactiveUserTimeoutMsec < Math.abs(new Date().getTime() - lastAccess.getTime())) { + log.warn("UI for {} expired but were not evicted from DB. Contact MongoDB admin to create expireable " + + "index on 'expireAt' key.", accessToken); + this.deleteUserInfo(accessToken); + return true; + } + return false; + } + } diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/dao/filter/SearchResultMapper.java b/services/security-service/src/main/java/com/epam/dlab/auth/dao/filter/SearchResultMapper.java deleted file mode 100644 index 7b51a98..0000000 --- a/services/security-service/src/main/java/com/epam/dlab/auth/dao/filter/SearchResultMapper.java +++ /dev/null @@ -1,30 +0,0 @@ -/*************************************************************************** - - Copyright (c) 2016, EPAM SYSTEMS INC - - 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 com.epam.dlab.auth.dao.filter; - -import org.apache.directory.api.ldap.model.cursor.SearchCursor; - -import java.io.IOException; -import java.util.Map; - -public interface SearchResultMapper<M extends Map<String, Object>> { - M transformSearchResult(SearchCursor cursor) throws IOException; - - M getBranch(); -} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/dao/filter/SearchResultProcessor.java b/services/security-service/src/main/java/com/epam/dlab/auth/dao/filter/SearchResultProcessor.java deleted file mode 100644 index 936ac05..0000000 --- a/services/security-service/src/main/java/com/epam/dlab/auth/dao/filter/SearchResultProcessor.java +++ /dev/null @@ -1,67 +0,0 @@ -/*************************************************************************** - - Copyright (c) 2016, EPAM SYSTEMS INC - - 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 com.epam.dlab.auth.dao.filter; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; - -public class SearchResultProcessor { - private String language; - private String code; - private String path; - - public String getLanguage() { - return language; - } - - public void setLanguage(String language) { - this.language = language; - } - - public String getCode() { - if (code == null || "".equals(code)) { - try { - code = new String(Files.readAllBytes(Paths.get(path))); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } - - @Override - public String toString() { - return "SearchResultProcessor [language=" + language + ", path=" + path + ", code=" + code + "]"; - } - - -} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/dao/script/DeepMap.java b/services/security-service/src/main/java/com/epam/dlab/auth/dao/script/DeepMap.java deleted file mode 100644 index 9d54a8b..0000000 --- a/services/security-service/src/main/java/com/epam/dlab/auth/dao/script/DeepMap.java +++ /dev/null @@ -1,56 +0,0 @@ -/*************************************************************************** - -Copyright (c) 2016, EPAM SYSTEMS INC - -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 com.epam.dlab.auth.dao.script; - -import java.util.HashMap; -import java.util.Map; - -public class DeepMap { - - private final Map<String, Object> root; - - public DeepMap(Map<String, Object> parent) { - super(); - this.root = parent; - } - - public DeepMap() { - super(); - this.root = new HashMap<>(); - } - - public Map<String, Object> getRoot() { - return root; - } - - public DeepMap getBranch(String branchName) { - @SuppressWarnings("unchecked") - Map<String, Object> branch = (Map<String, Object>) root.get(branchName); - if( branch == null ) { - branch = new HashMap<>(); - root.put(branchName, branch); - } - return new DeepMap(branch); - } - - public void put(String key,Object val) { - root.put(key, val); - } - -} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/dao/script/ScriptHolder.java b/services/security-service/src/main/java/com/epam/dlab/auth/dao/script/ScriptHolder.java deleted file mode 100644 index 83cd4b5..0000000 --- a/services/security-service/src/main/java/com/epam/dlab/auth/dao/script/ScriptHolder.java +++ /dev/null @@ -1,58 +0,0 @@ -/*************************************************************************** - -Copyright (c) 2016, EPAM SYSTEMS INC - -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 com.epam.dlab.auth.dao.script; - -import com.epam.dlab.auth.UserInfo; - -import javax.script.Invocable; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; -import java.util.HashMap; -import java.util.Map; -import java.util.function.BiFunction; - -public class ScriptHolder { - - private final static String FUNCTION = "enrichUserInfo"; - - private final ScriptEngineManager mgr = new ScriptEngineManager(); - private final Map<String,Invocable> map = new HashMap<>(); - - public ScriptHolder() { - - } - - public BiFunction<UserInfo,Map<String,?>,UserInfo> evalOnce(String name, String language, String code) throws ScriptException { - if( ! map.containsKey(name)) { - ScriptEngine engine = mgr.getEngineByName( language ); - engine.eval(code); - map.put(name, (Invocable) engine); - } - return (ui,context)->{ - try { - return (UserInfo) map.get(name).invokeFunction(FUNCTION, ui,context); - } catch (Exception e) { - throw new RuntimeException(e); - } - }; - } - - -} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/dao/script/SearchResultToDictionaryMapper.java b/services/security-service/src/main/java/com/epam/dlab/auth/dao/script/SearchResultToDictionaryMapper.java deleted file mode 100644 index f9c7a47..0000000 --- a/services/security-service/src/main/java/com/epam/dlab/auth/dao/script/SearchResultToDictionaryMapper.java +++ /dev/null @@ -1,97 +0,0 @@ -/*************************************************************************** - - Copyright (c) 2016, EPAM SYSTEMS INC - - 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 com.epam.dlab.auth.dao.script; - -import com.epam.dlab.auth.dao.filter.SearchResultMapper; -import org.apache.commons.lang3.StringUtils; -import org.apache.directory.api.ldap.model.cursor.SearchCursor; -import org.apache.directory.api.ldap.model.entry.Entry; -import org.apache.directory.api.ldap.model.message.SearchResultEntry; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class SearchResultToDictionaryMapper implements SearchResultMapper<Map<String, Object>> { - - private static final Logger LOG = LoggerFactory.getLogger(SearchResultToDictionaryMapper.class); - private static final String DISTINGUISH_NAME = "dn"; - - private final DeepMap root; - private final DeepMap reqBranch; - private final String name; - - public SearchResultToDictionaryMapper(String name) { - this.name = name; - this.root = new DeepMap(); - reqBranch = root.getBranch(name); - } - - public SearchResultToDictionaryMapper(String name, Map<String, Object> context) { - this.name = name; - this.root = new DeepMap(context); - reqBranch = root.getBranch(name); - } - - @Override - public Map<String, Object> transformSearchResult(SearchCursor cursor) throws IOException { - LOG.debug(name); - cursor.forEach(response -> { - if (response instanceof SearchResultEntry) { - Entry resultEntry = ((SearchResultEntry) response).getEntry(); - String dn = resultEntry.getDn().toString(); - LOG.debug("\tEntryDN {}", dn); - DeepMap dnBranch = reqBranch.getBranch(dn.toLowerCase()); - dnBranch.put(DISTINGUISH_NAME, dn); - resultEntry.forEach(attr -> { - - // Since there might be multiple attributes with the same name, it is required to collect all their values (i.e. memberUid in group) - if (attr.size() > 1) { - - List<Object> list = new ArrayList<>(); - attr.iterator().forEachRemaining(list::add); - - String join = StringUtils.join(list, ","); - dnBranch.put(attr.getId() + "", join); - LOG.debug("\t\tAttr {} : {} ", attr.getId(), join); - } else { - dnBranch.put(attr.getId() + "", attr.get() + ""); - LOG.debug("\t\tAttr {}", attr); - } - }); - } - }); - return reqBranch.getRoot(); - } - - @Override - public Map<String, Object> getBranch() { - return reqBranch.getRoot(); - } - - @Override - public String toString() { - return "SearchResultToDictionaryMapper [name=" + name + ", parent=" + root + ", branch=" + reqBranch + "]"; - } - - -} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/modules/AwsSecurityServiceModule.java b/services/security-service/src/main/java/com/epam/dlab/auth/modules/AwsSecurityServiceModule.java index d07bb7a..43683fe 100644 --- a/services/security-service/src/main/java/com/epam/dlab/auth/modules/AwsSecurityServiceModule.java +++ b/services/security-service/src/main/java/com/epam/dlab/auth/modules/AwsSecurityServiceModule.java @@ -25,7 +25,7 @@ import com.epam.dlab.auth.aws.dao.AwsUserDAO; import com.epam.dlab.auth.aws.dao.AwsUserDAOImpl; import com.epam.dlab.auth.aws.service.AwsCredentialRefreshService; import com.epam.dlab.auth.aws.service.AwsUserVerificationService; -import com.epam.dlab.auth.resources.SynchronousLdapAuthenticationService; +import com.epam.dlab.auth.resources.SynchronousLdapAuthenticationResource; import com.epam.dlab.cloud.CloudModule; import com.google.inject.Injector; import com.google.inject.Provides; @@ -51,7 +51,7 @@ public class AwsSecurityServiceModule extends CloudModule { @Override public void init(Environment environment, Injector injector) { - environment.jersey().register(injector.getInstance(SynchronousLdapAuthenticationService.class)); + environment.jersey().register(injector.getInstance(SynchronousLdapAuthenticationResource.class)); if (conf.isAwsUserIdentificationEnabled()) { environment.lifecycle().manage(injector.getInstance(AwsCredentialRefreshService.class)); } diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/modules/AzureSecurityServiceModule.java b/services/security-service/src/main/java/com/epam/dlab/auth/modules/AzureSecurityServiceModule.java index e517093..9b02044 100644 --- a/services/security-service/src/main/java/com/epam/dlab/auth/modules/AzureSecurityServiceModule.java +++ b/services/security-service/src/main/java/com/epam/dlab/auth/modules/AzureSecurityServiceModule.java @@ -25,7 +25,7 @@ import com.epam.dlab.auth.azure.AzureSecurityResource; import com.epam.dlab.auth.azure.service.AzureAuthorizationCodeService; import com.epam.dlab.auth.azure.service.AzureAuthorizationCodeServiceImpl; import com.epam.dlab.auth.conf.AzureLoginConfiguration; -import com.epam.dlab.auth.resources.SynchronousLdapAuthenticationService; +import com.epam.dlab.auth.resources.SynchronousLdapAuthenticationResource; import com.epam.dlab.cloud.CloudModule; import com.google.inject.Injector; import io.dropwizard.setup.Environment; @@ -63,7 +63,7 @@ public class AzureSecurityServiceModule extends CloudModule { public void init(Environment environment, Injector injector) { if (conf.getAzureLoginConfiguration().isUseLdap()) { - environment.jersey().register(injector.getInstance(SynchronousLdapAuthenticationService.class)); + environment.jersey().register(injector.getInstance(SynchronousLdapAuthenticationResource.class)); } else { final AzureAuthenticationResource azureAuthenticationResource = new AzureAuthenticationResource(conf, injector.getInstance(UserInfoDAO.class), conf.getAzureLoginConfiguration(), diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/modules/GcpSecurityServiceModule.java b/services/security-service/src/main/java/com/epam/dlab/auth/modules/GcpSecurityServiceModule.java index ff59a84..c8de7dd 100644 --- a/services/security-service/src/main/java/com/epam/dlab/auth/modules/GcpSecurityServiceModule.java +++ b/services/security-service/src/main/java/com/epam/dlab/auth/modules/GcpSecurityServiceModule.java @@ -21,7 +21,7 @@ import com.epam.dlab.auth.UserVerificationService; import com.epam.dlab.auth.gcp.resources.GcpOauth2SecurityResource; import com.epam.dlab.auth.gcp.service.GcpAuthenticationService; import com.epam.dlab.auth.oauth2.Oauth2AuthenticationService; -import com.epam.dlab.auth.resources.SynchronousLdapAuthenticationService; +import com.epam.dlab.auth.resources.SynchronousLdapAuthenticationResource; import com.epam.dlab.cloud.CloudModule; import com.google.api.client.auth.oauth2.AuthorizationCodeFlow; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; @@ -60,7 +60,7 @@ public class GcpSecurityServiceModule extends CloudModule { @Override public void init(Environment environment, Injector injector) { - environment.jersey().register(injector.getInstance(SynchronousLdapAuthenticationService.class)); + environment.jersey().register(injector.getInstance(SynchronousLdapAuthenticationResource.class)); if (conf.isOauth2authenticationEnabled()) { environment.jersey().register(injector.getInstance(GcpOauth2SecurityResource.class)); } diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/modules/SecurityServiceModule.java b/services/security-service/src/main/java/com/epam/dlab/auth/modules/SecurityServiceModule.java index b2ba95d..7672faa 100644 --- a/services/security-service/src/main/java/com/epam/dlab/auth/modules/SecurityServiceModule.java +++ b/services/security-service/src/main/java/com/epam/dlab/auth/modules/SecurityServiceModule.java @@ -21,8 +21,11 @@ import com.epam.dlab.auth.SecurityServiceConfiguration; import com.epam.dlab.auth.UserInfoDAO; import com.epam.dlab.auth.UserVerificationService; import com.epam.dlab.auth.dao.LdapUserDAO; +import com.epam.dlab.auth.dao.LdapUserDAOImpl; import com.epam.dlab.auth.dao.UserInfoDAODumbImpl; import com.epam.dlab.auth.dao.UserInfoDAOMongoImpl; +import com.epam.dlab.auth.service.AuthenticationService; +import com.epam.dlab.auth.service.impl.LdapAuthenticationService; import com.epam.dlab.mongo.MongoService; import com.google.inject.Provides; import com.google.inject.Singleton; @@ -39,6 +42,8 @@ public class SecurityServiceModule extends ModuleBase<SecurityServiceConfigurati @Override protected void configure() { bind(SecurityServiceConfiguration.class).toInstance(configuration); + bind(LdapUserDAO.class).to(LdapUserDAOImpl.class); + bind(AuthenticationService.class).to(LdapAuthenticationService.class); if (configuration.isUserInfoPersistenceEnabled()) { bind(UserInfoDAO.class).to(UserInfoDAOMongoImpl.class); } else { @@ -52,13 +57,7 @@ public class SecurityServiceModule extends ModuleBase<SecurityServiceConfigurati return configuration.getMongoFactory().build(environment); } - @Provides - @Singleton - private LdapUserDAO ldapUserDAOWithoutCache() { - return new LdapUserDAO(configuration, false); - } - public static UserVerificationService defaultUserVerificationService() { - return (username, userInfo) -> log.debug("No additional user verification configured"); + return userInfo -> log.debug("No additional user verification configured"); } } diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/resources/SynchronousLdapAuthenticationResource.java b/services/security-service/src/main/java/com/epam/dlab/auth/resources/SynchronousLdapAuthenticationResource.java new file mode 100644 index 0000000..113208d --- /dev/null +++ b/services/security-service/src/main/java/com/epam/dlab/auth/resources/SynchronousLdapAuthenticationResource.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017, EPAM SYSTEMS INC + * + * 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 com.epam.dlab.auth.resources; + +import com.epam.dlab.auth.UserInfo; +import com.epam.dlab.auth.dto.UserCredentialDTO; +import com.epam.dlab.auth.service.AuthenticationService; +import com.epam.dlab.rest.dto.ErrorDTO; +import com.google.inject.Inject; +import lombok.extern.slf4j.Slf4j; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +/** + * Used for authentication against LDAP server + */ +@Path("/") +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Slf4j +public class SynchronousLdapAuthenticationResource { + private static final String INVALID_CREDENTIALS = "Username or password is invalid"; + private final AuthenticationService authenticationService; + + @Inject + public SynchronousLdapAuthenticationResource(AuthenticationService authenticationService) { + this.authenticationService = authenticationService; + } + + @POST + @Path("/login") + public Response login(UserCredentialDTO cred) { + log.debug("validating username:{} password:****** token:{}", cred.getUsername(), cred.getAccessToken()); + return authenticationService.login(cred) + .map(userInfo -> Response.ok(userInfo.getAccessToken()).build()) + .orElse(unauthorizedResponse()); + } + + @POST + @Path("/getuserinfo") + public UserInfo getUserInfo(String accessToken) { + return authenticationService.getUserInfo(accessToken).orElse(null); + } + + @POST + @Path("/logout") + public Response logout(String accessToken) { + authenticationService.logout(accessToken); + return Response.ok().build(); + } + + private Response unauthorizedResponse() { + return Response.status(Response.Status.UNAUTHORIZED) + .entity(new ErrorDTO(Response.Status.UNAUTHORIZED.getStatusCode(), INVALID_CREDENTIALS)) + .type(MediaType.APPLICATION_JSON) + .build(); + } +} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/resources/SynchronousLdapAuthenticationService.java b/services/security-service/src/main/java/com/epam/dlab/auth/resources/SynchronousLdapAuthenticationService.java deleted file mode 100644 index 2194215..0000000 --- a/services/security-service/src/main/java/com/epam/dlab/auth/resources/SynchronousLdapAuthenticationService.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) 2017, EPAM SYSTEMS INC - * - * 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 com.epam.dlab.auth.resources; - -import com.epam.dlab.auth.SecurityServiceConfiguration; -import com.epam.dlab.auth.UserInfo; -import com.epam.dlab.auth.UserInfoDAO; -import com.epam.dlab.auth.UserVerificationService; -import com.epam.dlab.auth.dao.LdapUserDAO; -import com.epam.dlab.auth.dto.UserCredentialDTO; -import com.epam.dlab.auth.rest.AbstractAuthenticationService; -import com.epam.dlab.constants.ServiceConsts; -import com.epam.dlab.exceptions.DlabException; -import com.epam.dlab.rest.dto.ErrorDTO; -import com.google.inject.Inject; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -/** - * Used for authentication against LDAP server - */ -@Path("/") -@Consumes(MediaType.APPLICATION_JSON) -@Produces(MediaType.APPLICATION_JSON) -public class SynchronousLdapAuthenticationService extends AbstractAuthenticationService<SecurityServiceConfiguration> { - private final LdapUserDAO ldapUserDAO; - private final UserInfoDAO userInfoDao; - private final UserVerificationService userVerificationService; - - @Inject - public SynchronousLdapAuthenticationService(SecurityServiceConfiguration config, UserInfoDAO userInfoDao, - LdapUserDAO ldapUserDAO, - UserVerificationService userVerificationService) { - super(config); - this.ldapUserDAO = ldapUserDAO; - this.userInfoDao = userInfoDao; - this.userVerificationService = userVerificationService; - } - - @Override - @POST - @Path("/login") - public Response login(UserCredentialDTO credential, @Context HttpServletRequest request) { - - String username = credential.getUsername(); - String password = credential.getPassword(); - String accessToken = credential.getAccessToken(); - String remoteIp = request.getRemoteAddr(); - String userAgent = request.getHeader(HttpHeaders.USER_AGENT); - - log.debug("validating username:{} password:****** token:{} ip:{}", username, accessToken, remoteIp); - - final Response.Status unauthorized = Response.Status.UNAUTHORIZED; - if (accessToken != null && !accessToken.isEmpty()) { - UserInfo ui = getUserInfo(accessToken, userAgent, remoteIp); - if (ui != null) { - return Response.ok(accessToken).build(); - } else { - log.debug("User info not found on login by access_token for user {}", username); - return Response.status(unauthorized).build(); - } - } - - try { - - login(username, password); - UserInfo enriched = enrichUser(username); - userVerificationService.verify(username, enriched); - - enriched.setRemoteIp(remoteIp); - log.info("User authenticated is {}", enriched); - String token = getRandomToken(); - - userInfoDao.saveUserInfo(enriched.withToken(token)); - return Response.ok(token).build(); - - } catch (Exception e) { - log.error("User {} is not authenticated", username, e); - return Response.status(unauthorized) - .entity(new ErrorDTO(unauthorized.getStatusCode(), e.getMessage())) - .type(MediaType.APPLICATION_JSON).build(); - } - } - - @Override - @POST - @Path("/getuserinfo") - public UserInfo getUserInfo(String accessToken, @Context HttpServletRequest request) { - String userAgent = request.getHeader(HttpHeaders.USER_AGENT); - String remoteIp = request.getRemoteAddr(); - - UserInfo ui = getUserInfo(accessToken, userAgent, remoteIp); - - if (ui != null) { - return ui; - } - - log.debug("Session {} is expired", accessToken); - - return null; - } - - private UserInfo getUserInfo(String accessToken, String userAgent, String remoteIp) { - - UserInfo ui = userInfoDao.getUserInfoByAccessToken(accessToken); - - if (ui != null) { - ui = ui.withToken(accessToken); - updateTTL(accessToken, ui, userAgent); - log.debug("restored UserInfo from DB {}", ui); - - log.debug("Authorized {} {} {}", accessToken, ui, remoteIp); - return ui; - } - - return null; - - } - - @Override - @POST - @Path("/logout") - public Response logout(String accessToken) { - userInfoDao.deleteUserInfo(accessToken); - log.info("Logged out user {}", accessToken); - return Response.ok().build(); - } - - private UserInfo login(String username, String password) { - try { - UserInfo userInfo = ldapUserDAO.getUserInfo(username, password); - log.debug("User Authenticated: {}", username); - return userInfo; - } catch (Exception e) { - log.error("Authentication error", e); - throw new DlabException("Username or password are not valid", e); - } - } - - private UserInfo enrichUser(String username) { - - try { - UserInfo userInfo = ldapUserDAO.enrichUserInfo(new UserInfo(username, null)); - log.debug("User Enriched: {}", username); - return userInfo; - } catch (Exception e) { - log.error("Authentication error", e); - throw new DlabException("User not authorized. Please contact DLAB administrator."); - } - } - - - private void updateTTL(String accessToken, UserInfo ui, String userAgent) { - log.debug("updating TTL agent {} {}", userAgent, ui); - if (ServiceConsts.PROVISIONING_USER_AGENT.equals(userAgent)) { - return; - } - - userInfoDao.updateUserInfoTTL(accessToken, ui); - } -} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/service/AuthenticationService.java b/services/security-service/src/main/java/com/epam/dlab/auth/service/AuthenticationService.java new file mode 100644 index 0000000..fde6a82 --- /dev/null +++ b/services/security-service/src/main/java/com/epam/dlab/auth/service/AuthenticationService.java @@ -0,0 +1,33 @@ +/* + * + * * Copyright (c) 2018, EPAM SYSTEMS INC + * * + * * 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 com.epam.dlab.auth.service; + +import com.epam.dlab.auth.UserInfo; +import com.epam.dlab.auth.dto.UserCredentialDTO; + +import java.util.Optional; + +public interface AuthenticationService { + + Optional<UserInfo> getUserInfo(String token); + + Optional<UserInfo> login(UserCredentialDTO credentialDTO); + + void logout(String token); +} diff --git a/services/security-service/src/main/java/com/epam/dlab/auth/service/impl/LdapAuthenticationService.java b/services/security-service/src/main/java/com/epam/dlab/auth/service/impl/LdapAuthenticationService.java new file mode 100644 index 0000000..d17cd85 --- /dev/null +++ b/services/security-service/src/main/java/com/epam/dlab/auth/service/impl/LdapAuthenticationService.java @@ -0,0 +1,82 @@ +/* + * + * * Copyright (c) 2018, EPAM SYSTEMS INC + * * + * * 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 com.epam.dlab.auth.service.impl; + +import com.epam.dlab.auth.UserInfo; +import com.epam.dlab.auth.UserInfoDAO; +import com.epam.dlab.auth.UserVerificationService; +import com.epam.dlab.auth.dao.LdapUserDAO; +import com.epam.dlab.auth.dto.UserCredentialDTO; +import com.epam.dlab.auth.service.AuthenticationService; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.util.Optional; + +import static com.epam.dlab.auth.rest.AbstractAuthenticationService.getRandomToken; + +@Singleton +@Slf4j +public class LdapAuthenticationService implements AuthenticationService { + private final UserInfoDAO userInfoDAO; + private final LdapUserDAO ldapUserDAO; + private final UserVerificationService verificationService; + + @Inject + public LdapAuthenticationService(UserInfoDAO userInfoDAO, LdapUserDAO ldapUserDAO, + UserVerificationService verificationService) { + this.userInfoDAO = userInfoDAO; + this.ldapUserDAO = ldapUserDAO; + this.verificationService = verificationService; + } + + @Override + public Optional<UserInfo> getUserInfo(String token) { + return userInfoDAO.getUserInfoByAccessToken(token) + .map(userInfo -> touchedUser(token, userInfo)); + } + + @Override + public Optional<UserInfo> login(UserCredentialDTO credentialDTO) { + final String token = credentialDTO.getAccessToken(); + return StringUtils.isNoneBlank(token) ? getUserInfo(token) : getLdapUserInfo(credentialDTO); + } + + @Override + public void logout(String token) { + userInfoDAO.deleteUserInfo(token); + } + + private Optional<UserInfo> getLdapUserInfo(UserCredentialDTO credentialDTO) { + final UserInfo user = ldapUserDAO.getUserInfo(credentialDTO.getUsername(), credentialDTO.getPassword()); + user.addRoles(ldapUserDAO.getUserGroups(user)); + verificationService.verify(user); + final String token = getRandomToken(); + final UserInfo userWithToken = user.withToken(token); + userInfoDAO.saveUserInfo(userWithToken); + return Optional.of(userWithToken); + } + + private UserInfo touchedUser(String token, UserInfo userInfo) { + userInfoDAO.updateUserInfoTTL(token, userInfo); + return userInfo.withToken(token); + } +} diff --git a/services/security-service/src/test/java/com/epam/dlab/auth/aws/AwsTest.java b/services/security-service/src/test/java/com/epam/dlab/auth/aws/AwsTest.java deleted file mode 100644 index 15c5b9d..0000000 --- a/services/security-service/src/test/java/com/epam/dlab/auth/aws/AwsTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/*************************************************************************** - -Copyright (c) 2016, EPAM SYSTEMS INC - -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 com.epam.dlab.auth.aws; - -import com.epam.dlab.auth.UserInfo; -import com.epam.dlab.auth.core.LdapFilterCache; -import com.epam.dlab.auth.core.LoginCache; -import org.junit.*; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -public class AwsTest { - @BeforeClass - public static void setUpBeforeClass() throws Exception { - } - - @AfterClass - public static void tearDownAfterClass() throws Exception { - } - - @Before - public void setUp() throws Exception { - } - - @After - public void tearDown() throws Exception { - } - - @Test - public void testLoginCache() throws InterruptedException { - LoginCache c = LoginCache.getInstance(); - c.setDefaultBuilderTimeout(1, TimeUnit.SECONDS); - c.setIdleHeartBeat(100,TimeUnit.MILLISECONDS); - c.save(new UserInfo("test","a")); - UserInfo u = c.getUserInfo("a"); - assertNotNull(u); - System.out.println(u); - } - - @Test - public void testLdapCache() throws InterruptedException { - LdapFilterCache c = LdapFilterCache.getInstance(); - c.setIdleHeartBeat(100,TimeUnit.MILLISECONDS); - Map<String,Object> m = new HashMap<>(); - m.put("name","a"); - c.save("a",m,100); - Map<String,Object> m2 = c.getLdapFilterInfo("a"); - assertNotNull(m2); - assertTrue(m==m2); - m2.put("test","me"); - System.out.println(m); - Thread.sleep(1000); - } - - -} diff --git a/services/security-service/src/test/java/com/epam/dlab/auth/core/LoginConveyorTest.java b/services/security-service/src/test/java/com/epam/dlab/auth/core/LoginConveyorTest.java deleted file mode 100644 index 4e7406b..0000000 --- a/services/security-service/src/test/java/com/epam/dlab/auth/core/LoginConveyorTest.java +++ /dev/null @@ -1,130 +0,0 @@ -/*************************************************************************** - - Copyright (c) 2016, EPAM SYSTEMS INC - - 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 com.epam.dlab.auth.core; - -import com.amazonaws.services.identitymanagement.model.AccessKeyMetadata; -import com.epam.dlab.auth.UserInfo; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.concurrent.*; - -public class LoginConveyorTest { - - LoginConveyor lc = new LoginConveyor(10); - - @Before - public void createCOnveyor(){ - } - - @After - public void stopConveyor() { - } - - @Test - public void setUserInfoDao() throws Exception { - - } - - @SuppressWarnings("serial") - @Test - public void startUserInfoBuild() throws Exception { - CompletableFuture<UserInfo> uf = lc.startUserInfoBuild("1","test"); - UserInfo uiSource = new UserInfo("a","b"); - uiSource.setFirstName("test"); - uiSource.setLastName("user"); - uiSource.addRole("admin"); - - lc.add("1","127.0.0.1",LoginStep.REMOTE_IP); - lc.add("1","OK",LoginStep.LDAP_LOGIN); - lc.add("1",uiSource, LoginStep.LDAP_USER_INFO); - lc.add("1",true, LoginStep.AWS_USER); - lc.add("1",new ArrayList<AccessKeyMetadata>() { - { add(new AccessKeyMetadata() - .withAccessKeyId("a") - .withStatus("Active")); - }} ,LoginStep.AWS_KEYS); - - - - UserInfo ui = uf.get(15, TimeUnit.SECONDS); - System.out.println("Future now: "+ui); - } - - @Test - public void startUserInfoBuildWithoutAws() throws Exception { - CompletableFuture<UserInfo> uf = lc.startUserInfoBuild("1","test"); - UserInfo uiSource = new UserInfo("a","b"); - uiSource.setFirstName("test"); - uiSource.setLastName("user"); - uiSource.addRole("admin"); - - lc.add("1","127.0.0.1",LoginStep.REMOTE_IP); - lc.add("1","OK",LoginStep.LDAP_LOGIN); - lc.add("1",uiSource, LoginStep.LDAP_USER_INFO); - lc.add("1",true, LoginStep.AWS_USER); - lc.add("1",new ArrayList<AccessKeyMetadata>() ,LoginStep.AWS_KEYS_EMPTY); - - - - UserInfo ui = uf.get(15, TimeUnit.SECONDS); - System.out.println("Future now: "+ui); - } - - @Test(expected = CancellationException.class) - public void cacheTest() throws ExecutionException, InterruptedException, TimeoutException { - LoginCache cache = LoginCache.getInstance(); - System.out.println("---cacheTest"); - //Just for this test - cache.setDefaultBuilderTimeout(1,TimeUnit.SECONDS); - cache.setExpirationPostponeTime(1,TimeUnit.SECONDS); - - UserInfo userInfo = new UserInfo("test","user"); - userInfo.setFirstName("Mike"); - userInfo.setLastName("T"); - userInfo.addRole("tr"); - userInfo.setAwsUser(true); - userInfo.addKey("a","Active"); - - CompletableFuture<Boolean> f = cache.createBuild("2", CacheableReference.newInstance(userInfo)); - CompletableFuture<UserInfo> uif = cache.getFuture("2"); - f.get(); - //this will take at least 2 seconds - for(int i = 0; i < 10; i++) { - UserInfo ui = cache.getUserInfo("2"); - System.out.println(i+": "+ui); - Thread.sleep(200); - } - //and finally will exit with timeout - uif.get(5,TimeUnit.SECONDS); - } - - @Test - public void add() throws Exception { - - } - - @Test - public void cancel() throws Exception { - - } - -} \ No newline at end of file diff --git a/services/security-service/src/test/java/com/epam/dlab/auth/dao/script/ScriptHolderTest.java b/services/security-service/src/test/java/com/epam/dlab/auth/dao/script/ScriptHolderTest.java deleted file mode 100644 index b449ae2..0000000 --- a/services/security-service/src/test/java/com/epam/dlab/auth/dao/script/ScriptHolderTest.java +++ /dev/null @@ -1,64 +0,0 @@ -/*************************************************************************** - -Copyright (c) 2016, EPAM SYSTEMS INC - -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 com.epam.dlab.auth.dao.script; - -import com.epam.dlab.auth.UserInfo; -import org.junit.*; - -import javax.script.ScriptException; -import java.util.HashMap; -import java.util.Map; - -import static org.junit.Assert.assertNotNull; - -public class ScriptHolderTest { - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - } - - @AfterClass - public static void tearDownAfterClass() throws Exception { - } - - @Before - public void setUp() throws Exception { - } - - @After - public void tearDown() throws Exception { - } - - @Test - public void test() throws ScriptException { - ScriptHolder sh = new ScriptHolder(); - Map<String,Object> map = new HashMap<>(); - map.put("key1", "val1"); - map.put("key2", "val2"); - UserInfo ui1 = sh.evalOnce("first", "javascript", "var enrichUserInfo=function(ui,context){ui.addRole(context['key1']);ui.setFirstName(\"Mike\");return ui;}").apply(new UserInfo("",""), map); - System.out.println(ui1); - assertNotNull(ui1); - - UserInfo ui2 = sh.evalOnce("second", "python", "def enrichUserInfo(ui,context):\n ui.addRole(context['key2'])\n ui.setLastName(\"Teplitskiy\")\n return ui\n").apply(ui1, map); - System.out.println(ui2); - assertNotNull(ui2); - - } - -} diff --git a/services/security-service/src/test/java/com/epam/dlab/auth/ldap/AuthTest.java b/services/security-service/src/test/java/com/epam/dlab/auth/ldap/AuthTest.java deleted file mode 100644 index c0a1110..0000000 --- a/services/security-service/src/test/java/com/epam/dlab/auth/ldap/AuthTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/*************************************************************************** - -Copyright (c) 2016, EPAM SYSTEMS INC - -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 com.epam.dlab.auth.ldap; - -public class AuthTest { - - public static void main(String[] args) { - System.out.println("auth test"); - - } - -} diff --git a/services/security-service/src/test/java/com/epam/dlab/auth/ldap/BasicTest.java b/services/security-service/src/test/java/com/epam/dlab/auth/ldap/BasicTest.java deleted file mode 100644 index c976925..0000000 --- a/services/security-service/src/test/java/com/epam/dlab/auth/ldap/BasicTest.java +++ /dev/null @@ -1,115 +0,0 @@ -/*************************************************************************** - -Copyright (c) 2016, EPAM SYSTEMS INC - -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 com.epam.dlab.auth.ldap; - -import org.apache.commons.pool.PoolableObjectFactory; -import org.apache.directory.api.ldap.model.cursor.SearchCursor; -import org.apache.directory.api.ldap.model.entry.Entry; -import org.apache.directory.api.ldap.model.message.SearchRequest; -import org.apache.directory.api.ldap.model.message.SearchRequestImpl; -import org.apache.directory.api.ldap.model.message.SearchResultEntry; -import org.apache.directory.api.ldap.model.message.SearchScope; -import org.apache.directory.api.ldap.model.name.Dn; -import org.apache.directory.ldap.client.api.LdapConnection; -import org.apache.directory.ldap.client.api.LdapConnectionConfig; -import org.apache.directory.ldap.client.api.LdapConnectionPool; -import org.apache.directory.ldap.client.api.ValidatingPoolableLdapConnectionFactory; -//import org.apache.directory.ldap.client.api.PoolableLdapConnectionFactory; - -public class BasicTest { - - public static void main(String[] args) throws Exception { - System.out.println("Basic test"); - LdapConnectionConfig config = new LdapConnectionConfig(); - config.setLdapHost( "localhost" ); - config.setLdapPort( 3890 ); - config.setName( "cn=admin,dc=example,dc=com" ); - config.setCredentials( "ldap" ); - PoolableObjectFactory<LdapConnection> poolFactory = new ValidatingPoolableLdapConnectionFactory( config ); - LdapConnectionPool pool = new LdapConnectionPool( poolFactory ); - pool.setTestOnBorrow( true ); - LdapConnection con = pool.borrowObject(); - - SearchRequest sr = new SearchRequestImpl(); - sr.setScope(SearchScope.SUBTREE); - sr.addAttributes("*"); - sr.setTimeLimit(0); - sr.setBase(new Dn("dc=example,dc=com")); - sr.setFilter("(cn=Mike Teplitskiy)"); - sr.setMessageId(1); - -// EntryCursor cursor = con.search( "dc=example,dc=com", "(objectclass=*)", SearchScope.SUBTREE ); - SearchCursor cursor = con.search( sr ); -// -// cursor.forEach(entry->{ -// System.out.println( "---- DN "+entry.getDn() ); -// entry.forEach(attr->{ -// System.out.println( "---- ATTR "+attr ); -// }); -// -// }); - - cursor.forEach(response->{ - if ( response instanceof SearchResultEntry ) - { - Entry resultEntry = ( ( SearchResultEntry ) response ).getEntry(); - System.out.println( "---- DN "+resultEntry.getDn() ); - resultEntry.forEach(attr-> System.out.println( "---- ATTR "+attr )); - } - }); - cursor.close(); - - sr.setFilter("(cn=John Doe)"); - sr.setMessageId(1); - cursor = con.search( sr ); - cursor.forEach(response->{ - if ( response instanceof SearchResultEntry ) - { - Entry resultEntry = ( ( SearchResultEntry ) response ).getEntry(); - System.out.println( "---- DN "+resultEntry.getDn() ); - resultEntry.forEach(attr-> System.out.println( "---- ATTR "+attr )); - } - }); - cursor.close(); - - - - con.unBind(); - - con.bind("uid=mike,ou=People,dc=example,dc=com","test"); - sr.setFilter("(uid=mike)"); - sr.setMessageId(2); - cursor = con.search( sr ); - cursor.forEach(response->{ - if ( response instanceof SearchResultEntry ) - { - Entry resultEntry = ( ( SearchResultEntry ) response ).getEntry(); - System.out.println( "---- DN "+resultEntry.getDn() ); - resultEntry.forEach(attr-> System.out.println( "---- ATTR "+attr )); - } - }); - cursor.close(); - - System.out.println("Press ENTER"); - pool.releaseConnection(con); - pool.close(); - - } - -} diff --git a/services/security-service/src/test/java/com/epam/dlab/auth/ldap/JsonTest.java b/services/security-service/src/test/java/com/epam/dlab/auth/ldap/JsonTest.java deleted file mode 100644 index 932f60e..0000000 --- a/services/security-service/src/test/java/com/epam/dlab/auth/ldap/JsonTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/*************************************************************************** - -Copyright (c) 2016, EPAM SYSTEMS INC - -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 com.epam.dlab.auth.ldap; - -import com.epam.dlab.auth.UserInfo; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.*; - -import java.io.IOException; - -import static org.junit.Assert.assertNotNull; - -public class JsonTest { - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - } - - @AfterClass - public static void tearDownAfterClass() throws Exception { - } - - @Before - public void setUp() throws Exception { - } - - @After - public void tearDown() throws Exception { - } - - @Test - public void test1() throws IOException { - ObjectMapper om = new ObjectMapper(); - String jv = om.writeValueAsString(new UserInfo("user","info")); - assertNotNull(jv); - System.out.println(jv); - UserInfo ui = om.readerFor(UserInfo.class).readValue(jv); - assertNotNull(ui); - System.out.println(ui); - //String js = "{\\\"username\\\":\"user\",\\\"access_token\\\":\"info\",\\\"firstName\\\":null,\\\"lastName\\\":null,\\\"roles\\\":[]}"; - //System.out.println(js); - //UserInfo ui2 = om.readerFor(UserInfo.class).readValue(js); - - - } - @Test - public void test2() throws IOException { - ObjectMapper om = new ObjectMapper(); - UserInfo ui = new UserInfo("user","info"); - ui.setFirstName("first"); - ui.setLastName("last"); - ui.addRole("r1"); - ui.addRole("r2"); - String jv = om.writeValueAsString(ui); - assertNotNull(jv); - System.out.println(jv); - UserInfo ui2 = om.readerFor(UserInfo.class).readValue(jv); - assertNotNull(ui2); - System.out.println(ui2); - //String js = "{\\\"username\\\":\"user\",\\\"access_token\\\":\"info\",\\\"firstName\\\":null,\\\"lastName\\\":null,\\\"roles\\\":[]}"; - //System.out.println(js); - //UserInfo ui2 = om.readerFor(UserInfo.class).readValue(js); - - - } - -} diff --git a/services/security-service/src/test/java/com/epam/dlab/auth/ldap/ScriptList.java b/services/security-service/src/test/java/com/epam/dlab/auth/ldap/ScriptList.java deleted file mode 100644 index 75763a1..0000000 --- a/services/security-service/src/test/java/com/epam/dlab/auth/ldap/ScriptList.java +++ /dev/null @@ -1,75 +0,0 @@ -/*************************************************************************** - -Copyright (c) 2016, EPAM SYSTEMS INC - -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 com.epam.dlab.auth.ldap; - -import com.epam.dlab.auth.UserInfo; - -import javax.script.*; -import java.util.List; - -public class ScriptList { - - public static void main( String[] args ) throws ScriptException, NoSuchMethodException { - - ScriptEngineManager mgr = new ScriptEngineManager(); - List<ScriptEngineFactory> factories = mgr.getEngineFactories(); - - for (ScriptEngineFactory factory : factories) { - - System.out.println("ScriptEngineFactory Info"); - - String engName = factory.getEngineName(); - String engVersion = factory.getEngineVersion(); - String langName = factory.getLanguageName(); - String langVersion = factory.getLanguageVersion(); - - System.out.printf("\tScript Engine: %s (%s)%n", engName, engVersion); - - List<String> engNames = factory.getNames(); - for(String name : engNames) { - System.out.printf("\tEngine Alias: %s%n", name); - } - - System.out.printf("\tLanguage: %s (%s)%n", langName, langVersion); - - } - - ScriptEngine python = mgr.getEngineByName("python"); - ScriptEngine js = mgr.getEngineByName("javascript"); - python.eval("print \"Hello Python!\""); - - js.eval("print('Hello JavaScript!');"); - - Invocable ijs = (Invocable) js; - Invocable ipy = (Invocable) python; - - js.eval("var f=function(ui){print(ui);ui.setFirstName(\"Mike\");return ui;};"); - - Object res = ijs.invokeFunction("f", new UserInfo("test", "pass")); - System.out.println(res); - - python.eval("def f(ui):\n print ui\n ui.setLastName(\"Teplitskiy\")\n return ui\n"); - Object res2 = ipy.invokeFunction("f", new UserInfo("test", "pass")); - System.out.println(res2); - - - - } - -} \ No newline at end of file diff --git a/services/security-service/src/test/java/com/epam/dlab/auth/service/impl/LdapAuthenticationServiceTest.java b/services/security-service/src/test/java/com/epam/dlab/auth/service/impl/LdapAuthenticationServiceTest.java new file mode 100644 index 0000000..ac4a258 --- /dev/null +++ b/services/security-service/src/test/java/com/epam/dlab/auth/service/impl/LdapAuthenticationServiceTest.java @@ -0,0 +1,138 @@ +/* + * + * * Copyright (c) 2018, EPAM SYSTEMS INC + * * + * * 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 com.epam.dlab.auth.service.impl; + +import com.epam.dlab.auth.UserInfo; +import com.epam.dlab.auth.UserInfoDAO; +import com.epam.dlab.auth.UserVerificationService; +import com.epam.dlab.auth.dao.LdapUserDAO; +import com.epam.dlab.auth.dto.UserCredentialDTO; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.Optional; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.refEq; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class LdapAuthenticationServiceTest { + + private static final String TOKEN = "token123"; + private static final String USER = "user"; + private static final String PASSWORD = "password"; + @Mock + private LdapUserDAO ldapUserDAO; + @Mock + private UserInfoDAO userInfoDAO; + @Mock + private UserVerificationService verificationService; + @InjectMocks + private LdapAuthenticationService ldapAuthenticationService; + + @Test + public void getUserInfo() { + + when(userInfoDAO.getUserInfoByAccessToken(anyString())).thenReturn(Optional.of(userInfo())); + final Optional<UserInfo> userInfo = ldapAuthenticationService.getUserInfo(TOKEN); + + assertTrue(userInfo.isPresent()); + assertEquals(USER.toLowerCase(), userInfo.get().getName()); + assertEquals(TOKEN, userInfo.get().getAccessToken()); + + verify(userInfoDAO).getUserInfoByAccessToken(TOKEN); + verify(userInfoDAO).updateUserInfoTTL(eq(TOKEN), refEq(userInfo())); + verifyNoMoreInteractions(userInfoDAO); + } + + @Test + public void getUserInfoWhenUserNotFound() { + + when(userInfoDAO.getUserInfoByAccessToken(anyString())).thenReturn(Optional.empty()); + final Optional<UserInfo> userInfo = ldapAuthenticationService.getUserInfo(TOKEN); + + assertFalse(userInfo.isPresent()); + + verify(userInfoDAO).getUserInfoByAccessToken(TOKEN); + verifyNoMoreInteractions(userInfoDAO); + } + + @Test + public void loginWithoutAccessToken() { + + when(ldapUserDAO.getUserInfo(anyString(), anyString())).thenReturn(userInfo()); + final Optional<UserInfo> userInfo = ldapAuthenticationService.login(getCredentialDTO()); + + assertTrue(userInfo.isPresent()); + assertEquals(USER, userInfo.get().getName()); + assertNotNull(userInfo.get().getAccessToken()); + + verify(verificationService).verify(refEq(userInfo())); + verify(ldapUserDAO).getUserInfo(USER, PASSWORD); + verify(ldapUserDAO).getUserGroups(refEq(userInfo())); + verify(userInfoDAO).saveUserInfo(refEq(userInfo().withToken(TOKEN), "accessToken")); + verifyNoMoreInteractions(ldapUserDAO, userInfoDAO); + } + + @Test + public void loginWithAccessToken() { + + when(userInfoDAO.getUserInfoByAccessToken(anyString())).thenReturn(Optional.of(userInfo())); + final UserCredentialDTO credentialDTO = getCredentialDTO(); + credentialDTO.setAccessToken(TOKEN); + final Optional<UserInfo> userInfo = ldapAuthenticationService.login(credentialDTO); + + assertTrue(userInfo.isPresent()); + assertEquals(USER, userInfo.get().getName()); + assertNotNull(userInfo.get().getAccessToken()); + + verify(userInfoDAO).getUserInfoByAccessToken(TOKEN); + verify(userInfoDAO).updateUserInfoTTL(eq(TOKEN), refEq(userInfo())); + verifyNoMoreInteractions(userInfoDAO); + verifyZeroInteractions(ldapUserDAO, verificationService); + } + + @Test + public void logout() { + + ldapAuthenticationService.logout(TOKEN); + + verify(userInfoDAO).deleteUserInfo(TOKEN); + verifyNoMoreInteractions(userInfoDAO); + verifyZeroInteractions(ldapUserDAO); + } + + private UserInfo userInfo() { + return new UserInfo(USER, null); + } + + private UserCredentialDTO getCredentialDTO() { + final UserCredentialDTO dto = new UserCredentialDTO(); + dto.setUsername(USER); + dto.setPassword(PASSWORD); + return dto; + } + +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
