Open uniquejava opened 1 year ago
For spring boot, this is the dependency I added in pom.xml.
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-saml2-service-provider -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-saml2-service-provider</artifactId>
</dependency>
And then I was able to extract saml attributes by configuring a SecurityBean.
The assertion.getAttributeStatements().stream()
part.
@EnableWebSecurity
public class BootSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
// Create a filter to generate a SAML Metadata file for the Application
Saml2MetadataFilter filter = new Saml2MetadataFilter(
(RelyingPartyRegistrationResolver)new DefaultRelyingPartyRegistrationResolver(this.relyingPartyRegistrationRepository),
new OpenSamlMetadataResolver());
// No Transformation of Authorities done
GrantedAuthoritiesMapper authoritiesMapper = (authCol -> authCol);
// Actual code to extract the Authorities (or roles) from the Assertion
Converter<Assertion, Collection<? extends GrantedAuthority>> authoritiesExtractor = assertion -> {
List<SimpleGrantedAuthority> userRoles
= assertion.getAttributeStatements().stream()
.map(AttributeStatement::getAttributes)
.flatMap(Collection::stream)
.filter(attr -> "groups".equalsIgnoreCase(attr.getName()))
.map(Attribute::getAttributeValues)
.flatMap(Collection::stream)
.map(xml -> new SimpleGrantedAuthority("ROLE_" + xml.getDOM().getTextContent()))
.toList();
return userRoles;
};
http
.saml2Login()
.addObjectPostProcessor(new ObjectPostProcessor<OpenSamlAuthenticationProvider>() {
public <P extends OpenSamlAuthenticationProvider> P postProcess(
P samlAuthProvider) {
// Set the Authorities extractor
samlAuthProvider.setAuthoritiesExtractor(authoritiesExtractor);
samlAuthProvider.setAuthoritiesMapper(authoritiesMapper);
return samlAuthProvider;
}
});
http
.saml2Logout(withDefaults())
.addFilterBefore(filter, Saml2WebSsoAuthenticationFilter.class)
.authorizeRequests()
.mvcMatchers("/", "/api").hasAnyRole("user","admin")
.mvcMatchers("/edit/**").hasAnyRole("admin")
.mvcMatchers("/css/**").permitAll()
.anyRequest().denyAll();
}
}
And then, use the Authentication
object anywhere in Rest Controller to get user attributes/groups.
My intention is, from within in the PostProcessor or SAML ACS Url Callback class, if only I could optionally create a new user in our own app_users
table if IDP authenticated user(email) not exist in our application.
I then can perform some permission control based on SQL joints like the following.
select * from business_table t left join app_users u on u.userid = t.userid and u.role='admin'
Or I can craft a JWT token in the callback class and send it to angular frontend. I am not sure how we could gracefully handle all this in the OpenLiberty world.
I managed to get the necessary dependencies later today. I am using wlp-webProfile8-22.0.0.13
, not sure the following are using the correct version(I think it should, I searched inside the OL lib directory got the versions there), but if there were some BOM file defined for us(like what spring boot does), that would be easier.
<dependency>
<groupId>com.ibm.websphere.appserver.api</groupId>
<artifactId>com.ibm.websphere.appserver.api.security</artifactId>
<version>1.3.72</version>
</dependency>
<dependency>
<groupId>com.ibm.websphere.appserver.api</groupId>
<artifactId>com.ibm.websphere.appserver.api.saml20</artifactId>
<version>1.1.72</version>
</dependency>
<dependency>
<groupId>com.ibm.websphere.appserver.spi</groupId>
<artifactId>com.ibm.websphere.appserver.spi.saml20</artifactId>
<version>1.0.72</version>
</dependency>
<dependency>
<groupId>com.ibm.websphere.appserver.api</groupId>
<artifactId>com.ibm.websphere.appserver.api.basics</artifactId>
<version>1.4.72</version>
</dependency>
Now I am able to extract user groups in side a Resource Class like the following:
@RequestScoped
@Path("/some")
public class SomeResource {
@Context
private SecurityContext securityContext;
@GET
public Response execute() throws Exception {
System.out.println(securityContext.isUserInRole("admin"));
System.out.println(securityContext.getUserPrincipal());
// https://stackoverflow.com/a/34891405/2497876
Subject subject = WSSubject.getRunAsSubject();
// logger.info(subject.toString());
Iterator authIterator = subject.getPrivateCredentials(Saml20Token.class).iterator();
if (authIterator.hasNext()) {
Saml20Token samlToken = (Saml20Token) authIterator.next();
// prints out raw assertion xml
// <?xml version="1.0" encoding="UTF-8"?><saml:Assertion ...
// System.out.println(samlToken.getSAMLAsString());
System.out.println("getSAMLNameID => " + samlToken.getSAMLNameID());
// hooray! prints out [admin, login-user]
List<String> groups = samlToken.getSAMLAttributes().stream()
.filter(attr -> "groups".equalsIgnoreCase(attr.getName())).map(attr -> attr.getValuesAsString())
.flatMap(Collection::stream)
.collect(Collectors.toList());
System.out.println(groups);
}
....
It prints out:
[INFO] false
[INFO] WSPrincipal:adminx@example.com
[INFO] getSAMLNameID => adminx@example.com
[INFO] [login-user, admin]
I am still wondering if there is a way for us to construct some Authentication
object after SSO authentication is performed.
I wrote the following UserCredentialResolver
class, it seems that it's not loaded and doesn't work.
So where can I activate this class? I can't find any document. :(
package some.package;
import com.ibm.wsspi.security.saml2.UserCredentialResolver;
import com.ibm.wsspi.security.saml2.UserIdentityException;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
import java.util.stream.Collectors;
public class MyUserCredentialResolver implements UserCredentialResolver {
static Logger logger = Logger.getLogger(MyUserCredentialResolver.class.getName());
public String mapSAMLAssertionToUser(com.ibm.websphere.security.saml2.Saml20Token saml20Token) throws UserIdentityException {
logger.info("mapSAMLAssertionToUser");
return saml20Token.getSAMLNameID();
}
public List<String> mapSAMLAssertionToGroups(com.ibm.websphere.security.saml2.Saml20Token saml20Token) throws UserIdentityException {
logger.info("mapSAMLAssertionToGroups");
List<String> groups = saml20Token.getSAMLAttributes().stream()
.filter(attr -> "groups".equalsIgnoreCase(attr.getName())).map(attr->attr.getValuesAsString())
.flatMap(Collection::stream)
.collect(Collectors.toList());
return groups;
}
public String mapSAMLAssertionToUserUniqueID(com.ibm.websphere.security.saml2.Saml20Token saml20Token) throws UserIdentityException {
logger.info("mapSAMLAssertionToUserUniqueID");
return this.mapSAMLAssertionToUser(saml20Token);
}
public String mapSAMLAssertionToRealm(com.ibm.websphere.security.saml2.Saml20Token saml20Token) throws UserIdentityException {
return null;
}
}
I am expecting some SSOAuthenticationSuccessEvent and/or SSOAuthenticationFailureEvent, then I can listen to it to do some auditing, for example, we should always insert a login record in some Login_History
table and this kind of Event or Callback is much needed.
To conclude, my concern/questions are:
groups
attribute from Saml Assertion to Liberty roles
without using User Registry, so that I can use @RolesAllowed("admin")
annotation to do authorization.App_Users
tableUserCredentialResolver
in Liberty's server.xml if this is a solution for Question number one.TAIResult
object, but even I set invokeAfterSSO="true"
it's not being called. I am new to OpenLiberty, any suggestion is much appreciated.
Hello.
We are moving from Spring Boot to OpenLiberty. Spring Security has SAML support and after studying OpenLiberty documents, especially the SAML Web SSO part, I defined the following configuration file.
Now when I access the Backend API - a JAX-RS project , browser redirects me to IDP login page, and I was able to get SAML Response after enter user credentials.
The SAML response likes the following, (As some test, I tried Okta and Keycloak as IDP provider, they are similar)
My question is there any SAML ACS Url callback for us to Post Process SAML Token?
And also How can I get Custom User Attributes especially User Groups from SAML Token and put it into http session for wrap them into a JWT token?
I tried to inject SecurityContext into a Resource class like this.
For now, I can only get user's principal (email) in my case. I want to get other attributes like "first, last name, age, title, groups" etc.
Later, I found this class com.ibm.wsspi.security.saml2.UserCredentialResolver, the description looks quite promising to me.
I don't know where to find this class, I search maven repository and can't find any related maven dependency.
Would you please kindly shed me some light how to continue?