/* Copyright 2012 Cloudseal Ltd
*
* 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.cloudseal.client.spring;
import com.cloudseal.client.saml2.CloudsealPrincipal;
import com.cloudseal.client.saml2.SamlValidator;
import com.cloudseal.client.saml2.SamlValidatorImpl;
import com.cloudseal.client.saml2.VerificationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
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 org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.util.Assert;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class CloudsealAuthenticationProvider implements AuthenticationProvider, InitializingBean {
private CloudsealManager cloudsealManager;
private SamlValidator responseValidator;
private UserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
CloudsealAssertionAuthenticationToken token = (CloudsealAssertionAuthenticationToken) authentication;
CloudsealAssertionAuthenticationToken.TokenDetails tokenDetails = (CloudsealAssertionAuthenticationToken.TokenDetails) token.getDetails();
try {
CloudsealPrincipal cloudsealPrincipal = responseValidator.validateAuthResponse(cloudsealManager.getPublicKey(),
tokenDetails.getSamlResponse(), tokenDetails.getRequestId(), tokenDetails.getAudience());
Collection<? extends GrantedAuthority> authorities = mapAuthorities(cloudsealPrincipal);
CloudsealUserDetails userDetails = new CloudsealUserDetails(cloudsealPrincipal, authorities);
CloudsealAuthenticationToken authenticationToken = new CloudsealAuthenticationToken(userDetails);
authenticationToken.setAuthenticated(true);
return decorateUserDetails(authenticationToken);
} catch (VerificationException ex) {
throw new BadCredentialsException("Unable to verify response from Cloudseal IDP");
}
}
/**
* <p>By default the authentication provider will create a CloudsealUserDetails object and set this on the AuthenticationToken.
* However we also allow developers to provide their own UserDetailsService. If a developer has configured a UserDetailsService for this
* provider we will invoke the loadUserByUsername() method and allow the configured service to do it's work.</p>
*
* <p>Developers may also configure an instance of CloudsealUserDetailsService which extends UserDetailsService and adds an additional method:
* UserDetails loadUser(CloudsealUserDetails). This allows developers to define their own UserDetailsService whilst still providing access to
* all the user attributes that are returned from the IDP.</p>
* @param token
* @return
*/
private CloudsealAuthenticationToken decorateUserDetails(CloudsealAuthenticationToken token) {
if (userDetailsService == null) {
return token;
}
CloudsealUserDetails userDetails = (CloudsealUserDetails) token.getUserDetails();
if (CloudsealUserDetailsService.class.isAssignableFrom(userDetailsService.getClass())) {
UserDetails developersUserDetails = ((CloudsealUserDetailsService) userDetailsService).loadUser(userDetails);
token.setDetails(developersUserDetails);
} else {
UserDetails developerUserDetails = userDetailsService.loadUserByUsername(token.getName());
token.setPrincipal(developerUserDetails);
}
return token;
}
private Collection<? extends GrantedAuthority> mapAuthorities(CloudsealPrincipal cloudsealPrincipal) {
Set<SimpleGrantedAuthority> authorities = new HashSet<SimpleGrantedAuthority>();
if (cloudsealPrincipal.getRoles() != null) {
for (String role : cloudsealPrincipal.getRoles()) {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role);
authorities.add(authority);
}
}
return authorities;
}
@Override
public boolean supports(Class<?> authentication) {
return CloudsealAssertionAuthenticationToken.class.isAssignableFrom(authentication);
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(cloudsealManager, "cloudsealManager must be set");
if (responseValidator == null) {
responseValidator = new SamlValidatorImpl();
}
}
public void setCloudsealManager(CloudsealManager cloudsealManager) {
this.cloudsealManager = cloudsealManager;
}
public void setResponseValidator(SamlValidatorImpl responseValidator) {
this.responseValidator = responseValidator;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
}