package org.apereo.cas.web.ldap; import org.apereo.cas.configuration.model.core.web.security.AdminPagesSecurityProperties; import org.apereo.cas.configuration.support.Beans; import org.apereo.cas.web.support.WebUtils; import org.ldaptive.LdapEntry; import org.ldaptive.ReturnAttributes; import org.ldaptive.auth.AuthenticationRequest; import org.ldaptive.auth.AuthenticationResponse; import org.ldaptive.auth.Authenticator; import org.pac4j.core.authorization.authorizer.RequireAnyRoleAuthorizer; import org.pac4j.core.authorization.generator.AuthorizationGenerator; import org.pac4j.core.context.J2EContext; import org.pac4j.core.profile.CommonProfile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.stream.Collectors; /** * This is {@link LdapAuthenticationProvider}. * * @author Misagh Moayyed * @since 5.1.0 */ public class LdapAuthenticationProvider implements AuthenticationProvider { private static final Logger LOGGER = LoggerFactory.getLogger(LdapAuthenticationProvider.class); private final AuthorizationGenerator<CommonProfile> authorizationGenerator; private final AdminPagesSecurityProperties adminPagesSecurityProperties; public LdapAuthenticationProvider(final AuthorizationGenerator<CommonProfile> authorizationGenerator, final AdminPagesSecurityProperties adminPagesSecurityProperties) { this.authorizationGenerator = authorizationGenerator; this.adminPagesSecurityProperties = adminPagesSecurityProperties; } @Override public Authentication authenticate(final Authentication authentication) throws AuthenticationException { try { final String username = authentication.getPrincipal().toString(); final Object credentials = authentication.getCredentials(); final String password = credentials == null ? null : credentials.toString(); LOGGER.debug("Preparing LDAP authentication request for user [{}]", username); final AuthenticationRequest request = new AuthenticationRequest(username, new org.ldaptive.Credential(password), ReturnAttributes.ALL.value()); final Authenticator authenticator = Beans.newLdaptiveAuthenticator(adminPagesSecurityProperties.getLdap()); LOGGER.debug("Executing LDAP authentication request for user [{}]", username); final AuthenticationResponse response = authenticator.authenticate(request); LOGGER.debug("LDAP response: [{}]", response); if (response.getResult()) { final LdapEntry entry = response.getLdapEntry(); final CommonProfile profile = new CommonProfile(); profile.setId(username); entry.getAttributes().forEach(a -> profile.addAttribute(a.getName(), a.getStringValues())); LOGGER.debug("Collected user profile [{}]", profile); this.authorizationGenerator.generate(WebUtils.getPac4jJ2EContext(), profile); LOGGER.debug("Assembled user profile with roles after generating authorization claims [{}]", profile); final Collection<GrantedAuthority> authorities = new ArrayList<>(); authorities.addAll(profile.getRoles().stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList())); LOGGER.debug("List of authorities remapped from profile roles are [{}]", authorities); final RequireAnyRoleAuthorizer authorizer = new RequireAnyRoleAuthorizer(adminPagesSecurityProperties.getAdminRoles()); LOGGER.debug("Executing authorization for expected admin roles [{}]", authorizer.getElements()); final J2EContext context = WebUtils.getPac4jJ2EContext(); if (authorizer.isAllAuthorized(context, Arrays.asList(profile))) { return new UsernamePasswordAuthenticationToken(username, password, authorities); } LOGGER.warn("User [{}] is not authorized to access the requested resource allowed to roles [{}]", username, authorizer.getElements()); } else { LOGGER.warn("LDAP authentication response produced no results for [{}]", username); } } catch (final Exception e) { LOGGER.error(e.getMessage(), e); throw new InsufficientAuthenticationException("Unexpected LDAP error", e); } throw new BadCredentialsException("Could not authenticate provided credentials"); } @Override public boolean supports(final Class<?> aClass) { return UsernamePasswordAuthenticationToken.class.isAssignableFrom(aClass); } }