Java tutorial
/* * Artifactory is a binaries repository manager. * Copyright (C) 2012 JFrog Ltd. * * Artifactory is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Artifactory is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Artifactory. If not, see <http://www.gnu.org/licenses/>. */ package org.artifactory.security.ldap; import org.apache.commons.lang.StringUtils; import org.artifactory.addon.AddonsManager; import org.artifactory.addon.LdapGroupAddon; import org.artifactory.api.config.CentralConfigService; import org.artifactory.api.security.UserGroupService; import org.artifactory.api.security.ldap.LdapService; import org.artifactory.api.security.ldap.LdapUser; import org.artifactory.descriptor.security.ldap.LdapSetting; import org.artifactory.exception.InvalidNameException; import org.artifactory.factory.InfoFactoryHolder; import org.artifactory.security.*; import org.artifactory.spring.InternalContextHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.context.MessageSourceAware; import org.springframework.ldap.AuthenticationException; import org.springframework.ldap.CommunicationException; import org.springframework.ldap.NamingException; import org.springframework.ldap.core.DirContextOperations; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.core.Authentication; import org.springframework.security.ldap.LdapUtils; import org.springframework.security.ldap.authentication.BindAuthenticator; import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; import java.util.*; /** * Custom LDAP authentication provider just for creating local users for newly ldap authenticated users. * * @author Yossi Shaul */ public class ArtifactoryLdapAuthenticationProvider implements RealmAwareAuthenticationProvider, MessageSourceAware { private static final Logger log = LoggerFactory.getLogger(ArtifactoryLdapAuthenticationProvider.class); @Autowired private UserGroupService userGroupService; @Autowired private CentralConfigService centralConfig; /** * Keep the message source to in initialize LdapAuthenticationProvider when created */ private MessageSource messageSource; private Map<String, LdapAuthenticationProvider> ldapAuthenticationProviders = null; @Autowired private InternalLdapAuthenticator authenticator; @Autowired private LdapService ldapService; @Autowired private AddonsManager addonsManager; /** * Get the LDAP authentication providers, by iterating over all the bind authenticators and putting them in a map of * the settings key. * * @return The LDAP authentication provers */ public Map<String, LdapAuthenticationProvider> getLdapAuthenticationProviders() { if (ldapAuthenticationProviders == null) { ldapAuthenticationProviders = new HashMap<>(); Map<String, BindAuthenticator> authMap = authenticator.getAuthenticators(); for (Map.Entry<String, BindAuthenticator> entry : authMap.entrySet()) { LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider( entry.getValue()); if (messageSource != null) { ldapAuthenticationProvider.setMessageSource(messageSource); } ldapAuthenticationProviders.put(entry.getKey(), ldapAuthenticationProvider); } } return ldapAuthenticationProviders; } @Override public void setMessageSource(MessageSource messageSource) { this.messageSource = messageSource; if (ldapAuthenticationProviders != null) { for (LdapAuthenticationProvider ldapAuthenticationProvider : ldapAuthenticationProviders.values()) { ldapAuthenticationProvider.setMessageSource(messageSource); } } } @Override public boolean supports(Class<?> authentication) { if (centralConfig.getDescriptor().getSecurity().isLdapEnabled()) { for (LdapAuthenticationProvider ldapAuthenticationProvider : getLdapAuthenticationProviders() .values()) { if (ldapAuthenticationProvider.supports(authentication)) { return true; } } } return false; } @Override public Authentication authenticate(Authentication authentication) { String userName = authentication.getName(); // If it's an anonymous user, don't bother searching for the user. if (UserInfo.ANONYMOUS.equals(userName)) { return null; } log.debug("Trying to authenticate user '{}' via ldap.", userName); LdapSetting usedLdapSetting = null; DirContextOperations user = null; AddonsManager addonsManager = InternalContextHelper.get().beanForType(AddonsManager.class); LdapGroupAddon ldapGroupAddon = addonsManager.addonByType(LdapGroupAddon.class); try { RuntimeException authenticationException = null; for (Map.Entry<String, BindAuthenticator> entry : authenticator.getAuthenticators().entrySet()) { LdapSetting currentLdapSetting = centralConfig.getDescriptor().getSecurity() .getLdapSettings(entry.getKey()); BindAuthenticator bindAuthenticator = entry.getValue(); try { user = bindAuthenticator.authenticate(authentication); if (user != null) { usedLdapSetting = currentLdapSetting; break; } } catch (AuthenticationException e) { authenticationException = e; checkIfBindAndSearchActive(currentLdapSetting, userName); } catch (org.springframework.security.core.AuthenticationException e) { authenticationException = e; checkIfBindAndSearchActive(currentLdapSetting, userName); } catch (RuntimeException e) { authenticationException = e; } } if (user == null) { if (authenticationException != null) { UserInfo userInfo = userGroupService.findUser(userName); if (userInfo != null) { log.debug("user {} failed to perform ldap authentication (not bad credential)", userInfo.getUsername()); removeUserLdapRelatedGroups(userInfo); } throw authenticationException; } throw new AuthenticationServiceException(ArtifactoryLdapAuthenticator.LDAP_SERVICE_MISCONFIGURED); } // user authenticated via ldap log.debug("'{}' authenticated successfully by ldap server.", userName); //Collect internal groups, and if using external groups add them to the user info MutableUserInfo userInfo = InfoFactoryHolder.get().copyUser( userGroupService.findOrCreateExternalAuthUser(userName, !usedLdapSetting.isAutoCreateUser())); userInfo.setRealm(LdapService.REALM); String emailAttribute = usedLdapSetting.getEmailAttribute(); if (StringUtils.isNotBlank(emailAttribute)) { String email = user.getStringAttribute(emailAttribute); if (StringUtils.isNotBlank(email)) { log.debug("User '{}' has email address '{}'", userName, email); userInfo.setEmail(email); } } log.debug("Loading LDAP groups"); ldapGroupAddon.populateGroups(user, userInfo); log.debug("Finished Loading LDAP groups"); SimpleUser simpleUser = new SimpleUser(userInfo); // update user with latest attribute userGroupService.updateUser(userInfo, false); // create new authentication response containing the user and it's authorities return new LdapRealmAwareAuthentication(simpleUser, authentication.getCredentials(), simpleUser.getAuthorities()); } catch (AuthenticationException e) { String message = String.format("Failed to authenticate user '%s' via LDAP: %s", userName, e.getMessage()); log.debug(message); throw new AuthenticationServiceException(message, e); } catch (CommunicationException ce) { String message = String.format("Failed to authenticate user '%s' via LDAP: communication error", userName); log.warn(message); log.debug(message, ce); throw new AuthenticationServiceException(message, ce); } catch (org.springframework.security.core.AuthenticationException e) { String message = String.format("Failed to authenticate user '%s' via LDAP: %s", userName, e.getMessage()); log.debug(message); throw e; } catch (NamingException e) { String message = String.format("Failed to locate directory entry for authenticated user: %s", e.getMostSpecificCause().getMessage()); log.debug(message); throw new AuthenticationServiceException(message, e); } catch (InvalidNameException e) { String message = String.format("Failed to persist user '%s': %s", userName, e.getMessage()); log.warn(message); log.debug("Cause: {}", e); throw new InternalAuthenticationServiceException(message, e); } catch (Exception e) { String message = "Unexpected exception in LDAP authentication:"; log.error(message, e); throw new AuthenticationServiceException(message, e); } finally { LdapUtils.closeContext(user); } } /** * remove user ldap related group as user no longer exist in ldap * * @param userInfo Artifactory User Data */ private void removeUserLdapRelatedGroups(UserInfo userInfo) { MutableUserInfo mutableUserInfo = InfoFactoryHolder.get().copyUser(userInfo); Set<UserGroupInfo> updateUserGroup = new HashSet<>(); Set<UserGroupInfo> userGroupInfos = new HashSet<>(mutableUserInfo.getGroups()); for (Iterator<UserGroupInfo> userGroupInfoIterator = userGroupInfos.iterator(); userGroupInfoIterator .hasNext();) { UserGroupInfo userGroupInfo = userGroupInfoIterator.next(); if (!LdapService.REALM.equals(userGroupInfo.getRealm())) { updateUserGroup.add(userGroupInfo); } } mutableUserInfo.setGroups(updateUserGroup); if (!userInfo.isTransientUser()) { log.debug( "updating user {} after ldap login authentication failure (not bad credential) ,user groups for update are {}", mutableUserInfo.getUsername(), mutableUserInfo.getGroups()); userGroupService.updateUser(mutableUserInfo, false); } } private void checkIfBindAndSearchActive(LdapSetting ldapSetting, String userName) { if (StringUtils.isNotBlank(ldapSetting.getUserDnPattern()) && ldapSetting.getSearch() != null) { log.warn("LDAP authentication failed for '{}'. Note: you have configured direct user binding and " + "manager-based search, which are usually mutually exclusive. For AD leave the User DN Pattern " + "field empty.", userName); } } @Override public String getRealm() { return LdapService.REALM; } @Override public void addExternalGroups(String username, Set<UserGroupInfo> groups) { addonsManager.addonByType(LdapGroupAddon.class).addExternalGroups(username, groups); } @Override public boolean userExists(String username) { List<LdapSetting> settings = centralConfig.getMutableDescriptor().getSecurity().getLdapSettings(); if (settings == null || settings.isEmpty()) { log.debug("No LDAP settings defined"); return false; } for (LdapSetting setting : settings) { if (setting.isEnabled()) { log.debug("Trying to find user '{}' with LDAP settings '{}'", username, setting); LdapUser ldapUser = ldapService.getDnFromUserName(setting, username); if (ldapUser != null) { log.debug("Found user '{}' with LDAP settings '{}'", username, setting); return true; } } } return false; } }