org.openl.security.saml.OpenLResponseAuthenticationConverter Maven / Gradle / Ivy
package org.openl.security.saml;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.schema.XSAny;
import org.opensaml.core.xml.schema.XSString;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.Attribute;
import org.opensaml.saml.saml2.core.AttributeStatement;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.env.PropertyResolver;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
import org.w3c.dom.Node;
import org.openl.rules.security.Privilege;
import org.openl.rules.security.SimplePrivilege;
import org.openl.rules.security.SimpleUser;
import org.openl.rules.security.User;
import org.openl.util.CollectionUtils;
import org.openl.util.StringUtils;
/**
* Creates Saml2Authentication and SimpleUser based on the ResponseToken from the IDP.
*
* @author Eugene Biruk
*/
public class OpenLResponseAuthenticationConverter implements Converter {
private final BiFunction, Collection> privilegeMapper;
private final Consumer syncUserData;
private final PropertyResolver propertyResolver;
public OpenLResponseAuthenticationConverter(PropertyResolver propertyResolver,
Consumer syncUserData,
BiFunction, Collection> privilegeMapper) {
this.propertyResolver = propertyResolver;
this.syncUserData = syncUserData;
this.privilegeMapper = privilegeMapper;
}
/**
* Creates Saml2Authentication and SimpleUser based on the ResponseToken from the IDP.
*
* @param responseToken ResponseToken from the IDP
* @return Saml2Authentication
*/
@Override
public Saml2Authentication convert(OpenSaml4AuthenticationProvider.ResponseToken responseToken) {
Assertion assertion = responseToken.getResponse().getAssertions().iterator().next();
SimpleUserSamlBuilder simpleUserBuilder = new SimpleUserSamlBuilder(propertyResolver);
simpleUserBuilder.setAssertionAttributes(assertion);
simpleUserBuilder.setNameID(assertion.getSubject().getNameID().getValue());
SimpleUser simpleUser = simpleUserBuilder.build();
syncUserData.accept(simpleUser);
Collection privileges = privilegeMapper.apply(simpleUser.getUsername(), simpleUser.getAuthorities());
DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(simpleUser.getUsername(), Collections.emptyMap());
principal.setRelyingPartyRegistrationId(responseToken.getToken().getRelyingPartyRegistration().getRegistrationId());
return new Saml2Authentication(principal, responseToken.getToken().getSaml2Response(), privileges);
}
/**
* Builder is used to form a simpleUser based on the saml Assertion.
*
* @author Eugene Biruk
*/
private static class SimpleUserSamlBuilder {
private final String usernameAttribute;
private final String firstNameAttribute;
private final String lastNameAttribute;
private final String groupsAttribute;
private final String emailAttribute;
private final String displayNameAttribute;
private final Map> fields = new HashMap<>();
private String username;
public SimpleUserSamlBuilder(PropertyResolver propertyResolver) {
this.usernameAttribute = propertyResolver.getProperty("security.saml.attribute.username");
this.firstNameAttribute = propertyResolver.getProperty("security.saml.attribute.first-name");
this.lastNameAttribute = propertyResolver.getProperty("security.saml.attribute.last-name");
this.emailAttribute = propertyResolver.getProperty("security.saml.attribute.email");
this.groupsAttribute = propertyResolver.getProperty("security.saml.attribute.groups");
this.displayNameAttribute = propertyResolver.getProperty("security.saml.attribute.display-name");
fields.put(usernameAttribute, new ArrayList<>());
fields.put(firstNameAttribute, new ArrayList<>());
fields.put(lastNameAttribute, new ArrayList<>());
fields.put(emailAttribute, new ArrayList<>());
fields.put(groupsAttribute, new ArrayList<>());
fields.put(displayNameAttribute, new ArrayList<>());
}
private void setAssertionAttributes(Assertion assertion) {
for (AttributeStatement attributeStatement : assertion.getAttributeStatements()) {
for (Attribute attribute : attributeStatement.getAttributes()) {
if (fields.containsKey(attribute.getName())) {
List attributeValues = new ArrayList<>();
for (XMLObject xmlObject : attribute.getAttributeValues()) {
String attributeValue = getAttributeValue(xmlObject);
if (attributeValue != null) {
attributeValues.add(attributeValue);
}
}
fields.get(attribute.getName()).addAll(attributeValues);
}
}
}
}
public void setNameID(String username) {
this.username = username;
}
public SimpleUser build() {
final List grantedAuthorities = new ArrayList<>();
if (StringUtils.isNotBlank(groupsAttribute)) {
for (String name : getAttributeValues(groupsAttribute)) {
grantedAuthorities.add(new SimplePrivilege(name));
}
}
return SimpleUser.builder()
.setFirstName(getAttributeAsString(firstNameAttribute))
.setLastName(getAttributeAsString(lastNameAttribute))
.setUsername(StringUtils.isBlank(usernameAttribute) ? username : getAttributeAsString(usernameAttribute))
.setPrivileges(grantedAuthorities)
.setEmail(getAttributeAsString(emailAttribute))
.setDisplayName(getAttributeAsString(displayNameAttribute))
.build();
}
private String getAttributeAsString(String key) {
List values = fields.get(key);
return CollectionUtils.isNotEmpty(values) ? values.iterator().next() : null;
}
private List getAttributeValues(String key) {
return Collections.unmodifiableList(fields.getOrDefault(key, Collections.emptyList()));
}
// The resulting fields are used to create a SimpleUser, only strings are expected.
private String getAttributeValue(XMLObject xmlObject) {
String textContent;
if (xmlObject instanceof XSString) {
textContent = ((XSString) xmlObject).getValue();
} else if (xmlObject instanceof XSAny) {
textContent = ((XSAny) xmlObject).getTextContent();
} else {
textContent = Optional.ofNullable(xmlObject.getDOM()).map(Node::getTextContent).orElse(null);
}
return StringUtils.trimToNull(textContent);
}
}
}