View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.sra.security.cas;
20  
21  import java.net.URI;
22  import org.apache.syncope.sra.security.web.server.DoNothingIfCommittedServerRedirectStrategy;
23  import org.apache.syncope.sra.session.SessionUtils;
24  import org.jasig.cas.client.Protocol;
25  import org.jasig.cas.client.validation.Assertion;
26  import org.jasig.cas.client.validation.TicketValidationException;
27  import org.jasig.cas.client.validation.TicketValidator;
28  import org.jasig.cas.client.validation.json.Cas30JsonServiceTicketValidator;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  import org.springframework.security.authentication.BadCredentialsException;
32  import org.springframework.security.authentication.ReactiveAuthenticationManager;
33  import org.springframework.security.core.Authentication;
34  import org.springframework.security.web.server.ServerRedirectStrategy;
35  import org.springframework.security.web.server.WebFilterExchange;
36  import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
37  import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
38  import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
39  import org.springframework.security.web.server.util.matcher.AndServerWebExchangeMatcher;
40  import org.springframework.security.web.server.util.matcher.NegatedServerWebExchangeMatcher;
41  import reactor.core.publisher.Mono;
42  
43  public class CASAuthenticationWebFilter extends AuthenticationWebFilter {
44  
45      private static final Logger LOG = LoggerFactory.getLogger(CASAuthenticationWebFilter.class);
46  
47      private final Protocol protocol;
48  
49      private final TicketValidator ticketValidator;
50  
51      public CASAuthenticationWebFilter(
52              final ReactiveAuthenticationManager authenticationManager,
53              final Protocol protocol,
54              final String casServerUrlPrefix) {
55  
56          super(authenticationManager);
57  
58          this.protocol = protocol;
59          this.ticketValidator = new Cas30JsonServiceTicketValidator(casServerUrlPrefix);
60  
61          setRequiresAuthenticationMatcher(new AndServerWebExchangeMatcher(
62                  CASUtils.ticketAvailable(protocol),
63                  new NegatedServerWebExchangeMatcher(SessionUtils.authInSession())));
64  
65          setServerAuthenticationConverter(validateAssertion());
66  
67          setAuthenticationSuccessHandler(redirectToInitialRequestURI());
68      }
69  
70      private ServerAuthenticationConverter validateAssertion() {
71          return exchange -> CASUtils.retrieveTicketFromRequest(exchange, protocol).
72                  flatMap(ticket -> {
73                      try {
74                          String serviceUrl = CASUtils.constructServiceUrl(exchange, protocol);
75                          LOG.debug("Constructed service url: {}", serviceUrl);
76  
77                          Assertion assertion = ticketValidator.validate(
78                                  ticket,
79                                  CASUtils.constructServiceUrl(exchange, protocol));
80                          return Mono.just(new CASAuthenticationToken(assertion));
81                      } catch (TicketValidationException e) {
82                          LOG.error("Could not validate {}", ticket, e);
83                          throw new BadCredentialsException("Could not validate " + ticket);
84                      }
85                  });
86      }
87  
88      private ServerAuthenticationSuccessHandler redirectToInitialRequestURI() {
89          return new ServerAuthenticationSuccessHandler() {
90  
91              private final ServerRedirectStrategy redirectStrategy = new DoNothingIfCommittedServerRedirectStrategy();
92  
93              @Override
94              public Mono<Void> onAuthenticationSuccess(
95                      final WebFilterExchange webFilterExchange, final Authentication authentication) {
96  
97                  return webFilterExchange.getExchange().getSession().
98                          flatMap(session -> redirectStrategy.sendRedirect(
99                          webFilterExchange.getExchange(),
100                         session.<URI>getRequiredAttribute(SessionUtils.INITIAL_REQUEST_URI)));
101             }
102         };
103     }
104 }