Commit 64dcc6ee authored by Gradl, Tobias's avatar Gradl, Tobias
Browse files

5: Implement compatible User/Role behavior from CommonProfile

Task-Url: #5
parent 114b02a7
Pipeline #17801 passed with stage
in 1 minute and 55 seconds
package eu.dariah.de.dariahsp;
import java.util.LinkedHashMap;
import org.pac4j.core.context.JEEContext;
import org.pac4j.core.context.WebContext;
import org.pac4j.core.context.session.SessionStore;
import org.pac4j.core.profile.CommonProfile;
import org.pac4j.springframework.security.profile.SpringSecurityProfileManager;
import eu.dariah.de.dariahsp.model.ExtendedUserProfile;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class CustomizableProfileManager extends SpringSecurityProfileManager {
private final ProfileActionHandler postprocessor;
public CustomizableProfileManager(final WebContext context, final ProfileActionHandler postprocessor) {
super(context);
this.postprocessor = postprocessor;
}
public CustomizableProfileManager(final WebContext context, final SessionStore<JEEContext> sessionStore, final ProfileActionHandler postprocessor) {
super(context, sessionStore);
this.postprocessor = postprocessor;
}
@Override
public void remove(final boolean removeFromSession) {
if (postprocessor!=null) {
LinkedHashMap<String, CommonProfile> profiles = super.retrieveAll(removeFromSession);
for (CommonProfile p : profiles.values()) {
postprocessor.handleLogout(this.castOrConvert(p));
}
}
super.remove(removeFromSession);
}
@Override
protected LinkedHashMap<String, CommonProfile> retrieveAll(boolean readFromSession) {
LinkedHashMap<String, CommonProfile> profiles = super.retrieveAll(readFromSession);
if (postprocessor==null) {
return profiles;
}
for (CommonProfile p : profiles.values()) {
postprocessor.handleActivity(this.castOrConvert(p));
}
return profiles;
}
@Override
protected void saveAll(LinkedHashMap<String, CommonProfile> profiles, boolean saveInSession) {
super.saveAll(profiles, saveInSession);
if (postprocessor==null) {
return;
}
for (CommonProfile p : profiles.values()) {
postprocessor.handleLogin(this.castOrConvert(p));
}
}
private ExtendedUserProfile castOrConvert(CommonProfile p) {
if (ExtendedUserProfile.class.isAssignableFrom(p.getClass())) {
return ExtendedUserProfile.class.cast(p);
} else {
log.warn("Expected ExtendedUserProfile for processing, but received {}", p.getClass().getSimpleName());
return new ExtendedUserProfile(p);
}
}
}
\ No newline at end of file
package eu.dariah.de.dariahsp;
import eu.dariah.de.dariahsp.model.ExtendedUserProfile;
public interface ProfileActionHandler {
/**
* Triggered when a user is logging in
*
* @param profile ExtendedUserProfile
*/
public void handleLogin(ExtendedUserProfile profile);
/**
* Triggered when a user is logging out
*
* @param profile ExtendedUserProfile
*/
public void handleLogout(ExtendedUserProfile profile);
/**
* Triggered upon any activity related to a profile. This method is only triggered by requests
* are matched by any security filters.
*
* Note: at login- and logout-time, this method is triggered before the according methods
*
* @param profile ExtendedUserProfile
*/
public default void handleActivity(ExtendedUserProfile profile) {};
}
package eu.dariah.de.dariahsp;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.pac4j.core.config.Config;
import org.pac4j.core.context.JEEContext;
import org.pac4j.core.context.session.JEESessionStore;
import org.pac4j.core.context.session.SessionStore;
import org.pac4j.core.engine.DefaultSecurityLogic;
import org.pac4j.core.engine.SecurityLogic;
import org.pac4j.core.http.adapter.HttpActionAdapter;
import org.pac4j.core.http.adapter.JEEHttpActionAdapter;
import org.pac4j.core.util.FindBest;
/**
* This SecurityFilter implementation is based on {@link org.pac4j.springframework.security.web.SecurityFilter}.
* It is required because the referenced default implementation statically sets the ProfileManagerFactory,
* of which more control is required.
*
* @author Tobias Gradl
*/
@SuppressWarnings({ "unchecked" })
public class SecurityFilter implements Filter {
private final Config config;
private final String clients;
public SecurityFilter(final Config config, final String clients) {
this.config = config;
this.clients = clients;
}
@Override
public void doFilter(final ServletRequest req, final ServletResponse resp, final FilterChain filterChain) throws IOException, ServletException {
final SessionStore<JEEContext> bestSessionStore = FindBest.sessionStore(null, config, JEESessionStore.INSTANCE);
final HttpActionAdapter<Object, JEEContext> bestAdapter = FindBest.httpActionAdapter(null, config, JEEHttpActionAdapter.INSTANCE);
final SecurityLogic<Object, JEEContext> bestLogic = FindBest.securityLogic(null, config, DefaultSecurityLogic.INSTANCE);
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) resp;
final JEEContext context = new JEEContext(request, response, bestSessionStore);
bestLogic.perform(context, this.config, (ctx, profiles, parameters) -> {
filterChain.doFilter(request, response);
return null;
}, bestAdapter, this.clients, null, null, false);
}
}
......@@ -11,11 +11,14 @@ import java.util.Optional;
import org.pac4j.core.client.Client;
import org.pac4j.core.client.Clients;
import org.pac4j.core.config.Config;
import org.pac4j.core.profile.factory.ProfileManagerFactory;
import org.pac4j.http.client.indirect.FormClient;
import org.pac4j.saml.client.SAML2Client;
import org.pac4j.saml.config.SAML2Configuration;
import org.pac4j.springframework.annotation.AnnotationConfig;
import org.pac4j.springframework.component.ComponentConfig;
import org.pac4j.springframework.security.profile.SpringSecurityProfileManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
......@@ -26,13 +29,15 @@ import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.access.vote.RoleHierarchyVoter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import eu.dariah.de.dariahsp.CustomizableProfileManager;
import eu.dariah.de.dariahsp.ProfileActionHandler;
import eu.dariah.de.dariahsp.authentication.LocalUsernamePasswordAuthenticator;
import eu.dariah.de.dariahsp.authentication.SAMLRequiredAttributeAuthenticator;
import eu.dariah.de.dariahsp.config.local.LocalSecurity;
import eu.dariah.de.dariahsp.config.saml.SAMLSecurity;
import eu.dariah.de.dariahsp.helpers.SAMLMetadataHelper;
import eu.dariah.de.dariahsp.profilecreation.LocalProfileCreator;
import eu.dariah.de.dariahsp.profilecreation.SamlProfileCreator;
import eu.dariah.de.dariahsp.profiles.LocalProfileCreator;
import eu.dariah.de.dariahsp.profiles.SamlProfileCreator;
import eu.dariah.de.dariahsp.web.AuthInfoHelper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
......@@ -53,12 +58,10 @@ public class SecurityConfig {
private String baseUrl = "http://localhost:8080";
private String defaultLoginUrl = null;
private String defaultLogoutUrl = null;
public String getDefaultLoginUrl() { return defaultLoginUrl==null ? baseUrl : defaultLoginUrl; }
public String getDefaultLogoutUrl() { return defaultLogoutUrl==null ? baseUrl : defaultLogoutUrl; }
@Bean
public Optional<LocalUsernamePasswordAuthenticator> localUsernamePasswordAuthenticator() {
if (!local.isEnabled()) {
......@@ -101,7 +104,7 @@ public class SecurityConfig {
@Bean
@SuppressWarnings("rawtypes")
public Config config() throws URISyntaxException {
public Config config(Optional<ProfileActionHandler> profileActionHandler) throws URISyntaxException {
List<Client> clients = new ArrayList<>();
SAML2Client samlClient = getSamlClient();
FormClient formClient = getFormClient();
......@@ -112,6 +115,7 @@ public class SecurityConfig {
if (formClient!=null) {
clients.add(formClient);
}
Config.setProfileManagerFactory("customizableProfileManager", ctx -> new CustomizableProfileManager(ctx, profileActionHandler.orElse(null)));
return new Config(new Clients(baseUrl().getAbsoluteUrl("/callback"), clients));
}
......@@ -161,7 +165,6 @@ public class SecurityConfig {
c.setName(saml.getAuthorizerName());
c.setProfileCreator(saml2ProfileCreator());
c.setAuthenticator(new SAMLRequiredAttributeAuthenticator(cfg.getAttributeAsId(), cfg.getMappedAttributes(), saml.getSp()));
return c;
}
......
......@@ -22,5 +22,5 @@ public class SAMLSecurity {
@Getter @Setter
public class MetadataProperties {
private String url;
}
}
}
\ No newline at end of file
......@@ -4,7 +4,6 @@ import java.util.List;
import java.util.stream.Collectors;
import org.pac4j.springframework.security.web.Pac4jEntryPoint;
import org.pac4j.springframework.security.web.SecurityFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
......@@ -14,6 +13,8 @@ import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import eu.dariah.de.dariahsp.SecurityFilter;
public class SecurityConfigurerAdapter extends BaseSecurityConfigurerAdapter {
@Autowired private RoleHierarchy roleHierarchy;
......
package eu.dariah.de.dariahsp.profilecreation;
package eu.dariah.de.dariahsp.profiles;
import java.util.LinkedHashSet;
import java.util.List;
......
package eu.dariah.de.dariahsp.profilecreation;
package eu.dariah.de.dariahsp.profiles;
import java.util.Optional;
import org.pac4j.core.context.WebContext;
......
package eu.dariah.de.dariahsp.profilecreation;
package eu.dariah.de.dariahsp.profiles;
import java.util.LinkedHashSet;
import java.util.List;
......
......@@ -3,13 +3,22 @@ package eu.dariah.de.dariahsp.sample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import eu.dariah.de.dariahsp.ProfileActionHandler;
import eu.dariah.de.dariahsp.sample.profiles.SampleProfileActionHandler;
@SpringBootApplication
@ConfigurationPropertiesScan
@ComponentScan({"eu.dariah.de.dariahsp.sample", "eu.dariah.de.dariahsp.web.controller"})
public class SampleApplication {
@Bean
public ProfileActionHandler profileActionPostprocessor() {
return new SampleProfileActionHandler();
}
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
......
package eu.dariah.de.dariahsp.sample.profiles;
import eu.dariah.de.dariahsp.ProfileActionHandler;
import eu.dariah.de.dariahsp.model.ExtendedUserProfile;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SampleProfileActionHandler implements ProfileActionHandler {
@Override
public void handleLogin(ExtendedUserProfile profile) {
log.debug("User has logged in: {}", profile.getId());
}
@Override
public void handleLogout(ExtendedUserProfile profile) {
log.debug("User has logged out: {}", profile.getId());
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment