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.oauth2;
20  
21  import java.util.Set;
22  import org.apache.syncope.sra.ApplicationContextUtils;
23  import org.apache.syncope.sra.SRAProperties;
24  import org.apache.syncope.sra.security.LogoutRouteMatcher;
25  import org.slf4j.Logger;
26  import org.slf4j.LoggerFactory;
27  import org.springframework.cache.CacheManager;
28  import org.springframework.context.ApplicationContext;
29  import org.springframework.context.ConfigurableApplicationContext;
30  import org.springframework.http.MediaType;
31  import org.springframework.security.authentication.DelegatingReactiveAuthenticationManager;
32  import org.springframework.security.authentication.ReactiveAuthenticationManager;
33  import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
34  import org.springframework.security.config.web.server.ServerHttpSecurity;
35  import org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService;
36  import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;
37  import org.springframework.security.oauth2.client.authentication.OAuth2LoginReactiveAuthenticationManager;
38  import org.springframework.security.oauth2.client.endpoint.WebClientReactiveAuthorizationCodeTokenResponseClient;
39  import org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeReactiveAuthenticationManager;
40  import org.springframework.security.oauth2.client.oidc.userinfo.OidcReactiveOAuth2UserService;
41  import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
42  import org.springframework.security.oauth2.client.userinfo.DefaultReactiveOAuth2UserService;
43  import org.springframework.security.oauth2.client.web.server.AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository;
44  import org.springframework.security.oauth2.client.web.server.OAuth2AuthorizationRequestRedirectWebFilter;
45  import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationCodeAuthenticationTokenConverter;
46  import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
47  import org.springframework.security.oauth2.client.web.server.authentication.OAuth2LoginAuthenticationWebFilter;
48  import org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint.DelegateEntry;
49  import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
50  import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
51  import org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint;
52  import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;
53  import org.springframework.security.web.server.authentication.logout.LogoutWebFilter;
54  import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
55  import org.springframework.security.web.server.util.matcher.MediaTypeServerWebExchangeMatcher;
56  import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
57  import reactor.core.publisher.Mono;
58  
59  public final class OAuth2SecurityConfigUtils {
60  
61      private static final Logger LOG = LoggerFactory.getLogger(OAuth2SecurityConfigUtils.class);
62  
63      private static ReactiveAuthenticationManager authenticationManager(final SRAProperties.AMType amType) {
64          WebClientReactiveAuthorizationCodeTokenResponseClient client =
65                  new WebClientReactiveAuthorizationCodeTokenResponseClient();
66          ReactiveAuthenticationManager authenticationManager =
67                  new OAuth2LoginReactiveAuthenticationManager(client, new DefaultReactiveOAuth2UserService());
68  
69          if (SRAProperties.AMType.OIDC == amType) {
70              OidcAuthorizationCodeReactiveAuthenticationManager oidc =
71                      new OidcAuthorizationCodeReactiveAuthenticationManager(client, new OidcReactiveOAuth2UserService());
72              authenticationManager = new DelegatingReactiveAuthenticationManager(oidc, authenticationManager);
73          }
74  
75          return authenticationManager;
76      }
77  
78      public static void forLogin(
79              final ServerHttpSecurity http,
80              final SRAProperties.AMType amType,
81              final ApplicationContext ctx) {
82  
83          ReactiveClientRegistrationRepository clientRegistrationRepository =
84                  ctx.getBean(ReactiveClientRegistrationRepository.class);
85  
86          ReactiveOAuth2AuthorizedClientService authorizedClientService =
87                  new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository);
88          ServerOAuth2AuthorizedClientRepository authorizedClientRepository =
89                  new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(authorizedClientService);
90  
91          OAuth2AuthorizationRequestRedirectWebFilter authRequestRedirectFilter =
92                  new OAuth2AuthorizationRequestRedirectWebFilter(clientRegistrationRepository);
93          http.addFilterAt(authRequestRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC);
94  
95          AuthenticationWebFilter authenticationFilter =
96                  new OAuth2LoginAuthenticationWebFilter(authenticationManager(amType), authorizedClientRepository);
97          authenticationFilter.setRequiresAuthenticationMatcher(
98                  new PathPatternParserServerWebExchangeMatcher("/login/oauth2/code/{registrationId}"));
99          authenticationFilter.setServerAuthenticationConverter(
100                 new ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(clientRegistrationRepository));
101         authenticationFilter.setAuthenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler());
102         authenticationFilter.setAuthenticationFailureHandler((exchange, ex) -> Mono.error(ex));
103         authenticationFilter.setSecurityContextRepository(new WebSessionServerSecurityContextRepository());
104         http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION);
105 
106         MediaTypeServerWebExchangeMatcher htmlMatcher = new MediaTypeServerWebExchangeMatcher(MediaType.TEXT_HTML);
107         htmlMatcher.setIgnoredMediaTypes(Set.of(MediaType.ALL));
108         ServerAuthenticationEntryPoint entrypoint =
109                 new RedirectServerAuthenticationEntryPoint("/oauth2/authorization/" + amType.name());
110         http.exceptionHandling().authenticationEntryPoint(new DelegateEntry(htmlMatcher, entrypoint).getEntryPoint());
111     }
112 
113     public static void forLogout(
114             final ServerHttpSecurity.AuthorizeExchangeSpec builder,
115             final SRAProperties.AMType amType,
116             final CacheManager cacheManager,
117             final LogoutRouteMatcher logoutRouteMatcher,
118             final ConfigurableApplicationContext ctx) {
119 
120         LogoutWebFilter logoutWebFilter = new LogoutWebFilter();
121         logoutWebFilter.setRequiresLogoutMatcher(logoutRouteMatcher);
122         logoutWebFilter.setLogoutHandler(new OAuth2SessionRemovalServerLogoutHandler(cacheManager));
123 
124         if (SRAProperties.AMType.OIDC == amType) {
125             try {
126                 OidcClientInitiatedServerLogoutSuccessHandler handler = ApplicationContextUtils.getOrCreateBean(
127                         ctx,
128                         OidcClientInitiatedServerLogoutSuccessHandler.class.getName(),
129                         OidcClientInitiatedServerLogoutSuccessHandler.class);
130                 logoutWebFilter.setLogoutSuccessHandler(handler);
131             } catch (ClassNotFoundException e) {
132                 LOG.error("While creating instance of {}",
133                         OidcClientInitiatedServerLogoutSuccessHandler.class.getName(), e);
134             }
135         }
136 
137         builder.and().logout().disable().addFilterAt(logoutWebFilter, SecurityWebFiltersOrder.LOGOUT);
138     }
139 
140     private OAuth2SecurityConfigUtils() {
141         // private constructor for static utility class
142     }
143 }