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);
}
}