1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.sra.security.saml2;
20
21 import java.net.URI;
22 import org.apache.syncope.sra.security.pac4j.NoOpSessionStore;
23 import org.apache.syncope.sra.security.pac4j.ServerWebExchangeContext;
24 import org.apache.syncope.sra.security.web.server.DoNothingIfCommittedServerRedirectStrategy;
25 import org.apache.syncope.sra.session.SessionUtils;
26 import org.pac4j.saml.client.SAML2Client;
27 import org.pac4j.saml.credentials.SAML2Credentials;
28 import org.springframework.security.authentication.ReactiveAuthenticationManager;
29 import org.springframework.security.core.Authentication;
30 import org.springframework.security.web.server.ServerRedirectStrategy;
31 import org.springframework.security.web.server.WebFilterExchange;
32 import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
33 import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
34 import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
35 import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
36 import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
37 import org.springframework.web.server.ServerWebExchange;
38 import org.springframework.web.server.WebFilterChain;
39 import reactor.core.publisher.Mono;
40
41 public class SAML2WebSsoAuthenticationWebFilter extends AuthenticationWebFilter {
42
43 public static final String FILTER_PROCESSES_URI = "/login/saml2/sso";
44
45 private static final ServerWebExchangeMatcher MATCHER =
46 ServerWebExchangeMatchers.pathMatchers(FILTER_PROCESSES_URI);
47
48 private final SAML2Client saml2Client;
49
50 public SAML2WebSsoAuthenticationWebFilter(
51 final ReactiveAuthenticationManager authenticationManager,
52 final SAML2Client saml2Client) {
53
54 super(authenticationManager);
55
56 this.saml2Client = saml2Client;
57
58 setRequiresAuthenticationMatcher(matchSamlResponse());
59
60 setServerAuthenticationConverter(convertSamlResponse());
61
62 setAuthenticationSuccessHandler(redirectToInitialRequestURI());
63 }
64
65 @Override
66 public Mono<Void> filter(final ServerWebExchange exchange, final WebFilterChain chain) {
67 return super.filter(exchange, chain).then(Mono.defer(exchange.getResponse()::setComplete));
68 }
69
70 private ServerWebExchangeMatcher matchSamlResponse() {
71 return exchange -> exchange.getFormData().
72 filter(form -> form.containsKey("SAMLResponse")).
73 flatMap(form -> ServerWebExchangeMatcher.MatchResult.match()).
74 switchIfEmpty(ServerWebExchangeMatcher.MatchResult.notMatch());
75 }
76
77 private ServerAuthenticationConverter convertSamlResponse() {
78 return exchange -> exchange.getFormData().
79 flatMap(form -> MATCHER.matches(exchange).
80 flatMap(matchResult -> {
81 ServerWebExchangeContext swec = new ServerWebExchangeContext(exchange).setForm(form);
82
83 SAML2Credentials credentials = (SAML2Credentials) saml2Client.getCredentialsExtractor().
84 extract(swec, NoOpSessionStore.INSTANCE).
85 orElseThrow(() -> new IllegalStateException("No AuthnResponse found"));
86
87 saml2Client.getAuthenticator().validate(credentials, swec, NoOpSessionStore.INSTANCE);
88
89 return Mono.just(new SAML2AuthenticationToken(credentials));
90 }));
91 }
92
93 private ServerAuthenticationSuccessHandler redirectToInitialRequestURI() {
94 return new ServerAuthenticationSuccessHandler() {
95
96 private final ServerRedirectStrategy redirectStrategy = new DoNothingIfCommittedServerRedirectStrategy();
97
98 @Override
99 public Mono<Void> onAuthenticationSuccess(
100 final WebFilterExchange webFilterExchange, final Authentication authentication) {
101
102 return webFilterExchange.getExchange().getSession().
103 flatMap(session -> this.redirectStrategy.sendRedirect(
104 webFilterExchange.getExchange(),
105 session.<URI>getRequiredAttribute(SessionUtils.INITIAL_REQUEST_URI)));
106 }
107 };
108 }
109 }