oauthUserProfileViewRenderer
last analyzed

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
c 0
b 0
f 0
1
package org.apereo.cas.config;
2
3
import org.apereo.cas.CentralAuthenticationService;
4
import org.apereo.cas.authentication.AuthenticationSystemSupport;
5
import org.apereo.cas.authentication.principal.DefaultPrincipalFactory;
6
import org.apereo.cas.authentication.principal.PrincipalFactory;
7
import org.apereo.cas.authentication.principal.Service;
8
import org.apereo.cas.authentication.principal.ServiceFactory;
9
import org.apereo.cas.configuration.CasConfigurationProperties;
10
import org.apereo.cas.configuration.model.support.oauth.OAuthProperties;
11
import org.apereo.cas.services.DenyAllAttributeReleasePolicy;
12
import org.apereo.cas.services.RegexRegisteredService;
13
import org.apereo.cas.services.RegisteredService;
14
import org.apereo.cas.services.ServicesManager;
15
import org.apereo.cas.support.oauth.OAuth20Constants;
16
import org.apereo.cas.support.oauth.authenticator.Authenticators;
17
import org.apereo.cas.support.oauth.authenticator.OAuth20CasAuthenticationBuilder;
18
import org.apereo.cas.support.oauth.authenticator.OAuthClientAuthenticator;
19
import org.apereo.cas.support.oauth.authenticator.OAuthUserAuthenticator;
20
import org.apereo.cas.support.oauth.profile.DefaultOAuth20ProfileScopeToAttributesFilter;
21
import org.apereo.cas.support.oauth.profile.OAuth20ProfileScopeToAttributesFilter;
22
import org.apereo.cas.support.oauth.util.OAuth20Utils;
23
import org.apereo.cas.support.oauth.validator.OAuth20AuthorizationCodeResponseTypeRequestValidator;
24
import org.apereo.cas.support.oauth.validator.OAuth20ClientCredentialsGrantTypeRequestValidator;
25
import org.apereo.cas.support.oauth.validator.OAuth20IdTokenResponseTypeRequestValidator;
26
import org.apereo.cas.support.oauth.validator.OAuth20PasswordGrantTypeRequestValidator;
27
import org.apereo.cas.support.oauth.validator.OAuth20RefreshTokenGrantTypeRequestValidator;
28
import org.apereo.cas.support.oauth.validator.OAuth20RequestValidator;
29
import org.apereo.cas.support.oauth.validator.OAuth20TokenResponseTypeRequestValidator;
30
import org.apereo.cas.support.oauth.validator.OAuth20Validator;
31
import org.apereo.cas.support.oauth.web.OAuth20CasCallbackUrlResolver;
32
import org.apereo.cas.support.oauth.web.OAuth20HandlerInterceptorAdapter;
33
import org.apereo.cas.support.oauth.web.endpoints.OAuth20AccessTokenEndpointController;
34
import org.apereo.cas.support.oauth.web.endpoints.OAuth20AuthorizeEndpointController;
35
import org.apereo.cas.support.oauth.web.endpoints.OAuth20CallbackAuthorizeEndpointController;
36
import org.apereo.cas.support.oauth.web.endpoints.OAuth20UserProfileControllerController;
37
import org.apereo.cas.support.oauth.web.response.OAuth20CasClientRedirectActionBuilder;
38
import org.apereo.cas.support.oauth.web.response.OAuth20DefaultCasClientRedirectActionBuilder;
39
import org.apereo.cas.support.oauth.web.response.accesstoken.AccessTokenResponseGenerator;
40
import org.apereo.cas.support.oauth.web.response.accesstoken.OAuth20AccessTokenResponseGenerator;
41
import org.apereo.cas.support.oauth.web.response.accesstoken.OAuth20DefaultTokenGenerator;
42
import org.apereo.cas.support.oauth.web.response.accesstoken.OAuth20TokenGenerator;
43
import org.apereo.cas.support.oauth.web.response.accesstoken.ext.AccessTokenAuthorizationCodeGrantRequestExtractor;
44
import org.apereo.cas.support.oauth.web.response.accesstoken.ext.AccessTokenClientCredentialsGrantRequestExtractor;
45
import org.apereo.cas.support.oauth.web.response.accesstoken.ext.AccessTokenPasswordGrantRequestExtractor;
46
import org.apereo.cas.support.oauth.web.response.accesstoken.ext.AccessTokenRefreshTokenGrantRequestExtractor;
47
import org.apereo.cas.support.oauth.web.response.accesstoken.ext.BaseAccessTokenGrantRequestExtractor;
48
import org.apereo.cas.support.oauth.web.response.callback.OAuth20AuthorizationCodeAuthorizationResponseBuilder;
49
import org.apereo.cas.support.oauth.web.response.callback.OAuth20AuthorizationResponseBuilder;
50
import org.apereo.cas.support.oauth.web.response.callback.OAuth20ClientCredentialsResponseBuilder;
51
import org.apereo.cas.support.oauth.web.response.callback.OAuth20ResourceOwnerCredentialsResponseBuilder;
52
import org.apereo.cas.support.oauth.web.response.callback.OAuth20TokenAuthorizationResponseBuilder;
53
import org.apereo.cas.support.oauth.web.views.ConsentApprovalViewResolver;
54
import org.apereo.cas.support.oauth.web.views.OAuth20CallbackAuthorizeViewResolver;
55
import org.apereo.cas.support.oauth.web.views.OAuth20ConsentApprovalViewResolver;
56
import org.apereo.cas.support.oauth.web.views.OAuth20DefaultUserProfileViewRenderer;
57
import org.apereo.cas.support.oauth.web.views.OAuth20UserProfileViewRenderer;
58
import org.apereo.cas.ticket.ExpirationPolicy;
59
import org.apereo.cas.ticket.UniqueTicketIdGenerator;
60
import org.apereo.cas.ticket.accesstoken.AccessTokenFactory;
61
import org.apereo.cas.ticket.accesstoken.DefaultAccessTokenFactory;
62
import org.apereo.cas.ticket.accesstoken.OAuthAccessTokenExpirationPolicy;
63
import org.apereo.cas.ticket.code.DefaultOAuthCodeFactory;
64
import org.apereo.cas.ticket.code.OAuthCodeExpirationPolicy;
65
import org.apereo.cas.ticket.code.OAuthCodeFactory;
66
import org.apereo.cas.ticket.refreshtoken.DefaultRefreshTokenFactory;
67
import org.apereo.cas.ticket.refreshtoken.OAuthRefreshTokenExpirationPolicy;
68
import org.apereo.cas.ticket.refreshtoken.RefreshTokenFactory;
69
import org.apereo.cas.ticket.registry.TicketRegistry;
70
import org.apereo.cas.util.CollectionUtils;
71
import org.apereo.cas.util.DefaultUniqueTicketIdGenerator;
72
import org.apereo.cas.util.RandomUtils;
73
import org.apereo.cas.util.RegexUtils;
74
import org.apereo.cas.web.support.CookieRetrievingCookieGenerator;
75
import org.pac4j.cas.client.CasClient;
76
import org.pac4j.cas.config.CasConfiguration;
77
import org.pac4j.core.config.Config;
78
import org.pac4j.core.credentials.UsernamePasswordCredentials;
79
import org.pac4j.core.credentials.authenticator.Authenticator;
80
import org.pac4j.core.http.UrlResolver;
81
import org.pac4j.http.client.direct.DirectBasicAuthClient;
82
import org.pac4j.http.client.direct.DirectFormClient;
83
import org.pac4j.springframework.web.SecurityInterceptor;
84
import org.slf4j.Logger;
85
import org.slf4j.LoggerFactory;
86
import org.springframework.beans.factory.annotation.Autowired;
87
import org.springframework.beans.factory.annotation.Qualifier;
88
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
89
import org.springframework.boot.context.properties.EnableConfigurationProperties;
90
import org.springframework.cloud.context.config.annotation.RefreshScope;
91
import org.springframework.context.ApplicationContext;
92
import org.springframework.context.annotation.Bean;
93
import org.springframework.context.annotation.Configuration;
94
import org.springframework.web.servlet.HandlerInterceptor;
95
import org.springframework.web.servlet.ModelAndView;
96
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
97
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
98
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
99
100
import javax.annotation.PostConstruct;
101
import javax.servlet.http.HttpServletRequest;
102
import javax.servlet.http.HttpServletResponse;
103
import java.util.Collection;
104
import java.util.HashSet;
105
import java.util.Map;
106
import java.util.Set;
107
import java.util.regex.Pattern;
108
import java.util.stream.Collectors;
109
import java.util.stream.Stream;
110
111
import static org.apereo.cas.support.oauth.OAuth20Constants.*;
112
113
/**
114
 * This this {@link CasOAuthConfiguration}.
115
 *
116
 * @author Misagh Moayyed
117
 * @since 5.0.0
118
 */
119
@Configuration("oauthConfiguration")
120
@EnableConfigurationProperties(CasConfigurationProperties.class)
121
public class CasOAuthConfiguration extends WebMvcConfigurerAdapter {
122
    private static final Logger LOGGER = LoggerFactory.getLogger(CasOAuthConfiguration.class);
123
124
    @Autowired
125
    private ApplicationContext applicationContext;
126
127
    @Autowired
128
    @Qualifier("centralAuthenticationService")
129
    private CentralAuthenticationService centralAuthenticationService;
130
131
    @Autowired
132
    private CasConfigurationProperties casProperties;
133
134
    @Autowired
135
    @Qualifier("webApplicationServiceFactory")
136
    private ServiceFactory webApplicationServiceFactory;
137
138
    @Autowired
139
    @Qualifier("servicesManager")
140
    private ServicesManager servicesManager;
141
142
    @Autowired
143
    @Qualifier("defaultAuthenticationSystemSupport")
144
    private AuthenticationSystemSupport authenticationSystemSupport;
145
146
    @Autowired
147
    @Qualifier("ticketRegistry")
148
    private TicketRegistry ticketRegistry;
149
150
    @Autowired
151
    @Qualifier("ticketGrantingTicketCookieGenerator")
152
    private CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator;
153
154
    @ConditionalOnMissingBean(name = "accessTokenResponseGenerator")
155
    @Bean
156
    public AccessTokenResponseGenerator accessTokenResponseGenerator() {
157
        return new OAuth20AccessTokenResponseGenerator();
158
    }
159
160
    @ConditionalOnMissingBean(name = "oauthCasClientRedirectActionBuilder")
161
    @Bean
162
    public OAuth20CasClientRedirectActionBuilder oauthCasClientRedirectActionBuilder() {
163
        return new OAuth20DefaultCasClientRedirectActionBuilder();
164
    }
165
166
    @RefreshScope
167
    @Bean
168
    public UrlResolver casCallbackUrlResolver() {
169
        return new OAuth20CasCallbackUrlResolver(OAuth20Utils.casOAuthCallbackUrl(casProperties.getServer().getPrefix()));
170
    }
171
172
    @RefreshScope
173
    @Bean
174
    public Config oauthSecConfig() {
175
        final CasConfiguration cfg = new CasConfiguration(casProperties.getServer().getLoginUrl());
176
        final CasClient oauthCasClient = new CasClient(cfg);
177
        oauthCasClient.setRedirectActionBuilder(webContext -> oauthCasClientRedirectActionBuilder().build(oauthCasClient, webContext));
178
        oauthCasClient.setName(Authenticators.CAS_OAUTH_CLIENT);
179
        oauthCasClient.setUrlResolver(casCallbackUrlResolver());
180
181
        final Authenticator authenticator = oAuthClientAuthenticator();
182
        final DirectBasicAuthClient basicAuthClient = new DirectBasicAuthClient(authenticator);
183
        basicAuthClient.setName(Authenticators.CAS_OAUTH_CLIENT_BASIC_AUTHN);
184
185
        final DirectFormClient directFormClient = new DirectFormClient(authenticator);
186
        directFormClient.setName(Authenticators.CAS_OAUTH_CLIENT_DIRECT_FORM);
187
        directFormClient.setUsernameParameter(CLIENT_ID);
188
        directFormClient.setPasswordParameter(CLIENT_SECRET);
189
190
        final DirectFormClient userFormClient = new DirectFormClient(oAuthUserAuthenticator());
191
        userFormClient.setName(Authenticators.CAS_OAUTH_CLIENT_USER_FORM);
192
        return new Config(OAuth20Utils.casOAuthCallbackUrl(casProperties.getServer().getPrefix()),
193
                oauthCasClient, basicAuthClient, directFormClient, userFormClient);
194
    }
195
196
    @ConditionalOnMissingBean(name = "requiresAuthenticationAuthorizeInterceptor")
197
    @Bean
198
    @RefreshScope
199
    public SecurityInterceptor requiresAuthenticationAuthorizeInterceptor() {
200
        return new SecurityInterceptor(oauthSecConfig(), Authenticators.CAS_OAUTH_CLIENT);
201
    }
202
203
    @ConditionalOnMissingBean(name = "consentApprovalViewResolver")
204
    @Bean
205
    @RefreshScope
206
    public ConsentApprovalViewResolver consentApprovalViewResolver() {
207
        return new OAuth20ConsentApprovalViewResolver(casProperties);
208
    }
209
210
    @ConditionalOnMissingBean(name = "callbackAuthorizeViewResolver")
211
    @Bean
212
    @RefreshScope
213
    public OAuth20CallbackAuthorizeViewResolver callbackAuthorizeViewResolver() {
214
        return new OAuth20CallbackAuthorizeViewResolver() {
215
        };
216
    }
217
218
    @ConditionalOnMissingBean(name = "requiresAuthenticationAccessTokenInterceptor")
219
    @Bean
220
    @RefreshScope
221
    public SecurityInterceptor requiresAuthenticationAccessTokenInterceptor() {
222
        final String clients = Stream.of(Authenticators.CAS_OAUTH_CLIENT_BASIC_AUTHN,
223
                Authenticators.CAS_OAUTH_CLIENT_DIRECT_FORM,
224
                Authenticators.CAS_OAUTH_CLIENT_USER_FORM).collect(Collectors.joining(","));
225
        return new SecurityInterceptor(oauthSecConfig(), clients);
226
    }
227
228
    @ConditionalOnMissingBean(name = "oauthInterceptor")
229
    @Bean
230
    @RefreshScope
231
    public HandlerInterceptorAdapter oauthInterceptor() {
232
        final String throttler = casProperties.getAuthn().getOauth().getThrottler();
233
        final OAuth20HandlerInterceptorAdapter oAuth20HandlerInterceptorAdapter = new OAuth20HandlerInterceptorAdapter(
234
                requiresAuthenticationAccessTokenInterceptor(), requiresAuthenticationAuthorizeInterceptor(), accessTokenGrantRequestExtractors());
235
236
        if ("neverThrottle".equalsIgnoreCase(throttler)) {
237
            LOGGER.debug("Authentication throttling is disabled for OAuth");
238
            return oAuth20HandlerInterceptorAdapter;
239
        }
240
        return getHandlerInterceptorForThrottling(oAuth20HandlerInterceptorAdapter);
241
    }
242
243
    private HandlerInterceptorAdapter getHandlerInterceptorForThrottling(final OAuth20HandlerInterceptorAdapter oAuth20HandlerInterceptorAdapter) {
244
        final String throttler = casProperties.getAuthn().getOauth().getThrottler();
245
        LOGGER.debug("Locating authentication throttler instance [{}] for OAuth", throttler);
246
        final HandlerInterceptor throttledInterceptor = this.applicationContext.getBean(throttler, HandlerInterceptor.class);
247
        
248
        final String throttledUrl = OAuth20Constants.BASE_OAUTH20_URL.concat("/")
249
                .concat(OAuth20Constants.ACCESS_TOKEN_URL + "|" + OAuth20Constants.TOKEN_URL);
250
        final Pattern pattern = RegexUtils.createPattern(throttledUrl);
251
        LOGGER.debug("Authentication throttler instance for OAuth shall intercept the URL pattern [{}]", pattern.pattern());
252
253
        final HandlerInterceptorAdapter throttledInterceptorAdapter = new HandlerInterceptorAdapter() {
254
            @Override
255
            public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception {
256
                if (RegexUtils.matches(pattern, request.getServletPath()) && !throttledInterceptor.preHandle(request, response, handler)) {
257
                    LOGGER.trace("OAuth authentication throttler prevented the request at [{}]", request.getServletPath());
258
                    return false;
259
                }
260
                return oAuth20HandlerInterceptorAdapter.preHandle(request, response, handler);
261
            }
262
263
            @Override
264
            public void postHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler,
265
                                   final ModelAndView modelAndView) throws Exception {
266
                if (RegexUtils.matches(pattern, request.getServletPath())) {
267
                    LOGGER.trace("OAuth authentication throttler post-processing the request at [{}]", request.getServletPath());
268
                    throttledInterceptor.postHandle(request, response, handler, modelAndView);
269
                }
270
            }
271
        };
272
        return throttledInterceptorAdapter;
273
    }
274
275
    @Override
276
    public void addInterceptors(final InterceptorRegistry registry) {
277
        registry.addInterceptor(oauthInterceptor()).addPathPatterns(BASE_OAUTH20_URL.concat("/").concat("*"));
278
    }
279
280
    @Bean
281
    @RefreshScope
282
    public OAuth20CasClientRedirectActionBuilder defaultOAuthCasClientRedirectActionBuilder() {
283
        return new OAuth20DefaultCasClientRedirectActionBuilder();
284
    }
285
286
    @ConditionalOnMissingBean(name = "oAuthClientAuthenticator")
287
    @Bean
288
    @RefreshScope
289
    public Authenticator<UsernamePasswordCredentials> oAuthClientAuthenticator() {
290
        return new OAuthClientAuthenticator(oAuthValidator(), this.servicesManager);
291
    }
292
293
    @ConditionalOnMissingBean(name = "oAuthUserAuthenticator")
294
    @Bean
295
    @RefreshScope
296
    public Authenticator<UsernamePasswordCredentials> oAuthUserAuthenticator() {
297
        return new OAuthUserAuthenticator(authenticationSystemSupport, servicesManager, webApplicationServiceFactory);
298
    }
299
300
    @ConditionalOnMissingBean(name = "oAuthValidator")
301
    @Bean
302
    @RefreshScope
303
    public OAuth20Validator oAuthValidator() {
304
        return new OAuth20Validator(webApplicationServiceFactory);
305
    }
306
307
    @ConditionalOnMissingBean(name = "oauthAccessTokenResponseGenerator")
308
    @Bean
309
    @RefreshScope
310
    public AccessTokenResponseGenerator oauthAccessTokenResponseGenerator() {
311
        return new OAuth20AccessTokenResponseGenerator();
312
    }
313
314
    @Bean
315
    @RefreshScope
316
    @ConditionalOnMissingBean(name = "defaultAccessTokenFactory")
317
    public AccessTokenFactory defaultAccessTokenFactory() {
318
        return new DefaultAccessTokenFactory(accessTokenIdGenerator(), accessTokenExpirationPolicy());
319
    }
320
321
    @Bean
322
    @ConditionalOnMissingBean(name = "accessTokenExpirationPolicy")
323
    public ExpirationPolicy accessTokenExpirationPolicy() {
324
        final OAuthProperties oauth = casProperties.getAuthn().getOauth();
325
        return new OAuthAccessTokenExpirationPolicy(
326
                oauth.getAccessToken().getMaxTimeToLiveInSeconds(),
327
                oauth.getAccessToken().getTimeToKillInSeconds()
328
        );
329
    }
330
331
    private ExpirationPolicy oAuthCodeExpirationPolicy() {
332
        final OAuthProperties oauth = casProperties.getAuthn().getOauth();
333
        return new OAuthCodeExpirationPolicy(oauth.getCode().getNumberOfUses(), oauth.getCode().getTimeToKillInSeconds());
334
    }
335
336
    @Bean
337
    @RefreshScope
338
    @ConditionalOnMissingBean(name = "oAuthCodeIdGenerator")
339
    public UniqueTicketIdGenerator oAuthCodeIdGenerator() {
340
        return new DefaultUniqueTicketIdGenerator();
341
    }
342
343
    @Bean
344
    @RefreshScope
345
    @ConditionalOnMissingBean(name = "refreshTokenIdGenerator")
346
    public UniqueTicketIdGenerator refreshTokenIdGenerator() {
347
        return new DefaultUniqueTicketIdGenerator();
348
    }
349
350
    @Bean
351
    @RefreshScope
352
    @ConditionalOnMissingBean(name = "defaultOAuthCodeFactory")
353
    public OAuthCodeFactory defaultOAuthCodeFactory() {
354
        return new DefaultOAuthCodeFactory(oAuthCodeIdGenerator(), oAuthCodeExpirationPolicy());
355
    }
356
357
    @ConditionalOnMissingBean(name = "profileScopeToAttributesFilter")
358
    @Bean
359
    public OAuth20ProfileScopeToAttributesFilter profileScopeToAttributesFilter() {
360
        return new DefaultOAuth20ProfileScopeToAttributesFilter();
361
    }
362
363
    @Bean
364
    @ConditionalOnMissingBean(name = "callbackAuthorizeController")
365
    @RefreshScope
366
    public OAuth20CallbackAuthorizeEndpointController callbackAuthorizeController() {
367
        return new OAuth20CallbackAuthorizeEndpointController(servicesManager, ticketRegistry,
368
                oAuthValidator(), defaultAccessTokenFactory(), oauthPrincipalFactory(),
369
                webApplicationServiceFactory,
370
                oauthSecConfig(), callbackAuthorizeViewResolver(),
371
                profileScopeToAttributesFilter(), casProperties, ticketGrantingTicketCookieGenerator);
372
    }
373
374
    @ConditionalOnMissingBean(name = "oauthTokenGenerator")
375
    @Bean
376
    @RefreshScope
377
    public OAuth20TokenGenerator oauthTokenGenerator() {
378
        return new OAuth20DefaultTokenGenerator(defaultAccessTokenFactory(), ticketRegistry, defaultRefreshTokenFactory());
379
    }
380
381
    @Bean
382
    public Collection<BaseAccessTokenGrantRequestExtractor> accessTokenGrantRequestExtractors() {
383
        final BaseAccessTokenGrantRequestExtractor authzCodeExt =
384
                new AccessTokenAuthorizationCodeGrantRequestExtractor(servicesManager, ticketRegistry,
385
                        centralAuthenticationService, casProperties.getAuthn().getOauth());
386
387
        final BaseAccessTokenGrantRequestExtractor refreshTokenExt =
388
                new AccessTokenRefreshTokenGrantRequestExtractor(servicesManager, ticketRegistry,
389
                        centralAuthenticationService, casProperties.getAuthn().getOauth());
390
391
        final BaseAccessTokenGrantRequestExtractor pswExt =
392
                new AccessTokenPasswordGrantRequestExtractor(servicesManager, ticketRegistry,
393
                        oauthCasAuthenticationBuilder(), centralAuthenticationService,
394
                        casProperties.getAuthn().getOauth());
395
396
        final BaseAccessTokenGrantRequestExtractor credsExt =
397
                new AccessTokenClientCredentialsGrantRequestExtractor(servicesManager, ticketRegistry,
398
                        oauthCasAuthenticationBuilder(), centralAuthenticationService,
399
                        casProperties.getAuthn().getOauth());
400
401
        return CollectionUtils.wrapList(authzCodeExt, refreshTokenExt, pswExt, credsExt);
402
    }
403
404
    @ConditionalOnMissingBean(name = "accessTokenController")
405
    @Bean
406
    @RefreshScope
407
    public OAuth20AccessTokenEndpointController accessTokenController() {
408
        return new OAuth20AccessTokenEndpointController(
409
                servicesManager,
410
                ticketRegistry,
411
                oAuthValidator(),
412
                defaultAccessTokenFactory(),
413
                oauthPrincipalFactory(),
414
                webApplicationServiceFactory,
415
                oauthTokenGenerator(),
416
                accessTokenResponseGenerator(),
417
                profileScopeToAttributesFilter(),
418
                casProperties,
419
                ticketGrantingTicketCookieGenerator,
420
                accessTokenExpirationPolicy(),
421
                accessTokenGrantRequestExtractors()
422
        );
423
    }
424
425
    @ConditionalOnMissingBean(name = "oauthUserProfileViewRenderer")
426
    @Bean
427
    @RefreshScope
428
    public OAuth20UserProfileViewRenderer oauthUserProfileViewRenderer() {
429
        return new OAuth20DefaultUserProfileViewRenderer(casProperties.getAuthn().getOauth());
430
    }
431
432
    @ConditionalOnMissingBean(name = "profileController")
433
    @Bean
434
    @RefreshScope
435
    public OAuth20UserProfileControllerController profileController() {
436
        return new OAuth20UserProfileControllerController(servicesManager,
437
                ticketRegistry, oAuthValidator(), defaultAccessTokenFactory(),
438
                oauthPrincipalFactory(), webApplicationServiceFactory,
439
                profileScopeToAttributesFilter(), casProperties,
440
                ticketGrantingTicketCookieGenerator,
441
                oauthUserProfileViewRenderer());
442
    }
443
444
    @ConditionalOnMissingBean(name = "oauthAuthorizationResponseBuilders")
445
    @Bean
446
    @RefreshScope
447
    public Set<OAuth20AuthorizationResponseBuilder> oauthAuthorizationResponseBuilders() {
448
        final Map<String, OAuth20AuthorizationResponseBuilder> builders =
449
                this.applicationContext.getBeansOfType(OAuth20AuthorizationResponseBuilder.class, false, true);
450
        return new HashSet<>(builders.values());
451
    }
452
453
454
    @ConditionalOnMissingBean(name = "oauthRequestValidators")
455
    @Bean
456
    @RefreshScope
457
    public Set<OAuth20RequestValidator> oauthRequestValidators() {
458
        final Map<String, OAuth20RequestValidator> builders =
459
                this.applicationContext.getBeansOfType(OAuth20RequestValidator.class, false, true);
460
        return new HashSet<>(builders.values());
461
    }
462
463
    @ConditionalOnMissingBean(name = "oauthClientCredentialsGrantTypeRequestValidator")
464
    @Bean
465
    @RefreshScope
466
    public OAuth20RequestValidator oauthClientCredentialsGrantTypeRequestValidator() {
467
        return new OAuth20ClientCredentialsGrantTypeRequestValidator(servicesManager, oAuthValidator());
468
    }
469
470
    @ConditionalOnMissingBean(name = "oauthAuthorizationCodeResponseTypeRequestValidator")
471
    @Bean
472
    @RefreshScope
473
    public OAuth20RequestValidator oauthAuthorizationCodeResponseTypeRequestValidator() {
474
        return new OAuth20AuthorizationCodeResponseTypeRequestValidator(servicesManager, oAuthValidator());
475
    }
476
477
    @ConditionalOnMissingBean(name = "oauthTokenResponseTypeRequestValidator")
478
    @Bean
479
    @RefreshScope
480
    public OAuth20RequestValidator oauthTokenResponseTypeRequestValidator() {
481
        return new OAuth20TokenResponseTypeRequestValidator(servicesManager, oAuthValidator());
482
    }
483
484
    @ConditionalOnMissingBean(name = "oauthIdTokenResponseTypeRequestValidator")
485
    @Bean
486
    @RefreshScope
487
    public OAuth20RequestValidator oauthIdTokenResponseTypeRequestValidator() {
488
        return new OAuth20IdTokenResponseTypeRequestValidator(servicesManager, oAuthValidator());
489
    }
490
491
    @ConditionalOnMissingBean(name = "oauthPasswordGrantTypeRequestValidator")
492
    @Bean
493
    @RefreshScope
494
    public OAuth20RequestValidator oauthPasswordGrantTypeRequestValidator() {
495
        return new OAuth20PasswordGrantTypeRequestValidator(servicesManager, oAuthValidator());
496
    }
497
498
    @ConditionalOnMissingBean(name = "oauthRefreshTokenGrantTypeRequestValidator")
499
    @Bean
500
    @RefreshScope
501
    public OAuth20RequestValidator oauthRefreshTokenGrantTypeRequestValidator() {
502
        return new OAuth20RefreshTokenGrantTypeRequestValidator(servicesManager, oAuthValidator());
503
    }
504
505
    @ConditionalOnMissingBean(name = "oauthResourceOwnerCredentialsResponseBuilder")
506
    @Bean
507
    @RefreshScope
508
    public OAuth20AuthorizationResponseBuilder oauthResourceOwnerCredentialsResponseBuilder() {
509
        return new OAuth20ResourceOwnerCredentialsResponseBuilder(accessTokenResponseGenerator(), oauthTokenGenerator(),
510
                accessTokenExpirationPolicy());
511
    }
512
513
    @ConditionalOnMissingBean(name = "oauthClientCredentialsResponseBuilder")
514
    @Bean
515
    @RefreshScope
516
    public OAuth20AuthorizationResponseBuilder oauthClientCredentialsResponseBuilder() {
517
        return new OAuth20ClientCredentialsResponseBuilder(accessTokenResponseGenerator(),
518
                oauthTokenGenerator(), accessTokenExpirationPolicy());
519
    }
520
521
    @ConditionalOnMissingBean(name = "oauthTokenResponseBuilder")
522
    @Bean
523
    @RefreshScope
524
    public OAuth20AuthorizationResponseBuilder oauthTokenResponseBuilder() {
525
        return new OAuth20TokenAuthorizationResponseBuilder(oauthTokenGenerator(), accessTokenExpirationPolicy());
526
    }
527
528
    @ConditionalOnMissingBean(name = "oauthAuthorizationCodeResponseBuilder")
529
    @Bean
530
    @RefreshScope
531
    public OAuth20AuthorizationResponseBuilder oauthAuthorizationCodeResponseBuilder() {
532
        return new OAuth20AuthorizationCodeAuthorizationResponseBuilder(ticketRegistry, defaultOAuthCodeFactory());
533
    }
534
535
    @ConditionalOnMissingBean(name = "authorizeController")
536
    @Bean
537
    @RefreshScope
538
    public OAuth20AuthorizeEndpointController authorizeController() {
539
        return new OAuth20AuthorizeEndpointController(
540
                servicesManager, ticketRegistry, oAuthValidator(), defaultAccessTokenFactory(),
541
                oauthPrincipalFactory(), webApplicationServiceFactory, defaultOAuthCodeFactory(),
542
                consentApprovalViewResolver(), profileScopeToAttributesFilter(), casProperties,
543
                ticketGrantingTicketCookieGenerator, oauthCasAuthenticationBuilder(),
544
                oauthAuthorizationResponseBuilders(), oauthRequestValidators()
545
        );
546
    }
547
548
    @ConditionalOnMissingBean(name = "oauthPrincipalFactory")
549
    @Bean
550
    @RefreshScope
551
    public PrincipalFactory oauthPrincipalFactory() {
552
        return new DefaultPrincipalFactory();
553
    }
554
555
    @Bean
556
    @RefreshScope
557
    @ConditionalOnMissingBean(name = "defaultRefreshTokenFactory")
558
    public RefreshTokenFactory defaultRefreshTokenFactory() {
559
        return new DefaultRefreshTokenFactory(refreshTokenIdGenerator(), refreshTokenExpirationPolicy());
560
    }
561
562
    private ExpirationPolicy refreshTokenExpirationPolicy() {
563
        return new OAuthRefreshTokenExpirationPolicy(casProperties.getAuthn().getOauth().getRefreshToken().getTimeToKillInSeconds());
564
    }
565
566
    @ConditionalOnMissingBean(name = "oauthCasAuthenticationBuilder")
567
    @Bean
568
    @RefreshScope
569
    public OAuth20CasAuthenticationBuilder oauthCasAuthenticationBuilder() {
570
        return new OAuth20CasAuthenticationBuilder(oauthPrincipalFactory(), webApplicationServiceFactory,
571
                profileScopeToAttributesFilter(), casProperties);
572
    }
573
574
    @ConditionalOnMissingBean(name = "accessTokenIdGenerator")
575
    @Bean
576
    @RefreshScope
577
    public UniqueTicketIdGenerator accessTokenIdGenerator() {
578
        return new DefaultUniqueTicketIdGenerator();
579
    }
580
581
    @PostConstruct
582
    public void initializeServletApplicationContext() {
583
        final String oAuthCallbackUrl = casProperties.getServer().getPrefix() + BASE_OAUTH20_URL + '/' + CALLBACK_AUTHORIZE_URL_DEFINITION;
584
585
        final Service callbackService = this.webApplicationServiceFactory.createService(oAuthCallbackUrl);
586
        final RegisteredService svc = servicesManager.findServiceBy(callbackService);
587
588
        if (svc == null || !svc.getServiceId().equals(oAuthCallbackUrl)) {
589
            final RegexRegisteredService service = new RegexRegisteredService();
590
            service.setId(Math.abs(RandomUtils.getInstanceNative().nextLong()));
591
            service.setEvaluationOrder(0);
592
            service.setName(service.getClass().getSimpleName());
593
            service.setDescription("OAuth Authentication Callback Request URL");
594
            service.setServiceId(oAuthCallbackUrl);
595
            service.setAttributeReleasePolicy(new DenyAllAttributeReleasePolicy());
596
597
            servicesManager.save(service);
598
            servicesManager.load();
599
        }
600
    }
601
}
602