From caf4c471053aa23f7c8dc2c2d484450f02657430 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Mon, 25 Oct 2021 13:38:04 -0300 Subject: [PATCH] Remove CAS module Closes gh-10441 --- cas/spring-security-cas.gradle | 25 - .../security/cas/SamlServiceProperties.java | 37 -- .../security/cas/ServiceProperties.java | 132 ----- .../CasAssertionAuthenticationToken.java | 60 --- .../CasAuthenticationProvider.java | 244 --------- .../CasAuthenticationToken.java | 173 ------- .../NullStatelessTicketCache.java | 64 --- .../SpringCacheBasedTicketCache.java | 69 --- .../authentication/StatelessTicketCache.java | 110 ----- .../cas/authentication/package-info.java | 21 - .../cas/jackson2/AssertionImplMixin.java | 69 --- .../jackson2/AttributePrincipalImplMixin.java | 66 --- .../jackson2/CasAuthenticationTokenMixin.java | 83 ---- .../cas/jackson2/CasJackson2Module.java | 58 --- .../security/cas/package-info.java | 21 - ...bstractCasAssertionUserDetailsService.java | 51 -- ...AssertionAttributesUserDetailsService.java | 87 ---- .../cas/web/CasAuthenticationEntryPoint.java | 150 ------ .../cas/web/CasAuthenticationFilter.java | 397 --------------- .../DefaultServiceAuthenticationDetails.java | 146 ------ .../ServiceAuthenticationDetails.java | 42 -- .../ServiceAuthenticationDetailsSource.java | 83 ---- .../cas/web/authentication/package-info.java | 21 - .../security/cas/web/package-info.java | 20 - .../AbstractStatelessTicketCacheTests.java | 45 -- .../CasAuthenticationProviderTests.java | 382 --------------- .../CasAuthenticationTokenTests.java | 169 ------- .../NullStatelessTicketCacheTests.java | 46 -- .../SpringCacheBasedTicketCacheTests.java | 65 --- .../CasAuthenticationTokenMixinTests.java | 153 ------ ...tionAttributesUserDetailsServiceTests.java | 63 --- .../web/CasAuthenticationEntryPointTests.java | 98 ---- .../cas/web/CasAuthenticationFilterTests.java | 199 -------- .../cas/web/ServicePropertiesTests.java | 67 --- ...aultServiceAuthenticationDetailsTests.java | 132 ----- cas/src/test/resources/logback-test.xml | 15 - ...tserviceauthenticationdetails-explicit.xml | 23 - ...serviceauthenticationdetails-passivity.xml | 15 - config/spring-security-config.gradle | 1 - .../web/builders/HttpConfigurationTests.java | 31 -- docs/modules/ROOT/nav.adoc | 1 - .../pages/servlet/authentication/cas.adoc | 463 ------------------ .../pages/servlet/authentication/index.adoc | 1 - .../pages/servlet/authentication/logout.adoc | 1 - 44 files changed, 4199 deletions(-) delete mode 100644 cas/spring-security-cas.gradle delete mode 100644 cas/src/main/java/org/springframework/security/cas/SamlServiceProperties.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/ServiceProperties.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/authentication/CasAssertionAuthenticationToken.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationToken.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/authentication/NullStatelessTicketCache.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/authentication/SpringCacheBasedTicketCache.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/authentication/StatelessTicketCache.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/authentication/package-info.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/jackson2/AssertionImplMixin.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/jackson2/AttributePrincipalImplMixin.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/jackson2/CasAuthenticationTokenMixin.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/jackson2/CasJackson2Module.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/package-info.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/userdetails/AbstractCasAssertionUserDetailsService.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/userdetails/GrantedAuthorityFromAssertionAttributesUserDetailsService.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationEntryPoint.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/web/authentication/DefaultServiceAuthenticationDetails.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/web/authentication/ServiceAuthenticationDetails.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/web/authentication/ServiceAuthenticationDetailsSource.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/web/authentication/package-info.java delete mode 100644 cas/src/main/java/org/springframework/security/cas/web/package-info.java delete mode 100644 cas/src/test/java/org/springframework/security/cas/authentication/AbstractStatelessTicketCacheTests.java delete mode 100644 cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationProviderTests.java delete mode 100644 cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationTokenTests.java delete mode 100644 cas/src/test/java/org/springframework/security/cas/authentication/NullStatelessTicketCacheTests.java delete mode 100644 cas/src/test/java/org/springframework/security/cas/authentication/SpringCacheBasedTicketCacheTests.java delete mode 100644 cas/src/test/java/org/springframework/security/cas/jackson2/CasAuthenticationTokenMixinTests.java delete mode 100644 cas/src/test/java/org/springframework/security/cas/userdetails/GrantedAuthorityFromAssertionAttributesUserDetailsServiceTests.java delete mode 100644 cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationEntryPointTests.java delete mode 100644 cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationFilterTests.java delete mode 100644 cas/src/test/java/org/springframework/security/cas/web/ServicePropertiesTests.java delete mode 100644 cas/src/test/java/org/springframework/security/cas/web/authentication/DefaultServiceAuthenticationDetailsTests.java delete mode 100644 cas/src/test/resources/logback-test.xml delete mode 100644 cas/src/test/resources/org/springframework/security/cas/web/authentication/defaultserviceauthenticationdetails-explicit.xml delete mode 100644 cas/src/test/resources/org/springframework/security/cas/web/authentication/defaultserviceauthenticationdetails-passivity.xml delete mode 100644 docs/modules/ROOT/pages/servlet/authentication/cas.adoc diff --git a/cas/spring-security-cas.gradle b/cas/spring-security-cas.gradle deleted file mode 100644 index f340ec7dda..0000000000 --- a/cas/spring-security-cas.gradle +++ /dev/null @@ -1,25 +0,0 @@ -apply plugin: 'io.spring.convention.spring-module' - -dependencies { - management platform(project(":spring-security-dependencies")) - api project(':spring-security-core') - api project(':spring-security-web') - api 'org.jasig.cas.client:cas-client-core' - api 'org.springframework:spring-beans' - api 'org.springframework:spring-context' - api 'org.springframework:spring-core' - api 'org.springframework:spring-web' - - optional 'com.fasterxml.jackson.core:jackson-databind' - - provided 'jakarta.servlet:jakarta.servlet-api' - - testImplementation "org.assertj:assertj-core" - testImplementation "org.junit.jupiter:junit-jupiter-api" - testImplementation "org.junit.jupiter:junit-jupiter-params" - testImplementation "org.junit.jupiter:junit-jupiter-engine" - testImplementation "org.mockito:mockito-core" - testImplementation "org.mockito:mockito-junit-jupiter" - testImplementation "org.springframework:spring-test" - testImplementation 'org.skyscreamer:jsonassert' -} diff --git a/cas/src/main/java/org/springframework/security/cas/SamlServiceProperties.java b/cas/src/main/java/org/springframework/security/cas/SamlServiceProperties.java deleted file mode 100644 index 8e300f8f8e..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/SamlServiceProperties.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas; - -/** - * Sets the appropriate parameters for CAS's implementation of SAML (which is not - * guaranteed to be actually SAML compliant). - * - * @author Scott Battaglia - * @since 3.0 - */ -public final class SamlServiceProperties extends ServiceProperties { - - public static final String DEFAULT_SAML_ARTIFACT_PARAMETER = "SAMLart"; - - public static final String DEFAULT_SAML_SERVICE_PARAMETER = "TARGET"; - - public SamlServiceProperties() { - super.setArtifactParameter(DEFAULT_SAML_ARTIFACT_PARAMETER); - super.setServiceParameter(DEFAULT_SAML_SERVICE_PARAMETER); - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/ServiceProperties.java b/cas/src/main/java/org/springframework/security/cas/ServiceProperties.java deleted file mode 100644 index caf03dd62a..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/ServiceProperties.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas; - -import org.springframework.beans.factory.InitializingBean; -import org.springframework.util.Assert; - -/** - * Stores properties related to this CAS service. - *

- * Each web application capable of processing CAS tickets is known as a service. This - * class stores the properties that are relevant to the local CAS service, being the - * application that is being secured by Spring Security. - * - * @author Ben Alex - */ -public class ServiceProperties implements InitializingBean { - - public static final String DEFAULT_CAS_ARTIFACT_PARAMETER = "ticket"; - - public static final String DEFAULT_CAS_SERVICE_PARAMETER = "service"; - - private String service; - - private boolean authenticateAllArtifacts; - - private boolean sendRenew = false; - - private String artifactParameter = DEFAULT_CAS_ARTIFACT_PARAMETER; - - private String serviceParameter = DEFAULT_CAS_SERVICE_PARAMETER; - - @Override - public void afterPropertiesSet() { - Assert.hasLength(this.service, "service cannot be empty."); - Assert.hasLength(this.artifactParameter, "artifactParameter cannot be empty."); - Assert.hasLength(this.serviceParameter, "serviceParameter cannot be empty."); - } - - /** - * Represents the service the user is authenticating to. - *

- * This service is the callback URL belonging to the local Spring Security System for - * Spring secured application. For example, - * - *

-	 * https://www.mycompany.com/application/login/cas
-	 * 
- * @return the URL of the service the user is authenticating to - */ - public final String getService() { - return this.service; - } - - /** - * Indicates whether the renew parameter should be sent to the CAS login - * URL and CAS validation URL. - *

- * If true, it will force CAS to authenticate the user again (even if the - * user has previously authenticated). During ticket validation it will require the - * ticket was generated as a consequence of an explicit login. High security - * applications would probably set this to true. Defaults to - * false, providing automated single sign on. - * @return whether to send the renew parameter to CAS - */ - public final boolean isSendRenew() { - return this.sendRenew; - } - - public final void setSendRenew(final boolean sendRenew) { - this.sendRenew = sendRenew; - } - - public final void setService(final String service) { - this.service = service; - } - - public final String getArtifactParameter() { - return this.artifactParameter; - } - - /** - * Configures the Request Parameter to look for when attempting to see if a CAS ticket - * was sent from the server. - * @param artifactParameter the id to use. Default is "ticket". - */ - public final void setArtifactParameter(final String artifactParameter) { - this.artifactParameter = artifactParameter; - } - - /** - * Configures the Request parameter to look for when attempting to send a request to - * CAS. - * @return the service parameter to use. Default is "service". - */ - public final String getServiceParameter() { - return this.serviceParameter; - } - - public final void setServiceParameter(final String serviceParameter) { - this.serviceParameter = serviceParameter; - } - - public final boolean isAuthenticateAllArtifacts() { - return this.authenticateAllArtifacts; - } - - /** - * If true, then any non-null artifact (ticket) should be authenticated. Additionally, - * the service will be determined dynamically in order to ensure the service matches - * the expected value for this artifact. - * @param authenticateAllArtifacts - */ - public final void setAuthenticateAllArtifacts(final boolean authenticateAllArtifacts) { - this.authenticateAllArtifacts = authenticateAllArtifacts; - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/authentication/CasAssertionAuthenticationToken.java b/cas/src/main/java/org/springframework/security/cas/authentication/CasAssertionAuthenticationToken.java deleted file mode 100644 index d04d30d154..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/authentication/CasAssertionAuthenticationToken.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.authentication; - -import java.util.ArrayList; - -import org.jasig.cas.client.validation.Assertion; - -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.core.SpringSecurityCoreVersion; - -/** - * Temporary authentication object needed to load the user details service. - * - * @author Scott Battaglia - * @since 3.0 - */ -public final class CasAssertionAuthenticationToken extends AbstractAuthenticationToken { - - private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; - - private final Assertion assertion; - - private final String ticket; - - public CasAssertionAuthenticationToken(final Assertion assertion, final String ticket) { - super(new ArrayList<>()); - this.assertion = assertion; - this.ticket = ticket; - } - - @Override - public Object getPrincipal() { - return this.assertion.getPrincipal().getName(); - } - - @Override - public Object getCredentials() { - return this.ticket; - } - - public Assertion getAssertion() { - return this.assertion; - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java b/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java deleted file mode 100644 index 3a84c2109a..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.authentication; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.jasig.cas.client.validation.Assertion; -import org.jasig.cas.client.validation.TicketValidationException; -import org.jasig.cas.client.validation.TicketValidator; - -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.MessageSource; -import org.springframework.context.MessageSourceAware; -import org.springframework.context.support.MessageSourceAccessor; -import org.springframework.core.log.LogMessage; -import org.springframework.security.authentication.AccountStatusUserDetailsChecker; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.cas.ServiceProperties; -import org.springframework.security.cas.web.CasAuthenticationFilter; -import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.SpringSecurityMessageSource; -import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; -import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper; -import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper; -import org.springframework.security.core.userdetails.UserDetailsChecker; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.util.Assert; - -/** - * An {@link AuthenticationProvider} implementation that integrates with JA-SIG Central - * Authentication Service (CAS). - *

- * This AuthenticationProvider is capable of validating - * {@link UsernamePasswordAuthenticationToken} requests which contain a - * principal name equal to either - * {@link CasAuthenticationFilter#CAS_STATEFUL_IDENTIFIER} or - * {@link CasAuthenticationFilter#CAS_STATELESS_IDENTIFIER}. It can also validate a - * previously created {@link CasAuthenticationToken}. - * - * @author Ben Alex - * @author Scott Battaglia - */ -public class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware { - - private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class); - - private AuthenticationUserDetailsService authenticationUserDetailsService; - - private final UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker(); - - protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); - - private StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache(); - - private String key; - - private TicketValidator ticketValidator; - - private ServiceProperties serviceProperties; - - private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper(); - - @Override - public void afterPropertiesSet() { - Assert.notNull(this.authenticationUserDetailsService, "An authenticationUserDetailsService must be set"); - Assert.notNull(this.ticketValidator, "A ticketValidator must be set"); - Assert.notNull(this.statelessTicketCache, "A statelessTicketCache must be set"); - Assert.hasText(this.key, - "A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated"); - Assert.notNull(this.messages, "A message source must be set"); - } - - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - if (!supports(authentication.getClass())) { - return null; - } - if (authentication instanceof UsernamePasswordAuthenticationToken - && (!CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER.equals(authentication.getPrincipal().toString()) - && !CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER - .equals(authentication.getPrincipal().toString()))) { - // UsernamePasswordAuthenticationToken not CAS related - return null; - } - // If an existing CasAuthenticationToken, just check we created it - if (authentication instanceof CasAuthenticationToken) { - if (this.key.hashCode() != ((CasAuthenticationToken) authentication).getKeyHash()) { - throw new BadCredentialsException(this.messages.getMessage("CasAuthenticationProvider.incorrectKey", - "The presented CasAuthenticationToken does not contain the expected key")); - } - return authentication; - } - - // Ensure credentials are presented - if ((authentication.getCredentials() == null) || "".equals(authentication.getCredentials())) { - throw new BadCredentialsException(this.messages.getMessage("CasAuthenticationProvider.noServiceTicket", - "Failed to provide a CAS service ticket to validate")); - } - - boolean stateless = (authentication instanceof UsernamePasswordAuthenticationToken - && CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER.equals(authentication.getPrincipal())); - CasAuthenticationToken result = null; - - if (stateless) { - // Try to obtain from cache - result = this.statelessTicketCache.getByTicketId(authentication.getCredentials().toString()); - } - if (result == null) { - result = this.authenticateNow(authentication); - result.setDetails(authentication.getDetails()); - } - if (stateless) { - // Add to cache - this.statelessTicketCache.putTicketInCache(result); - } - return result; - } - - private CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException { - try { - Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(), - getServiceUrl(authentication)); - UserDetails userDetails = loadUserByAssertion(assertion); - this.userDetailsChecker.check(userDetails); - return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(), - this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, assertion); - } - catch (TicketValidationException ex) { - throw new BadCredentialsException(ex.getMessage(), ex); - } - } - - /** - * Gets the serviceUrl. If the {@link Authentication#getDetails()} is an instance of - * {@link ServiceAuthenticationDetails}, then - * {@link ServiceAuthenticationDetails#getServiceUrl()} is used. Otherwise, the - * {@link ServiceProperties#getService()} is used. - * @param authentication - * @return - */ - private String getServiceUrl(Authentication authentication) { - String serviceUrl; - if (authentication.getDetails() instanceof ServiceAuthenticationDetails) { - return ((ServiceAuthenticationDetails) authentication.getDetails()).getServiceUrl(); - } - Assert.state(this.serviceProperties != null, - "serviceProperties cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails."); - Assert.state(this.serviceProperties.getService() != null, - "serviceProperties.getService() cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails."); - serviceUrl = this.serviceProperties.getService(); - logger.debug(LogMessage.format("serviceUrl = %s", serviceUrl)); - return serviceUrl; - } - - /** - * Template method for retrieving the UserDetails based on the assertion. Default is - * to call configured userDetailsService and pass the username. Deployers can override - * this method and retrieve the user based on any criteria they desire. - * @param assertion The CAS Assertion. - * @return the UserDetails. - */ - protected UserDetails loadUserByAssertion(final Assertion assertion) { - final CasAssertionAuthenticationToken token = new CasAssertionAuthenticationToken(assertion, ""); - return this.authenticationUserDetailsService.loadUserDetails(token); - } - - @SuppressWarnings("unchecked") - /** - * Sets the UserDetailsService to use. This is a convenience method to invoke - */ - public void setUserDetailsService(final UserDetailsService userDetailsService) { - this.authenticationUserDetailsService = new UserDetailsByNameServiceWrapper(userDetailsService); - } - - public void setAuthenticationUserDetailsService( - final AuthenticationUserDetailsService authenticationUserDetailsService) { - this.authenticationUserDetailsService = authenticationUserDetailsService; - } - - public void setServiceProperties(final ServiceProperties serviceProperties) { - this.serviceProperties = serviceProperties; - } - - protected String getKey() { - return this.key; - } - - public void setKey(String key) { - this.key = key; - } - - public StatelessTicketCache getStatelessTicketCache() { - return this.statelessTicketCache; - } - - protected TicketValidator getTicketValidator() { - return this.ticketValidator; - } - - @Override - public void setMessageSource(final MessageSource messageSource) { - this.messages = new MessageSourceAccessor(messageSource); - } - - public void setStatelessTicketCache(final StatelessTicketCache statelessTicketCache) { - this.statelessTicketCache = statelessTicketCache; - } - - public void setTicketValidator(final TicketValidator ticketValidator) { - this.ticketValidator = ticketValidator; - } - - public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) { - this.authoritiesMapper = authoritiesMapper; - } - - @Override - public boolean supports(final Class authentication) { - return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)) - || (CasAuthenticationToken.class.isAssignableFrom(authentication)) - || (CasAssertionAuthenticationToken.class.isAssignableFrom(authentication)); - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationToken.java b/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationToken.java deleted file mode 100644 index 8020da0400..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationToken.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.authentication; - -import java.io.Serializable; -import java.util.Collection; - -import org.jasig.cas.client.validation.Assertion; - -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.SpringSecurityCoreVersion; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; - -/** - * Represents a successful CAS Authentication. - * - * @author Ben Alex - * @author Scott Battaglia - */ -public class CasAuthenticationToken extends AbstractAuthenticationToken implements Serializable { - - private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; - - private final Object credentials; - - private final Object principal; - - private final UserDetails userDetails; - - private final int keyHash; - - private final Assertion assertion; - - /** - * Constructor. - * @param key to identify if this object made by a given - * {@link CasAuthenticationProvider} - * @param principal typically the UserDetails object (cannot be null) - * @param credentials the service/proxy ticket ID from CAS (cannot be - * null) - * @param authorities the authorities granted to the user (from the - * {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot - * be null) - * @param userDetails the user details (from the - * {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot - * be null) - * @param assertion the assertion returned from the CAS servers. It contains the - * principal and how to obtain a proxy ticket for the user. - * @throws IllegalArgumentException if a null was passed - */ - public CasAuthenticationToken(final String key, final Object principal, final Object credentials, - final Collection authorities, final UserDetails userDetails, - final Assertion assertion) { - this(extractKeyHash(key), principal, credentials, authorities, userDetails, assertion); - } - - /** - * Private constructor for Jackson Deserialization support - * @param keyHash hashCode of provided key to identify if this object made by a given - * {@link CasAuthenticationProvider} - * @param principal typically the UserDetails object (cannot be null) - * @param credentials the service/proxy ticket ID from CAS (cannot be - * null) - * @param authorities the authorities granted to the user (from the - * {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot - * be null) - * @param userDetails the user details (from the - * {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot - * be null) - * @param assertion the assertion returned from the CAS servers. It contains the - * principal and how to obtain a proxy ticket for the user. - * @throws IllegalArgumentException if a null was passed - * @since 4.2 - */ - private CasAuthenticationToken(final Integer keyHash, final Object principal, final Object credentials, - final Collection authorities, final UserDetails userDetails, - final Assertion assertion) { - super(authorities); - if ((principal == null) || "".equals(principal) || (credentials == null) || "".equals(credentials) - || (authorities == null) || (userDetails == null) || (assertion == null)) { - throw new IllegalArgumentException("Cannot pass null or empty values to constructor"); - } - this.keyHash = keyHash; - this.principal = principal; - this.credentials = credentials; - this.userDetails = userDetails; - this.assertion = assertion; - setAuthenticated(true); - } - - private static Integer extractKeyHash(String key) { - Assert.hasLength(key, "key cannot be null or empty"); - return key.hashCode(); - } - - @Override - public boolean equals(final Object obj) { - if (!super.equals(obj)) { - return false; - } - if (obj instanceof CasAuthenticationToken) { - CasAuthenticationToken test = (CasAuthenticationToken) obj; - if (!this.assertion.equals(test.getAssertion())) { - return false; - } - if (this.getKeyHash() != test.getKeyHash()) { - return false; - } - return true; - } - return false; - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + this.credentials.hashCode(); - result = 31 * result + this.principal.hashCode(); - result = 31 * result + this.userDetails.hashCode(); - result = 31 * result + this.keyHash; - result = 31 * result + ObjectUtils.nullSafeHashCode(this.assertion); - return result; - } - - @Override - public Object getCredentials() { - return this.credentials; - } - - public int getKeyHash() { - return this.keyHash; - } - - @Override - public Object getPrincipal() { - return this.principal; - } - - public Assertion getAssertion() { - return this.assertion; - } - - public UserDetails getUserDetails() { - return this.userDetails; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(super.toString()); - sb.append(" Assertion: ").append(this.assertion); - sb.append(" Credentials (Service/Proxy Ticket): ").append(this.credentials); - return (sb.toString()); - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/authentication/NullStatelessTicketCache.java b/cas/src/main/java/org/springframework/security/cas/authentication/NullStatelessTicketCache.java deleted file mode 100644 index 4284161a39..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/authentication/NullStatelessTicketCache.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.authentication; - -/** - * Implementation of @link {@link StatelessTicketCache} that has no backing cache. Useful - * in instances where storing of tickets for stateless session management is not required. - *

- * This is the default StatelessTicketCache of the @link {@link CasAuthenticationProvider} - * to eliminate the unnecessary dependency on EhCache that applications have even if they - * are not using the stateless session management. - * - * @author Scott Battaglia - * @see CasAuthenticationProvider - */ -public final class NullStatelessTicketCache implements StatelessTicketCache { - - /** - * @return null since we are not storing any tickets. - */ - @Override - public CasAuthenticationToken getByTicketId(final String serviceTicket) { - return null; - } - - /** - * This is a no-op since we are not storing tickets. - */ - @Override - public void putTicketInCache(final CasAuthenticationToken token) { - // nothing to do - } - - /** - * This is a no-op since we are not storing tickets. - */ - @Override - public void removeTicketFromCache(final CasAuthenticationToken token) { - // nothing to do - } - - /** - * This is a no-op since we are not storing tickets. - */ - @Override - public void removeTicketFromCache(final String serviceTicket) { - // nothing to do - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/authentication/SpringCacheBasedTicketCache.java b/cas/src/main/java/org/springframework/security/cas/authentication/SpringCacheBasedTicketCache.java deleted file mode 100644 index b72e824c75..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/authentication/SpringCacheBasedTicketCache.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.authentication; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cache.Cache; -import org.springframework.core.log.LogMessage; -import org.springframework.util.Assert; - -/** - * Caches tickets using a Spring IoC defined {@link Cache}. - * - * @author Marten Deinum - * @since 3.2 - * - */ -public class SpringCacheBasedTicketCache implements StatelessTicketCache { - - private static final Log logger = LogFactory.getLog(SpringCacheBasedTicketCache.class); - - private final Cache cache; - - public SpringCacheBasedTicketCache(Cache cache) { - Assert.notNull(cache, "cache mandatory"); - this.cache = cache; - } - - @Override - public CasAuthenticationToken getByTicketId(final String serviceTicket) { - final Cache.ValueWrapper element = (serviceTicket != null) ? this.cache.get(serviceTicket) : null; - logger.debug(LogMessage.of(() -> "Cache hit: " + (element != null) + "; service ticket: " + serviceTicket)); - return (element != null) ? (CasAuthenticationToken) element.get() : null; - } - - @Override - public void putTicketInCache(final CasAuthenticationToken token) { - String key = token.getCredentials().toString(); - logger.debug(LogMessage.of(() -> "Cache put: " + key)); - this.cache.put(key, token); - } - - @Override - public void removeTicketFromCache(final CasAuthenticationToken token) { - logger.debug(LogMessage.of(() -> "Cache remove: " + token.getCredentials().toString())); - this.removeTicketFromCache(token.getCredentials().toString()); - } - - @Override - public void removeTicketFromCache(final String serviceTicket) { - this.cache.evict(serviceTicket); - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/authentication/StatelessTicketCache.java b/cas/src/main/java/org/springframework/security/cas/authentication/StatelessTicketCache.java deleted file mode 100644 index 74df6bb9df..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/authentication/StatelessTicketCache.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2004 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.authentication; - -/** - * Caches CAS service tickets and CAS proxy tickets for stateless connections. - * - *

- * When a service ticket or proxy ticket is validated against the CAS server, it is unable - * to be used again. Most types of callers are stateful and are associated with a given - * HttpSession. This allows the affirmative CAS validation outcome to be - * stored in the HttpSession, meaning the removal of the ticket from the CAS - * server is not an issue. - *

- * - *

- * Stateless callers, such as remoting protocols, cannot take advantage of - * HttpSession. If the stateless caller is located a significant network - * distance from the CAS server, acquiring a fresh service ticket or proxy ticket for each - * invocation would be expensive. - *

- * - *

- * To avoid this issue with stateless callers, it is expected stateless callers will - * obtain a single service ticket or proxy ticket, and then present this same ticket to - * the Spring Security secured application on each occasion. As no - * HttpSession is available for such callers, the affirmative CAS validation - * outcome cannot be stored in this location. - *

- * - *

- * The StatelessTicketCache enables the service tickets and proxy tickets - * belonging to stateless callers to be placed in a cache. This in-memory cache stores the - * CasAuthenticationToken, effectively providing the same capability as a - * HttpSession with the ticket identifier being the key rather than a session - * identifier. - *

- * - *

- * Implementations should provide a reasonable timeout on stored entries, such that the - * stateless caller are not required to unnecessarily acquire fresh CAS service tickets or - * proxy tickets. - *

- * - * @author Ben Alex - */ -public interface StatelessTicketCache { - - /** - * Retrieves the CasAuthenticationToken associated with the specified - * ticket. - * - *

- * If not found, returns a nullCasAuthenticationToken. - *

- * @return the fully populated authentication token - */ - CasAuthenticationToken getByTicketId(String serviceTicket); - - /** - * Adds the specified CasAuthenticationToken to the cache. - * - *

- * The {@link CasAuthenticationToken#getCredentials()} method is used to retrieve the - * service ticket number. - *

- * @param token to be added to the cache - */ - void putTicketInCache(CasAuthenticationToken token); - - /** - * Removes the specified ticket from the cache, as per - * {@link #removeTicketFromCache(String)}. - * - *

- * Implementations should use {@link CasAuthenticationToken#getCredentials()} to - * obtain the ticket and then delegate to the {@link #removeTicketFromCache(String)} - * method. - *

- * @param token to be removed - */ - void removeTicketFromCache(CasAuthenticationToken token); - - /** - * Removes the specified ticket from the cache, meaning that future calls will require - * a new service ticket. - * - *

- * This is in case applications wish to provide a session termination capability for - * their stateless clients. - *

- * @param serviceTicket to be removed - */ - void removeTicketFromCache(String serviceTicket); - -} diff --git a/cas/src/main/java/org/springframework/security/cas/authentication/package-info.java b/cas/src/main/java/org/springframework/security/cas/authentication/package-info.java deleted file mode 100644 index 8803500a0a..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/authentication/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * An {@code AuthenticationProvider} that can process CAS service tickets and proxy - * tickets. - */ -package org.springframework.security.cas.authentication; diff --git a/cas/src/main/java/org/springframework/security/cas/jackson2/AssertionImplMixin.java b/cas/src/main/java/org/springframework/security/cas/jackson2/AssertionImplMixin.java deleted file mode 100644 index f3d7de8c02..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/jackson2/AssertionImplMixin.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2015-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.jackson2; - -import java.util.Date; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import org.jasig.cas.client.authentication.AttributePrincipal; - -/** - * Helps in jackson deserialization of class - * {@link org.jasig.cas.client.validation.AssertionImpl}, which is used with - * {@link org.springframework.security.cas.authentication.CasAuthenticationToken}. To use - * this class we need to register with - * {@link com.fasterxml.jackson.databind.ObjectMapper}. Type information will be stored - * in @class property. - *

- *

- *     ObjectMapper mapper = new ObjectMapper();
- *     mapper.registerModule(new CasJackson2Module());
- * 
- * - * @author Jitendra Singh - * @since 4.2 - * @see CasJackson2Module - * @see org.springframework.security.jackson2.SecurityJackson2Modules - */ -@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) -@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, - isGetterVisibility = JsonAutoDetect.Visibility.NONE) -@JsonIgnoreProperties(ignoreUnknown = true) -class AssertionImplMixin { - - /** - * Mixin Constructor helps in deserialize - * {@link org.jasig.cas.client.validation.AssertionImpl} - * @param principal the Principal to associate with the Assertion. - * @param validFromDate when the assertion is valid from. - * @param validUntilDate when the assertion is valid to. - * @param authenticationDate when the assertion is authenticated. - * @param attributes the key/value pairs for this attribute. - */ - @JsonCreator - AssertionImplMixin(@JsonProperty("principal") AttributePrincipal principal, - @JsonProperty("validFromDate") Date validFromDate, @JsonProperty("validUntilDate") Date validUntilDate, - @JsonProperty("authenticationDate") Date authenticationDate, - @JsonProperty("attributes") Map attributes) { - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/jackson2/AttributePrincipalImplMixin.java b/cas/src/main/java/org/springframework/security/cas/jackson2/AttributePrincipalImplMixin.java deleted file mode 100644 index 0ec671fb55..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/jackson2/AttributePrincipalImplMixin.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2015-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.jackson2; - -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import org.jasig.cas.client.proxy.ProxyRetriever; - -/** - * Helps in deserialize {@link org.jasig.cas.client.authentication.AttributePrincipalImpl} - * which is used with - * {@link org.springframework.security.cas.authentication.CasAuthenticationToken}. Type - * information will be stored in property named @class. - *

- *

- *     ObjectMapper mapper = new ObjectMapper();
- *     mapper.registerModule(new CasJackson2Module());
- * 
- * - * @author Jitendra Singh - * @since 4.2 - * @see CasJackson2Module - * @see org.springframework.security.jackson2.SecurityJackson2Modules - */ -@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) -@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, - isGetterVisibility = JsonAutoDetect.Visibility.NONE) -@JsonIgnoreProperties(ignoreUnknown = true) -class AttributePrincipalImplMixin { - - /** - * Mixin Constructor helps in deserialize - * {@link org.jasig.cas.client.authentication.AttributePrincipalImpl} - * @param name the unique identifier for the principal. - * @param attributes the key/value pairs for this principal. - * @param proxyGrantingTicket the ticket associated with this principal. - * @param proxyRetriever the ProxyRetriever implementation to call back to the CAS - * server. - */ - @JsonCreator - AttributePrincipalImplMixin(@JsonProperty("name") String name, - @JsonProperty("attributes") Map attributes, - @JsonProperty("proxyGrantingTicket") String proxyGrantingTicket, - @JsonProperty("proxyRetriever") ProxyRetriever proxyRetriever) { - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/jackson2/CasAuthenticationTokenMixin.java b/cas/src/main/java/org/springframework/security/cas/jackson2/CasAuthenticationTokenMixin.java deleted file mode 100644 index 80e40a0dca..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/jackson2/CasAuthenticationTokenMixin.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2015-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.jackson2; - -import java.util.Collection; - -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import org.jasig.cas.client.validation.Assertion; - -import org.springframework.security.cas.authentication.CasAuthenticationProvider; -import org.springframework.security.cas.authentication.CasAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; - -/** - * Mixin class which helps in deserialize - * {@link org.springframework.security.cas.authentication.CasAuthenticationToken} using - * jackson. Two more dependent classes needs to register along with this mixin class. - *
    - *
  1. {@link org.springframework.security.cas.jackson2.AssertionImplMixin}
  2. - *
  3. {@link org.springframework.security.cas.jackson2.AttributePrincipalImplMixin}
  4. - *
- * - *

- * - *

- *     ObjectMapper mapper = new ObjectMapper();
- *     mapper.registerModule(new CasJackson2Module());
- * 
- * - * @author Jitendra Singh - * @since 4.2 - * @see CasJackson2Module - * @see org.springframework.security.jackson2.SecurityJackson2Modules - */ -@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) -@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE, - getterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY) -@JsonIgnoreProperties(ignoreUnknown = true) -class CasAuthenticationTokenMixin { - - /** - * Mixin Constructor helps in deserialize {@link CasAuthenticationToken} - * @param keyHash hashCode of provided key to identify if this object made by a given - * {@link CasAuthenticationProvider} - * @param principal typically the UserDetails object (cannot be null) - * @param credentials the service/proxy ticket ID from CAS (cannot be - * null) - * @param authorities the authorities granted to the user (from the - * {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot - * be null) - * @param userDetails the user details (from the - * {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot - * be null) - * @param assertion the assertion returned from the CAS servers. It contains the - * principal and how to obtain a proxy ticket for the user. - */ - @JsonCreator - CasAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash, @JsonProperty("principal") Object principal, - @JsonProperty("credentials") Object credentials, - @JsonProperty("authorities") Collection authorities, - @JsonProperty("userDetails") UserDetails userDetails, @JsonProperty("assertion") Assertion assertion) { - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/jackson2/CasJackson2Module.java b/cas/src/main/java/org/springframework/security/cas/jackson2/CasJackson2Module.java deleted file mode 100644 index 34f19ca10a..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/jackson2/CasJackson2Module.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2015-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.jackson2; - -import com.fasterxml.jackson.core.Version; -import com.fasterxml.jackson.databind.module.SimpleModule; -import org.jasig.cas.client.authentication.AttributePrincipalImpl; -import org.jasig.cas.client.validation.AssertionImpl; - -import org.springframework.security.cas.authentication.CasAuthenticationToken; -import org.springframework.security.jackson2.SecurityJackson2Modules; - -/** - * Jackson module for spring-security-cas. This module register - * {@link AssertionImplMixin}, {@link AttributePrincipalImplMixin} and - * {@link CasAuthenticationTokenMixin}. If no default typing enabled by default then it'll - * enable it because typing info is needed to properly serialize/deserialize objects. In - * order to use this module just add this module into your ObjectMapper configuration. - * - *
- *     ObjectMapper mapper = new ObjectMapper();
- *     mapper.registerModule(new CasJackson2Module());
- * 
Note: use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get list - * of all security modules on the classpath. - * - * @author Jitendra Singh. - * @since 4.2 - * @see org.springframework.security.jackson2.SecurityJackson2Modules - */ -public class CasJackson2Module extends SimpleModule { - - public CasJackson2Module() { - super(CasJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null)); - } - - @Override - public void setupModule(SetupContext context) { - SecurityJackson2Modules.enableDefaultTyping(context.getOwner()); - context.setMixInAnnotations(AssertionImpl.class, AssertionImplMixin.class); - context.setMixInAnnotations(AttributePrincipalImpl.class, AttributePrincipalImplMixin.class); - context.setMixInAnnotations(CasAuthenticationToken.class, CasAuthenticationTokenMixin.class); - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/package-info.java b/cas/src/main/java/org/springframework/security/cas/package-info.java deleted file mode 100644 index 13fae9057d..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Spring Security support for Jasig's Central Authentication Service - * (CAS). - */ -package org.springframework.security.cas; diff --git a/cas/src/main/java/org/springframework/security/cas/userdetails/AbstractCasAssertionUserDetailsService.java b/cas/src/main/java/org/springframework/security/cas/userdetails/AbstractCasAssertionUserDetailsService.java deleted file mode 100644 index 3d8cd9e412..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/userdetails/AbstractCasAssertionUserDetailsService.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.userdetails; - -import org.jasig.cas.client.validation.Assertion; - -import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken; -import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; -import org.springframework.security.core.userdetails.UserDetails; - -/** - * Abstract class for using the provided CAS assertion to construct a new User object. - * This generally is most useful when combined with a SAML-based response from the CAS - * Server/client. - * - * @author Scott Battaglia - * @since 3.0 - */ -public abstract class AbstractCasAssertionUserDetailsService - implements AuthenticationUserDetailsService { - - @Override - public final UserDetails loadUserDetails(final CasAssertionAuthenticationToken token) { - return loadUserDetails(token.getAssertion()); - } - - /** - * Protected template method for construct a - * {@link org.springframework.security.core.userdetails.UserDetails} via the supplied - * CAS assertion. - * @param assertion the assertion to use to construct the new UserDetails. CANNOT be - * NULL. - * @return the newly constructed UserDetails. - */ - protected abstract UserDetails loadUserDetails(Assertion assertion); - -} diff --git a/cas/src/main/java/org/springframework/security/cas/userdetails/GrantedAuthorityFromAssertionAttributesUserDetailsService.java b/cas/src/main/java/org/springframework/security/cas/userdetails/GrantedAuthorityFromAssertionAttributesUserDetailsService.java deleted file mode 100644 index 0e47d1c57f..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/userdetails/GrantedAuthorityFromAssertionAttributesUserDetailsService.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.userdetails; - -import java.util.ArrayList; -import java.util.List; - -import org.jasig.cas.client.validation.Assertion; - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.util.Assert; - -/** - * Populates the {@link org.springframework.security.core.GrantedAuthority}s for a user by - * reading a list of attributes that were returned as part of the CAS response. Each - * attribute is read and each value of the attribute is turned into a GrantedAuthority. If - * the attribute has no value then its not added. - * - * @author Scott Battaglia - * @since 3.0 - */ -public final class GrantedAuthorityFromAssertionAttributesUserDetailsService - extends AbstractCasAssertionUserDetailsService { - - private static final String NON_EXISTENT_PASSWORD_VALUE = "NO_PASSWORD"; - - private final String[] attributes; - - private boolean convertToUpperCase = true; - - public GrantedAuthorityFromAssertionAttributesUserDetailsService(final String[] attributes) { - Assert.notNull(attributes, "attributes cannot be null."); - Assert.isTrue(attributes.length > 0, "At least one attribute is required to retrieve roles from."); - this.attributes = attributes; - } - - @SuppressWarnings("unchecked") - @Override - protected UserDetails loadUserDetails(final Assertion assertion) { - List grantedAuthorities = new ArrayList<>(); - for (String attribute : this.attributes) { - Object value = assertion.getPrincipal().getAttributes().get(attribute); - if (value != null) { - if (value instanceof List) { - for (Object o : (List) value) { - grantedAuthorities.add(createSimpleGrantedAuthority(o)); - } - } - else { - grantedAuthorities.add(createSimpleGrantedAuthority(value)); - } - } - } - return new User(assertion.getPrincipal().getName(), NON_EXISTENT_PASSWORD_VALUE, true, true, true, true, - grantedAuthorities); - } - - private SimpleGrantedAuthority createSimpleGrantedAuthority(Object o) { - return new SimpleGrantedAuthority(this.convertToUpperCase ? o.toString().toUpperCase() : o.toString()); - } - - /** - * Converts the returned attribute values to uppercase values. - * @param convertToUpperCase true if it should convert, false otherwise. - */ - public void setConvertToUpperCase(final boolean convertToUpperCase) { - this.convertToUpperCase = convertToUpperCase; - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationEntryPoint.java b/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationEntryPoint.java deleted file mode 100644 index 18ecb23623..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationEntryPoint.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.web; - -import java.io.IOException; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import org.jasig.cas.client.util.CommonUtils; - -import org.springframework.beans.factory.InitializingBean; -import org.springframework.security.cas.ServiceProperties; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.util.Assert; - -/** - * Used by the ExceptionTranslationFilter to commence authentication via the - * JA-SIG Central Authentication Service (CAS). - *

- * The user's browser will be redirected to the JA-SIG CAS enterprise-wide login page. - * This page is specified by the loginUrl property. Once login is complete, - * the CAS login page will redirect to the page indicated by the service - * property. The service is a HTTP URL belonging to the current application. - * The service URL is monitored by the {@link CasAuthenticationFilter}, which - * will validate the CAS login was successful. - * - * @author Ben Alex - * @author Scott Battaglia - */ -public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean { - - private ServiceProperties serviceProperties; - - private String loginUrl; - - /** - * Determines whether the Service URL should include the session id for the specific - * user. As of CAS 3.0.5, the session id will automatically be stripped. However, - * older versions of CAS (i.e. CAS 2), do not automatically strip the session - * identifier (this is a bug on the part of the older server implementations), so an - * option to disable the session encoding is provided for backwards compatibility. - * - * By default, encoding is enabled. - */ - private boolean encodeServiceUrlWithSessionId = true; - - @Override - public void afterPropertiesSet() { - Assert.hasLength(this.loginUrl, "loginUrl must be specified"); - Assert.notNull(this.serviceProperties, "serviceProperties must be specified"); - Assert.notNull(this.serviceProperties.getService(), "serviceProperties.getService() cannot be null."); - } - - @Override - public final void commence(final HttpServletRequest servletRequest, HttpServletResponse response, - AuthenticationException authenticationException) throws IOException { - String urlEncodedService = createServiceUrl(servletRequest, response); - String redirectUrl = createRedirectUrl(urlEncodedService); - preCommence(servletRequest, response); - response.sendRedirect(redirectUrl); - } - - /** - * Constructs a new Service Url. The default implementation relies on the CAS client - * to do the bulk of the work. - * @param request the HttpServletRequest - * @param response the HttpServlet Response - * @return the constructed service url. CANNOT be NULL. - */ - protected String createServiceUrl(HttpServletRequest request, HttpServletResponse response) { - return CommonUtils.constructServiceUrl(null, response, this.serviceProperties.getService(), null, - this.serviceProperties.getArtifactParameter(), this.encodeServiceUrlWithSessionId); - } - - /** - * Constructs the Url for Redirection to the CAS server. Default implementation relies - * on the CAS client to do the bulk of the work. - * @param serviceUrl the service url that should be included. - * @return the redirect url. CANNOT be NULL. - */ - protected String createRedirectUrl(String serviceUrl) { - return CommonUtils.constructRedirectUrl(this.loginUrl, this.serviceProperties.getServiceParameter(), serviceUrl, - this.serviceProperties.isSendRenew(), false); - } - - /** - * Template method for you to do your own pre-processing before the redirect occurs. - * @param request the HttpServletRequest - * @param response the HttpServletResponse - */ - protected void preCommence(HttpServletRequest request, HttpServletResponse response) { - - } - - /** - * The enterprise-wide CAS login URL. Usually something like - * https://www.mycompany.com/cas/login. - * @return the enterprise-wide CAS login URL - */ - public final String getLoginUrl() { - return this.loginUrl; - } - - public final ServiceProperties getServiceProperties() { - return this.serviceProperties; - } - - public final void setLoginUrl(String loginUrl) { - this.loginUrl = loginUrl; - } - - public final void setServiceProperties(ServiceProperties serviceProperties) { - this.serviceProperties = serviceProperties; - } - - /** - * Sets whether to encode the service url with the session id or not. - * @param encodeServiceUrlWithSessionId whether to encode the service url with the - * session id or not. - */ - public final void setEncodeServiceUrlWithSessionId(boolean encodeServiceUrlWithSessionId) { - this.encodeServiceUrlWithSessionId = encodeServiceUrlWithSessionId; - } - - /** - * Sets whether to encode the service url with the session id or not. - * @return whether to encode the service url with the session id or not. - * - */ - protected boolean getEncodeServiceUrlWithSessionId() { - return this.encodeServiceUrlWithSessionId; - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java b/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java deleted file mode 100644 index 8e8b84700f..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.web; - -import java.io.IOException; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; -import org.jasig.cas.client.util.CommonUtils; -import org.jasig.cas.client.validation.TicketValidator; - -import org.springframework.core.log.LogMessage; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.authentication.AuthenticationDetailsSource; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent; -import org.springframework.security.cas.ServiceProperties; -import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails; -import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; -import org.springframework.security.web.authentication.AuthenticationFailureHandler; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.util.Assert; - -/** - * Processes a CAS service ticket, obtains proxy granting tickets, and processes proxy - * tickets. - *

Service Tickets

- *

- * A service ticket consists of an opaque ticket string. It arrives at this filter by the - * user's browser successfully authenticating using CAS, and then receiving a HTTP - * redirect to a service. The opaque ticket string is presented in the - * ticket request parameter. - *

- * This filter monitors the service URL so it can receive the service ticket - * and process it. By default this filter processes the URL /login/cas. When - * processing this URL, the value of {@link ServiceProperties#getService()} is used as the - * service when validating the ticket. This means that it is - * important that {@link ServiceProperties#getService()} specifies the same value as the - * filterProcessesUrl. - *

- * Processing the service ticket involves creating a - * UsernamePasswordAuthenticationToken which uses - * {@link #CAS_STATEFUL_IDENTIFIER} for the principal and the opaque ticket - * string as the credentials. - *

Obtaining Proxy Granting Tickets

- *

- * If specified, the filter can also monitor the proxyReceptorUrl. The filter - * will respond to requests matching this url so that the CAS Server can provide a PGT to - * the filter. Note that in addition to the proxyReceptorUrl a non-null - * proxyGrantingTicketStorage must be provided in order for the filter to - * respond to proxy receptor requests. By configuring a shared - * {@link ProxyGrantingTicketStorage} between the {@link TicketValidator} and the - * CasAuthenticationFilter one can have the CasAuthenticationFilter handle the proxying - * requirements for CAS. - *

Proxy Tickets

- *

- * The filter can process tickets present on any url. This is useful when wanting to - * process proxy tickets. In order for proxy tickets to get processed - * {@link ServiceProperties#isAuthenticateAllArtifacts()} must return true. - * Additionally, if the request is already authenticated, authentication will not - * occur. Last, {@link AuthenticationDetailsSource#buildDetails(Object)} must return a - * {@link ServiceAuthenticationDetails}. This can be accomplished using the - * {@link ServiceAuthenticationDetailsSource}. In this case - * {@link ServiceAuthenticationDetails#getServiceUrl()} will be used for the service url. - *

- * Processing the proxy ticket involves creating a - * UsernamePasswordAuthenticationToken which uses - * {@link #CAS_STATELESS_IDENTIFIER} for the principal and the opaque ticket - * string as the credentials. When a proxy ticket is successfully - * authenticated, the FilterChain continues and the - * authenticationSuccessHandler is not used. - *

Notes about the AuthenticationManager

- *

- * The configured AuthenticationManager is expected to provide a provider - * that can recognise UsernamePasswordAuthenticationTokens containing this - * special principal name, and process them accordingly by validation with - * the CAS server. Additionally, it should be capable of using the result of - * {@link ServiceAuthenticationDetails#getServiceUrl()} as the service when validating the - * ticket. - *

Example Configuration

- *

- * An example configuration that supports service tickets, obtaining proxy granting - * tickets, and proxy tickets is illustrated below: - * - *

- * <b:bean id="serviceProperties"
- *     class="org.springframework.security.cas.ServiceProperties"
- *     p:service="https://service.example.com/cas-sample/login/cas"
- *     p:authenticateAllArtifacts="true"/>
- * <b:bean id="casEntryPoint"
- *     class="org.springframework.security.cas.web.CasAuthenticationEntryPoint"
- *     p:serviceProperties-ref="serviceProperties" p:loginUrl="https://login.example.org/cas/login" />
- * <b:bean id="casFilter"
- *     class="org.springframework.security.cas.web.CasAuthenticationFilter"
- *     p:authenticationManager-ref="authManager"
- *     p:serviceProperties-ref="serviceProperties"
- *     p:proxyGrantingTicketStorage-ref="pgtStorage"
- *     p:proxyReceptorUrl="/login/cas/proxyreceptor">
- *     <b:property name="authenticationDetailsSource">
- *         <b:bean class="org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource"/>
- *     </b:property>
- *     <b:property name="authenticationFailureHandler">
- *         <b:bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
- *             p:defaultFailureUrl="/casfailed.jsp"/>
- *     </b:property>
- * </b:bean>
- * <!--
- *     NOTE: In a real application you should not use an in memory implementation. You will also want
- *           to ensure to clean up expired tickets by calling ProxyGrantingTicketStorage.cleanup()
- *  -->
- * <b:bean id="pgtStorage" class="org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl"/>
- * <b:bean id="casAuthProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider"
- *     p:serviceProperties-ref="serviceProperties"
- *     p:key="casAuthProviderKey">
- *     <b:property name="authenticationUserDetailsService">
- *         <b:bean
- *             class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
- *             <b:constructor-arg ref="userService" />
- *         </b:bean>
- *     </b:property>
- *     <b:property name="ticketValidator">
- *         <b:bean
- *             class="org.jasig.cas.client.validation.Cas20ProxyTicketValidator"
- *             p:acceptAnyProxy="true"
- *             p:proxyCallbackUrl="https://service.example.com/cas-sample/login/cas/proxyreceptor"
- *             p:proxyGrantingTicketStorage-ref="pgtStorage">
- *             <b:constructor-arg value="https://login.example.org/cas" />
- *         </b:bean>
- *     </b:property>
- *     <b:property name="statelessTicketCache">
- *         <b:bean class="org.springframework.security.cas.authentication.EhCacheBasedTicketCache">
- *             <b:property name="cache">
- *                 <b:bean class="net.sf.ehcache.Cache"
- *                   init-method="initialise"
- *                   destroy-method="dispose">
- *                     <b:constructor-arg value="casTickets"/>
- *                     <b:constructor-arg value="50"/>
- *                     <b:constructor-arg value="true"/>
- *                     <b:constructor-arg value="false"/>
- *                     <b:constructor-arg value="3600"/>
- *                     <b:constructor-arg value="900"/>
- *                 </b:bean>
- *             </b:property>
- *         </b:bean>
- *     </b:property>
- * </b:bean>
- * 
- * - * @author Ben Alex - * @author Rob Winch - */ -public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFilter { - - /** - * Used to identify a CAS request for a stateful user agent, such as a web browser. - */ - public static final String CAS_STATEFUL_IDENTIFIER = "_cas_stateful_"; - - /** - * Used to identify a CAS request for a stateless user agent, such as a remoting - * protocol client (e.g. Hessian, Burlap, SOAP etc). Results in a more aggressive - * caching strategy being used, as the absence of a HttpSession will - * result in a new authentication attempt on every request. - */ - public static final String CAS_STATELESS_IDENTIFIER = "_cas_stateless_"; - - /** - * The last portion of the receptor url, i.e. /proxy/receptor - */ - private RequestMatcher proxyReceptorMatcher; - - /** - * The backing storage to store ProxyGrantingTicket requests. - */ - private ProxyGrantingTicketStorage proxyGrantingTicketStorage; - - private String artifactParameter = ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER; - - private boolean authenticateAllArtifacts; - - private AuthenticationFailureHandler proxyFailureHandler = new SimpleUrlAuthenticationFailureHandler(); - - public CasAuthenticationFilter() { - super("/login/cas"); - setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler()); - } - - @Override - protected final void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, - FilterChain chain, Authentication authResult) throws IOException, ServletException { - boolean continueFilterChain = proxyTicketRequest(serviceTicketRequest(request, response), request); - if (!continueFilterChain) { - super.successfulAuthentication(request, response, chain, authResult); - return; - } - this.logger.debug( - LogMessage.format("Authentication success. Updating SecurityContextHolder to contain: %s", authResult)); - SecurityContext context = SecurityContextHolder.createEmptyContext(); - context.setAuthentication(authResult); - SecurityContextHolder.setContext(context); - if (this.eventPublisher != null) { - this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass())); - } - chain.doFilter(request, response); - } - - @Override - public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) - throws AuthenticationException, IOException { - // if the request is a proxy request process it and return null to indicate the - // request has been processed - if (proxyReceptorRequest(request)) { - this.logger.debug("Responding to proxy receptor request"); - CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage); - return null; - } - boolean serviceTicketRequest = serviceTicketRequest(request, response); - String username = serviceTicketRequest ? CAS_STATEFUL_IDENTIFIER : CAS_STATELESS_IDENTIFIER; - String password = obtainArtifact(request); - if (password == null) { - this.logger.debug("Failed to obtain an artifact (cas ticket)"); - password = ""; - } - UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); - authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); - return this.getAuthenticationManager().authenticate(authRequest); - } - - /** - * If present, gets the artifact (CAS ticket) from the {@link HttpServletRequest}. - * @param request - * @return if present the artifact from the {@link HttpServletRequest}, else null - */ - protected String obtainArtifact(HttpServletRequest request) { - return request.getParameter(this.artifactParameter); - } - - /** - * Overridden to provide proxying capabilities. - */ - @Override - protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) { - final boolean serviceTicketRequest = serviceTicketRequest(request, response); - final boolean result = serviceTicketRequest || proxyReceptorRequest(request) - || (proxyTicketRequest(serviceTicketRequest, request)); - if (this.logger.isDebugEnabled()) { - this.logger.debug("requiresAuthentication = " + result); - } - return result; - } - - /** - * Sets the {@link AuthenticationFailureHandler} for proxy requests. - * @param proxyFailureHandler - */ - public final void setProxyAuthenticationFailureHandler(AuthenticationFailureHandler proxyFailureHandler) { - Assert.notNull(proxyFailureHandler, "proxyFailureHandler cannot be null"); - this.proxyFailureHandler = proxyFailureHandler; - } - - /** - * Wraps the {@link AuthenticationFailureHandler} to distinguish between handling - * proxy ticket authentication failures and service ticket failures. - */ - @Override - public final void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) { - super.setAuthenticationFailureHandler(new CasAuthenticationFailureHandler(failureHandler)); - } - - public final void setProxyReceptorUrl(final String proxyReceptorUrl) { - this.proxyReceptorMatcher = new AntPathRequestMatcher("/**" + proxyReceptorUrl); - } - - public final void setProxyGrantingTicketStorage(final ProxyGrantingTicketStorage proxyGrantingTicketStorage) { - this.proxyGrantingTicketStorage = proxyGrantingTicketStorage; - } - - public final void setServiceProperties(final ServiceProperties serviceProperties) { - this.artifactParameter = serviceProperties.getArtifactParameter(); - this.authenticateAllArtifacts = serviceProperties.isAuthenticateAllArtifacts(); - } - - /** - * Indicates if the request is elgible to process a service ticket. This method exists - * for readability. - * @param request - * @param response - * @return - */ - private boolean serviceTicketRequest(HttpServletRequest request, HttpServletResponse response) { - boolean result = super.requiresAuthentication(request, response); - this.logger.debug(LogMessage.format("serviceTicketRequest = %s", result)); - return result; - } - - /** - * Indicates if the request is elgible to process a proxy ticket. - * @param request - * @return - */ - private boolean proxyTicketRequest(boolean serviceTicketRequest, HttpServletRequest request) { - if (serviceTicketRequest) { - return false; - } - boolean result = this.authenticateAllArtifacts && obtainArtifact(request) != null && !authenticated(); - this.logger.debug(LogMessage.format("proxyTicketRequest = %s", result)); - return result; - } - - /** - * Determines if a user is already authenticated. - * @return - */ - private boolean authenticated() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - return authentication != null && authentication.isAuthenticated() - && !(authentication instanceof AnonymousAuthenticationToken); - } - - /** - * Indicates if the request is elgible to be processed as the proxy receptor. - * @param request - * @return - */ - private boolean proxyReceptorRequest(HttpServletRequest request) { - final boolean result = proxyReceptorConfigured() && this.proxyReceptorMatcher.matches(request); - this.logger.debug(LogMessage.format("proxyReceptorRequest = %s", result)); - return result; - } - - /** - * Determines if the {@link CasAuthenticationFilter} is configured to handle the proxy - * receptor requests. - * @return - */ - private boolean proxyReceptorConfigured() { - final boolean result = this.proxyGrantingTicketStorage != null && this.proxyReceptorMatcher != null; - this.logger.debug(LogMessage.format("proxyReceptorConfigured = %s", result)); - return result; - } - - /** - * A wrapper for the AuthenticationFailureHandler that will flex the - * {@link AuthenticationFailureHandler} that is used. The value - * {@link CasAuthenticationFilter#setProxyAuthenticationFailureHandler(AuthenticationFailureHandler)} - * will be used for proxy requests that fail. The value - * {@link CasAuthenticationFilter#setAuthenticationFailureHandler(AuthenticationFailureHandler)} - * will be used for service tickets that fail. - */ - private class CasAuthenticationFailureHandler implements AuthenticationFailureHandler { - - private final AuthenticationFailureHandler serviceTicketFailureHandler; - - CasAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) { - Assert.notNull(failureHandler, "failureHandler"); - this.serviceTicketFailureHandler = failureHandler; - } - - @Override - public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, - AuthenticationException exception) throws IOException, ServletException { - if (serviceTicketRequest(request, response)) { - this.serviceTicketFailureHandler.onAuthenticationFailure(request, response, exception); - } - else { - CasAuthenticationFilter.this.proxyFailureHandler.onAuthenticationFailure(request, response, exception); - } - } - - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/web/authentication/DefaultServiceAuthenticationDetails.java b/cas/src/main/java/org/springframework/security/cas/web/authentication/DefaultServiceAuthenticationDetails.java deleted file mode 100644 index c550e984ea..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/web/authentication/DefaultServiceAuthenticationDetails.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2011-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.web.authentication; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.regex.Pattern; - -import jakarta.servlet.http.HttpServletRequest; - -import org.springframework.security.web.authentication.WebAuthenticationDetails; -import org.springframework.security.web.util.UrlUtils; -import org.springframework.util.Assert; - -/** - * A default implementation of {@link ServiceAuthenticationDetails} that figures out the - * value for {@link #getServiceUrl()} by inspecting the current {@link HttpServletRequest} - * and using the current URL minus the artifact and the corresponding value. - * - * @author Rob Winch - */ -final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails - implements ServiceAuthenticationDetails { - - private static final long serialVersionUID = 6192409090610517700L; - - private final String serviceUrl; - - /** - * Creates a new instance - * @param request the current {@link HttpServletRequest} to obtain the - * {@link #getServiceUrl()} from. - * @param artifactPattern the {@link Pattern} that will be used to clean up the query - * string from containing the artifact name and value. This can be created using - * {@link #createArtifactPattern(String)}. - */ - DefaultServiceAuthenticationDetails(String casService, HttpServletRequest request, Pattern artifactPattern) - throws MalformedURLException { - super(request); - URL casServiceUrl = new URL(casService); - int port = getServicePort(casServiceUrl); - final String query = getQueryString(request, artifactPattern); - this.serviceUrl = UrlUtils.buildFullRequestUrl(casServiceUrl.getProtocol(), casServiceUrl.getHost(), port, - request.getRequestURI(), query); - } - - /** - * Returns the current URL minus the artifact parameter and its value, if present. - * @see org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails#getServiceUrl() - */ - @Override - public String getServiceUrl() { - return this.serviceUrl; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!super.equals(obj) || !(obj instanceof DefaultServiceAuthenticationDetails)) { - return false; - } - ServiceAuthenticationDetails that = (ServiceAuthenticationDetails) obj; - return this.serviceUrl.equals(that.getServiceUrl()); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + this.serviceUrl.hashCode(); - return result; - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - result.append(super.toString()); - result.append("ServiceUrl: "); - result.append(this.serviceUrl); - return result.toString(); - } - - /** - * If present, removes the artifactParameterName and the corresponding value from the - * query String. - * @param request - * @return the query String minus the artifactParameterName and the corresponding - * value. - */ - private String getQueryString(final HttpServletRequest request, final Pattern artifactPattern) { - final String query = request.getQueryString(); - if (query == null) { - return null; - } - String result = artifactPattern.matcher(query).replaceFirst(""); - if (result.length() == 0) { - return null; - } - // strip off the trailing & only if the artifact was the first query param - return result.startsWith("&") ? result.substring(1) : result; - } - - /** - * Creates a {@link Pattern} that can be passed into the constructor. This allows the - * {@link Pattern} to be reused for every instance of - * {@link DefaultServiceAuthenticationDetails}. - * @param artifactParameterName - * @return - */ - static Pattern createArtifactPattern(String artifactParameterName) { - Assert.hasLength(artifactParameterName, "artifactParameterName is expected to have a length"); - return Pattern.compile("&?" + Pattern.quote(artifactParameterName) + "=[^&]*"); - } - - /** - * Gets the port from the casServiceURL ensuring to return the proper value if the - * default port is being used. - * @param casServiceUrl the casServerUrl to be used (i.e. - * "https://example.com/context/login/cas") - * @return the port that is configured for the casServerUrl - */ - private static int getServicePort(URL casServiceUrl) { - int port = casServiceUrl.getPort(); - if (port == -1) { - port = casServiceUrl.getDefaultPort(); - } - return port; - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/web/authentication/ServiceAuthenticationDetails.java b/cas/src/main/java/org/springframework/security/cas/web/authentication/ServiceAuthenticationDetails.java deleted file mode 100644 index e14da3d70e..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/web/authentication/ServiceAuthenticationDetails.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2011-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.web.authentication; - -import java.io.Serializable; - -import org.springframework.security.cas.ServiceProperties; -import org.springframework.security.cas.authentication.CasAuthenticationProvider; -import org.springframework.security.core.Authentication; - -/** - * In order for the {@link CasAuthenticationProvider} to provide the correct service url - * to authenticate the ticket, the returned value of {@link Authentication#getDetails()} - * should implement this interface when tickets can be sent to any URL rather than only - * {@link ServiceProperties#getService()}. - * - * @author Rob Winch - * @see ServiceAuthenticationDetailsSource - */ -public interface ServiceAuthenticationDetails extends Serializable { - - /** - * Gets the absolute service url (i.e. https://example.com/service/). - * @return the service url. Cannot be null. - */ - String getServiceUrl(); - -} diff --git a/cas/src/main/java/org/springframework/security/cas/web/authentication/ServiceAuthenticationDetailsSource.java b/cas/src/main/java/org/springframework/security/cas/web/authentication/ServiceAuthenticationDetailsSource.java deleted file mode 100644 index b8515892dc..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/web/authentication/ServiceAuthenticationDetailsSource.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2011-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.web.authentication; - -import java.net.MalformedURLException; -import java.util.regex.Pattern; - -import jakarta.servlet.http.HttpServletRequest; - -import org.springframework.security.authentication.AuthenticationDetailsSource; -import org.springframework.security.cas.ServiceProperties; -import org.springframework.util.Assert; - -/** - * The {@code AuthenticationDetailsSource} that is set on the - * {@code CasAuthenticationFilter} should return a value that implements - * {@code ServiceAuthenticationDetails} if the application needs to authenticate dynamic - * service urls. The - * {@code ServiceAuthenticationDetailsSource#buildDetails(HttpServletRequest)} creates a - * default {@code ServiceAuthenticationDetails}. - * - * @author Rob Winch - */ -public class ServiceAuthenticationDetailsSource - implements AuthenticationDetailsSource { - - private final Pattern artifactPattern; - - private ServiceProperties serviceProperties; - - /** - * Creates an implementation that uses the specified ServiceProperties and the default - * CAS artifactParameterName. - * @param serviceProperties The ServiceProperties to use to construct the serviceUrl. - */ - public ServiceAuthenticationDetailsSource(ServiceProperties serviceProperties) { - this(serviceProperties, ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER); - } - - /** - * Creates an implementation that uses the specified artifactParameterName - * @param serviceProperties The ServiceProperties to use to construct the serviceUrl. - * @param artifactParameterName the artifactParameterName that is removed from the - * current URL. The result becomes the service url. Cannot be null and cannot be an - * empty String. - */ - public ServiceAuthenticationDetailsSource(ServiceProperties serviceProperties, String artifactParameterName) { - Assert.notNull(serviceProperties, "serviceProperties cannot be null"); - this.serviceProperties = serviceProperties; - this.artifactPattern = DefaultServiceAuthenticationDetails.createArtifactPattern(artifactParameterName); - } - - /** - * @param context the {@code HttpServletRequest} object. - * @return the {@code ServiceAuthenticationDetails} containing information about the - * current request - */ - @Override - public ServiceAuthenticationDetails buildDetails(HttpServletRequest context) { - try { - return new DefaultServiceAuthenticationDetails(this.serviceProperties.getService(), context, - this.artifactPattern); - } - catch (MalformedURLException ex) { - throw new RuntimeException(ex); - } - } - -} diff --git a/cas/src/main/java/org/springframework/security/cas/web/authentication/package-info.java b/cas/src/main/java/org/springframework/security/cas/web/authentication/package-info.java deleted file mode 100644 index ecd447dbac..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/web/authentication/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Authentication processing mechanisms which respond to the submission of authentication - * credentials using CAS. - */ -package org.springframework.security.cas.web.authentication; diff --git a/cas/src/main/java/org/springframework/security/cas/web/package-info.java b/cas/src/main/java/org/springframework/security/cas/web/package-info.java deleted file mode 100644 index 903fdb8d4c..0000000000 --- a/cas/src/main/java/org/springframework/security/cas/web/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Authenticates standard web browser users via CAS. - */ -package org.springframework.security.cas.web; diff --git a/cas/src/test/java/org/springframework/security/cas/authentication/AbstractStatelessTicketCacheTests.java b/cas/src/test/java/org/springframework/security/cas/authentication/AbstractStatelessTicketCacheTests.java deleted file mode 100644 index 7f1233b7d5..0000000000 --- a/cas/src/test/java/org/springframework/security/cas/authentication/AbstractStatelessTicketCacheTests.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.authentication; - -import java.util.ArrayList; -import java.util.List; - -import org.jasig.cas.client.validation.Assertion; -import org.jasig.cas.client.validation.AssertionImpl; - -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.userdetails.User; - -/** - * @author Scott Battaglia - * @since 2.0 - * - */ -public abstract class AbstractStatelessTicketCacheTests { - - protected CasAuthenticationToken getToken() { - List proxyList = new ArrayList<>(); - proxyList.add("https://localhost/newPortal/login/cas"); - User user = new User("rod", "password", true, true, true, true, - AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO")); - final Assertion assertion = new AssertionImpl("rod"); - return new CasAuthenticationToken("key", user, "ST-0-ER94xMJmn6pha35CQRoZ", - AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO"), user, assertion); - } - -} diff --git a/cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationProviderTests.java b/cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationProviderTests.java deleted file mode 100644 index 242d32a730..0000000000 --- a/cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationProviderTests.java +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.authentication; - -import java.util.HashMap; -import java.util.Map; - -import org.jasig.cas.client.validation.Assertion; -import org.jasig.cas.client.validation.AssertionImpl; -import org.jasig.cas.client.validation.TicketValidator; -import org.junit.jupiter.api.Test; - -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.cas.ServiceProperties; -import org.springframework.security.cas.web.CasAuthenticationFilter; -import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.web.authentication.WebAuthenticationDetails; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.assertj.core.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -/** - * Tests {@link CasAuthenticationProvider}. - * - * @author Ben Alex - * @author Scott Battaglia - */ -@SuppressWarnings("unchecked") -public class CasAuthenticationProviderTests { - - private UserDetails makeUserDetails() { - return new User("user", "password", true, true, true, true, - AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO")); - } - - private UserDetails makeUserDetailsFromAuthoritiesPopulator() { - return new User("user", "password", true, true, true, true, - AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B")); - } - - private ServiceProperties makeServiceProperties() { - final ServiceProperties serviceProperties = new ServiceProperties(); - serviceProperties.setSendRenew(false); - serviceProperties.setService("http://test.com"); - return serviceProperties; - } - - @Test - public void statefulAuthenticationIsSuccessful() throws Exception { - CasAuthenticationProvider cap = new CasAuthenticationProvider(); - cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator()); - cap.setKey("qwerty"); - StatelessTicketCache cache = new MockStatelessTicketCache(); - cap.setStatelessTicketCache(cache); - cap.setServiceProperties(makeServiceProperties()); - cap.setTicketValidator(new MockTicketValidator(true)); - cap.afterPropertiesSet(); - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( - CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, "ST-123"); - token.setDetails("details"); - Authentication result = cap.authenticate(token); - // Confirm ST-123 was NOT added to the cache - assertThat(cache.getByTicketId("ST-456") == null).isTrue(); - if (!(result instanceof CasAuthenticationToken)) { - fail("Should have returned a CasAuthenticationToken"); - } - CasAuthenticationToken casResult = (CasAuthenticationToken) result; - assertThat(casResult.getPrincipal()).isEqualTo(makeUserDetailsFromAuthoritiesPopulator()); - assertThat(casResult.getCredentials()).isEqualTo("ST-123"); - assertThat(casResult.getAuthorities()).contains(new SimpleGrantedAuthority("ROLE_A")); - assertThat(casResult.getAuthorities()).contains(new SimpleGrantedAuthority("ROLE_B")); - assertThat(casResult.getKeyHash()).isEqualTo(cap.getKey().hashCode()); - assertThat(casResult.getDetails()).isEqualTo("details"); - // Now confirm the CasAuthenticationToken is automatically re-accepted. - // To ensure TicketValidator not called again, set it to deliver an exception... - cap.setTicketValidator(new MockTicketValidator(false)); - Authentication laterResult = cap.authenticate(result); - assertThat(laterResult).isEqualTo(result); - } - - @Test - public void statelessAuthenticationIsSuccessful() throws Exception { - CasAuthenticationProvider cap = new CasAuthenticationProvider(); - cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator()); - cap.setKey("qwerty"); - StatelessTicketCache cache = new MockStatelessTicketCache(); - cap.setStatelessTicketCache(cache); - cap.setTicketValidator(new MockTicketValidator(true)); - cap.setServiceProperties(makeServiceProperties()); - cap.afterPropertiesSet(); - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( - CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, "ST-456"); - token.setDetails("details"); - Authentication result = cap.authenticate(token); - // Confirm ST-456 was added to the cache - assertThat(cache.getByTicketId("ST-456") != null).isTrue(); - if (!(result instanceof CasAuthenticationToken)) { - fail("Should have returned a CasAuthenticationToken"); - } - assertThat(result.getPrincipal()).isEqualTo(makeUserDetailsFromAuthoritiesPopulator()); - assertThat(result.getCredentials()).isEqualTo("ST-456"); - assertThat(result.getDetails()).isEqualTo("details"); - // Now try to authenticate again. To ensure TicketValidator not - // called again, set it to deliver an exception... - cap.setTicketValidator(new MockTicketValidator(false)); - // Previously created UsernamePasswordAuthenticationToken is OK - Authentication newResult = cap.authenticate(token); - assertThat(newResult.getPrincipal()).isEqualTo(makeUserDetailsFromAuthoritiesPopulator()); - assertThat(newResult.getCredentials()).isEqualTo("ST-456"); - } - - @Test - public void authenticateAllNullService() throws Exception { - String serviceUrl = "https://service/context"; - ServiceAuthenticationDetails details = mock(ServiceAuthenticationDetails.class); - given(details.getServiceUrl()).willReturn(serviceUrl); - TicketValidator validator = mock(TicketValidator.class); - given(validator.validate(any(String.class), any(String.class))).willReturn(new AssertionImpl("rod")); - ServiceProperties serviceProperties = makeServiceProperties(); - serviceProperties.setAuthenticateAllArtifacts(true); - CasAuthenticationProvider cap = new CasAuthenticationProvider(); - cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator()); - cap.setKey("qwerty"); - cap.setTicketValidator(validator); - cap.setServiceProperties(serviceProperties); - cap.afterPropertiesSet(); - String ticket = "ST-456"; - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( - CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket); - Authentication result = cap.authenticate(token); - } - - @Test - public void authenticateAllAuthenticationIsSuccessful() throws Exception { - String serviceUrl = "https://service/context"; - ServiceAuthenticationDetails details = mock(ServiceAuthenticationDetails.class); - given(details.getServiceUrl()).willReturn(serviceUrl); - TicketValidator validator = mock(TicketValidator.class); - given(validator.validate(any(String.class), any(String.class))).willReturn(new AssertionImpl("rod")); - ServiceProperties serviceProperties = makeServiceProperties(); - serviceProperties.setAuthenticateAllArtifacts(true); - CasAuthenticationProvider cap = new CasAuthenticationProvider(); - cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator()); - cap.setKey("qwerty"); - cap.setTicketValidator(validator); - cap.setServiceProperties(serviceProperties); - cap.afterPropertiesSet(); - String ticket = "ST-456"; - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( - CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket); - Authentication result = cap.authenticate(token); - verify(validator).validate(ticket, serviceProperties.getService()); - serviceProperties.setAuthenticateAllArtifacts(true); - result = cap.authenticate(token); - verify(validator, times(2)).validate(ticket, serviceProperties.getService()); - token.setDetails(details); - result = cap.authenticate(token); - verify(validator).validate(ticket, serviceUrl); - serviceProperties.setAuthenticateAllArtifacts(false); - serviceProperties.setService(null); - cap.setServiceProperties(serviceProperties); - cap.afterPropertiesSet(); - result = cap.authenticate(token); - verify(validator, times(2)).validate(ticket, serviceUrl); - token.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest())); - assertThatIllegalStateException().isThrownBy(() -> cap.authenticate(token)); - cap.setServiceProperties(null); - cap.afterPropertiesSet(); - assertThatIllegalStateException().isThrownBy(() -> cap.authenticate(token)); - } - - @Test - public void missingTicketIdIsDetected() throws Exception { - CasAuthenticationProvider cap = new CasAuthenticationProvider(); - cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator()); - cap.setKey("qwerty"); - StatelessTicketCache cache = new MockStatelessTicketCache(); - cap.setStatelessTicketCache(cache); - cap.setTicketValidator(new MockTicketValidator(true)); - cap.setServiceProperties(makeServiceProperties()); - cap.afterPropertiesSet(); - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( - CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, ""); - assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> cap.authenticate(token)); - } - - @Test - public void invalidKeyIsDetected() throws Exception { - final Assertion assertion = new AssertionImpl("test"); - CasAuthenticationProvider cap = new CasAuthenticationProvider(); - cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator()); - cap.setKey("qwerty"); - StatelessTicketCache cache = new MockStatelessTicketCache(); - cap.setStatelessTicketCache(cache); - cap.setTicketValidator(new MockTicketValidator(true)); - cap.setServiceProperties(makeServiceProperties()); - cap.afterPropertiesSet(); - CasAuthenticationToken token = new CasAuthenticationToken("WRONG_KEY", makeUserDetails(), "credentials", - AuthorityUtils.createAuthorityList("XX"), makeUserDetails(), assertion); - assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> cap.authenticate(token)); - } - - @Test - public void detectsMissingAuthoritiesPopulator() throws Exception { - CasAuthenticationProvider cap = new CasAuthenticationProvider(); - cap.setKey("qwerty"); - cap.setStatelessTicketCache(new MockStatelessTicketCache()); - cap.setTicketValidator(new MockTicketValidator(true)); - cap.setServiceProperties(makeServiceProperties()); - assertThatIllegalArgumentException().isThrownBy(() -> cap.afterPropertiesSet()); - } - - @Test - public void detectsMissingKey() throws Exception { - CasAuthenticationProvider cap = new CasAuthenticationProvider(); - cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator()); - cap.setStatelessTicketCache(new MockStatelessTicketCache()); - cap.setTicketValidator(new MockTicketValidator(true)); - cap.setServiceProperties(makeServiceProperties()); - assertThatIllegalArgumentException().isThrownBy(() -> cap.afterPropertiesSet()); - } - - @Test - public void detectsMissingStatelessTicketCache() throws Exception { - CasAuthenticationProvider cap = new CasAuthenticationProvider(); - // set this explicitly to null to test failure - cap.setStatelessTicketCache(null); - cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator()); - cap.setKey("qwerty"); - cap.setTicketValidator(new MockTicketValidator(true)); - cap.setServiceProperties(makeServiceProperties()); - assertThatIllegalArgumentException().isThrownBy(() -> cap.afterPropertiesSet()); - } - - @Test - public void detectsMissingTicketValidator() throws Exception { - CasAuthenticationProvider cap = new CasAuthenticationProvider(); - cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator()); - cap.setKey("qwerty"); - cap.setStatelessTicketCache(new MockStatelessTicketCache()); - cap.setServiceProperties(makeServiceProperties()); - assertThatIllegalArgumentException().isThrownBy(() -> cap.afterPropertiesSet()); - } - - @Test - public void gettersAndSettersMatch() throws Exception { - CasAuthenticationProvider cap = new CasAuthenticationProvider(); - cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator()); - cap.setKey("qwerty"); - cap.setStatelessTicketCache(new MockStatelessTicketCache()); - cap.setTicketValidator(new MockTicketValidator(true)); - cap.setServiceProperties(makeServiceProperties()); - cap.afterPropertiesSet(); - // TODO disabled because why do we need to expose this? - // assertThat(cap.getUserDetailsService() != null).isTrue(); - assertThat(cap.getKey()).isEqualTo("qwerty"); - assertThat(cap.getStatelessTicketCache() != null).isTrue(); - assertThat(cap.getTicketValidator() != null).isTrue(); - } - - @Test - public void ignoresClassesItDoesNotSupport() throws Exception { - CasAuthenticationProvider cap = new CasAuthenticationProvider(); - cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator()); - cap.setKey("qwerty"); - cap.setStatelessTicketCache(new MockStatelessTicketCache()); - cap.setTicketValidator(new MockTicketValidator(true)); - cap.setServiceProperties(makeServiceProperties()); - cap.afterPropertiesSet(); - TestingAuthenticationToken token = new TestingAuthenticationToken("user", "password", "ROLE_A"); - assertThat(cap.supports(TestingAuthenticationToken.class)).isFalse(); - // Try it anyway - assertThat(cap.authenticate(token)).isNull(); - } - - @Test - public void ignoresUsernamePasswordAuthenticationTokensWithoutCasIdentifiersAsPrincipal() throws Exception { - CasAuthenticationProvider cap = new CasAuthenticationProvider(); - cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator()); - cap.setKey("qwerty"); - cap.setStatelessTicketCache(new MockStatelessTicketCache()); - cap.setTicketValidator(new MockTicketValidator(true)); - cap.setServiceProperties(makeServiceProperties()); - cap.afterPropertiesSet(); - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("some_normal_user", - "password", AuthorityUtils.createAuthorityList("ROLE_A")); - assertThat(cap.authenticate(token)).isNull(); - } - - @Test - public void supportsRequiredTokens() { - CasAuthenticationProvider cap = new CasAuthenticationProvider(); - assertThat(cap.supports(UsernamePasswordAuthenticationToken.class)).isTrue(); - assertThat(cap.supports(CasAuthenticationToken.class)).isTrue(); - } - - private class MockAuthoritiesPopulator implements AuthenticationUserDetailsService { - - @Override - public UserDetails loadUserDetails(final Authentication token) throws UsernameNotFoundException { - return makeUserDetailsFromAuthoritiesPopulator(); - } - - } - - private class MockStatelessTicketCache implements StatelessTicketCache { - - private Map cache = new HashMap<>(); - - @Override - public CasAuthenticationToken getByTicketId(String serviceTicket) { - return this.cache.get(serviceTicket); - } - - @Override - public void putTicketInCache(CasAuthenticationToken token) { - this.cache.put(token.getCredentials().toString(), token); - } - - @Override - public void removeTicketFromCache(CasAuthenticationToken token) { - throw new UnsupportedOperationException("mock method not implemented"); - } - - @Override - public void removeTicketFromCache(String serviceTicket) { - throw new UnsupportedOperationException("mock method not implemented"); - } - - } - - private class MockTicketValidator implements TicketValidator { - - private boolean returnTicket; - - MockTicketValidator(boolean returnTicket) { - this.returnTicket = returnTicket; - } - - @Override - public Assertion validate(final String ticket, final String service) { - if (this.returnTicket) { - return new AssertionImpl("rod"); - } - throw new BadCredentialsException("As requested from mock"); - } - - } - -} diff --git a/cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationTokenTests.java b/cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationTokenTests.java deleted file mode 100644 index 8ac076830b..0000000000 --- a/cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationTokenTests.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.authentication; - -import java.util.Collections; -import java.util.List; - -import org.jasig.cas.client.validation.Assertion; -import org.jasig.cas.client.validation.AssertionImpl; -import org.junit.jupiter.api.Test; - -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; - -/** - * Tests {@link CasAuthenticationToken}. - * - * @author Ben Alex - */ -public class CasAuthenticationTokenTests { - - private final List ROLES = AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO"); - - private UserDetails makeUserDetails() { - return makeUserDetails("user"); - } - - private UserDetails makeUserDetails(final String name) { - return new User(name, "password", true, true, true, true, this.ROLES); - } - - @Test - public void testConstructorRejectsNulls() { - Assertion assertion = new AssertionImpl("test"); - assertThatIllegalArgumentException().isThrownBy(() -> new CasAuthenticationToken(null, makeUserDetails(), - "Password", this.ROLES, makeUserDetails(), assertion)); - assertThatIllegalArgumentException().isThrownBy( - () -> new CasAuthenticationToken("key", null, "Password", this.ROLES, makeUserDetails(), assertion)); - assertThatIllegalArgumentException().isThrownBy(() -> new CasAuthenticationToken("key", makeUserDetails(), null, - this.ROLES, makeUserDetails(), assertion)); - assertThatIllegalArgumentException().isThrownBy(() -> new CasAuthenticationToken("key", makeUserDetails(), - "Password", this.ROLES, makeUserDetails(), null)); - assertThatIllegalArgumentException().isThrownBy( - () -> new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES, null, assertion)); - assertThatIllegalArgumentException().isThrownBy(() -> new CasAuthenticationToken("key", makeUserDetails(), - "Password", AuthorityUtils.createAuthorityList("ROLE_1", null), makeUserDetails(), assertion)); - } - - @Test - public void constructorWhenEmptyKeyThenThrowsException() { - assertThatIllegalArgumentException().isThrownBy( - () -> new CasAuthenticationToken("", "user", "password", Collections.emptyList(), - new User("user", "password", Collections.emptyList()), null)); - } - - @Test - public void testEqualsWhenEqual() { - final Assertion assertion = new AssertionImpl("test"); - CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES, - makeUserDetails(), assertion); - CasAuthenticationToken token2 = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES, - makeUserDetails(), assertion); - assertThat(token2).isEqualTo(token1); - } - - @Test - public void testGetters() { - // Build the proxy list returned in the ticket from CAS - final Assertion assertion = new AssertionImpl("test"); - CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES, - makeUserDetails(), assertion); - assertThat(token.getKeyHash()).isEqualTo("key".hashCode()); - assertThat(token.getPrincipal()).isEqualTo(makeUserDetails()); - assertThat(token.getCredentials()).isEqualTo("Password"); - assertThat(token.getAuthorities()).contains(new SimpleGrantedAuthority("ROLE_ONE")); - assertThat(token.getAuthorities()).contains(new SimpleGrantedAuthority("ROLE_TWO")); - assertThat(token.getAssertion()).isEqualTo(assertion); - assertThat(token.getUserDetails().getUsername()).isEqualTo(makeUserDetails().getUsername()); - } - - @Test - public void testNoArgConstructorDoesntExist() { - assertThatExceptionOfType(NoSuchMethodException.class) - .isThrownBy(() -> CasAuthenticationToken.class.getDeclaredConstructor((Class[]) null)); - } - - @Test - public void testNotEqualsDueToAbstractParentEqualsCheck() { - final Assertion assertion = new AssertionImpl("test"); - CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES, - makeUserDetails(), assertion); - CasAuthenticationToken token2 = new CasAuthenticationToken("key", makeUserDetails("OTHER_NAME"), "Password", - this.ROLES, makeUserDetails(), assertion); - assertThat(!token1.equals(token2)).isTrue(); - } - - @Test - public void testNotEqualsDueToDifferentAuthenticationClass() { - final Assertion assertion = new AssertionImpl("test"); - CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES, - makeUserDetails(), assertion); - UsernamePasswordAuthenticationToken token2 = new UsernamePasswordAuthenticationToken("Test", "Password", - this.ROLES); - assertThat(!token1.equals(token2)).isTrue(); - } - - @Test - public void testNotEqualsDueToKey() { - final Assertion assertion = new AssertionImpl("test"); - CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES, - makeUserDetails(), assertion); - CasAuthenticationToken token2 = new CasAuthenticationToken("DIFFERENT_KEY", makeUserDetails(), "Password", - this.ROLES, makeUserDetails(), assertion); - assertThat(!token1.equals(token2)).isTrue(); - } - - @Test - public void testNotEqualsDueToAssertion() { - final Assertion assertion = new AssertionImpl("test"); - final Assertion assertion2 = new AssertionImpl("test"); - CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES, - makeUserDetails(), assertion); - CasAuthenticationToken token2 = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES, - makeUserDetails(), assertion2); - assertThat(!token1.equals(token2)).isTrue(); - } - - @Test - public void testSetAuthenticated() { - final Assertion assertion = new AssertionImpl("test"); - CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES, - makeUserDetails(), assertion); - assertThat(token.isAuthenticated()).isTrue(); - token.setAuthenticated(false); - assertThat(!token.isAuthenticated()).isTrue(); - } - - @Test - public void testToString() { - final Assertion assertion = new AssertionImpl("test"); - CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES, - makeUserDetails(), assertion); - String result = token.toString(); - assertThat(result.lastIndexOf("Credentials (Service/Proxy Ticket):") != -1).isTrue(); - } - -} diff --git a/cas/src/test/java/org/springframework/security/cas/authentication/NullStatelessTicketCacheTests.java b/cas/src/test/java/org/springframework/security/cas/authentication/NullStatelessTicketCacheTests.java deleted file mode 100644 index f5a87e5c4e..0000000000 --- a/cas/src/test/java/org/springframework/security/cas/authentication/NullStatelessTicketCacheTests.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.authentication; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Test cases for the @link {@link NullStatelessTicketCache} - * - * @author Scott Battaglia - * - */ -public class NullStatelessTicketCacheTests extends AbstractStatelessTicketCacheTests { - - private StatelessTicketCache cache = new NullStatelessTicketCache(); - - @Test - public void testGetter() { - assertThat(this.cache.getByTicketId(null)).isNull(); - assertThat(this.cache.getByTicketId("test")).isNull(); - } - - @Test - public void testInsertAndGet() { - final CasAuthenticationToken token = getToken(); - this.cache.putTicketInCache(token); - assertThat(this.cache.getByTicketId((String) token.getCredentials())).isNull(); - } - -} diff --git a/cas/src/test/java/org/springframework/security/cas/authentication/SpringCacheBasedTicketCacheTests.java b/cas/src/test/java/org/springframework/security/cas/authentication/SpringCacheBasedTicketCacheTests.java deleted file mode 100644 index e27344a9ce..0000000000 --- a/cas/src/test/java/org/springframework/security/cas/authentication/SpringCacheBasedTicketCacheTests.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.authentication; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import org.springframework.cache.CacheManager; -import org.springframework.cache.concurrent.ConcurrentMapCacheManager; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; - -/** - * Tests - * {@link org.springframework.security.cas.authentication.SpringCacheBasedTicketCache}. - * - * @author Marten Deinum - * @since 3.2 - */ -public class SpringCacheBasedTicketCacheTests extends AbstractStatelessTicketCacheTests { - - private static CacheManager cacheManager; - - @BeforeAll - public static void initCacheManaer() { - cacheManager = new ConcurrentMapCacheManager(); - cacheManager.getCache("castickets"); - } - - @Test - public void testCacheOperation() throws Exception { - SpringCacheBasedTicketCache cache = new SpringCacheBasedTicketCache(cacheManager.getCache("castickets")); - final CasAuthenticationToken token = getToken(); - // Check it gets stored in the cache - cache.putTicketInCache(token); - assertThat(cache.getByTicketId("ST-0-ER94xMJmn6pha35CQRoZ")).isEqualTo(token); - // Check it gets removed from the cache - cache.removeTicketFromCache(getToken()); - assertThat(cache.getByTicketId("ST-0-ER94xMJmn6pha35CQRoZ")).isNull(); - // Check it doesn't return values for null or unknown service tickets - assertThat(cache.getByTicketId(null)).isNull(); - assertThat(cache.getByTicketId("UNKNOWN_SERVICE_TICKET")).isNull(); - } - - @Test - public void testStartupDetectsMissingCache() throws Exception { - assertThatIllegalArgumentException().isThrownBy(() -> new SpringCacheBasedTicketCache(null)); - } - -} diff --git a/cas/src/test/java/org/springframework/security/cas/jackson2/CasAuthenticationTokenMixinTests.java b/cas/src/test/java/org/springframework/security/cas/jackson2/CasAuthenticationTokenMixinTests.java deleted file mode 100644 index ce333d4d83..0000000000 --- a/cas/src/test/java/org/springframework/security/cas/jackson2/CasAuthenticationTokenMixinTests.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2015-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.jackson2; - -import java.io.IOException; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.jasig.cas.client.authentication.AttributePrincipalImpl; -import org.jasig.cas.client.validation.Assertion; -import org.jasig.cas.client.validation.AssertionImpl; -import org.json.JSONException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.skyscreamer.jsonassert.JSONAssert; - -import org.springframework.security.cas.authentication.CasAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.jackson2.SecurityJackson2Modules; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Jitendra Singh - * @since 4.2 - */ -public class CasAuthenticationTokenMixinTests { - - private static final String KEY = "casKey"; - - private static final String PASSWORD = "\"1234\""; - - private static final Date START_DATE = new Date(); - - private static final Date END_DATE = new Date(); - - public static final String AUTHORITY_JSON = "{\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\", \"authority\": \"ROLE_USER\"}"; - - public static final String AUTHORITIES_SET_JSON = "[\"java.util.Collections$UnmodifiableSet\", [" + AUTHORITY_JSON - + "]]"; - - public static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", [" - + AUTHORITY_JSON + "]]"; - - // @formatter:off - public static final String USER_JSON = "{" - + "\"@class\": \"org.springframework.security.core.userdetails.User\", " - + "\"username\": \"admin\"," - + " \"password\": " + PASSWORD + ", " - + "\"accountNonExpired\": true, " - + "\"accountNonLocked\": true, " - + "\"credentialsNonExpired\": true, " - + "\"enabled\": true, " - + "\"authorities\": " + AUTHORITIES_SET_JSON - + "}"; - // @formatter:on - private static final String CAS_TOKEN_JSON = "{" - + "\"@class\": \"org.springframework.security.cas.authentication.CasAuthenticationToken\", " - + "\"keyHash\": " + KEY.hashCode() + "," + "\"principal\": " + USER_JSON + ", " + "\"credentials\": " - + PASSWORD + ", " + "\"authorities\": " + AUTHORITIES_ARRAYLIST_JSON + "," + "\"userDetails\": " + USER_JSON - + "," + "\"authenticated\": true, " + "\"details\": null," + "\"assertion\": {" - + "\"@class\": \"org.jasig.cas.client.validation.AssertionImpl\", " + "\"principal\": {" - + "\"@class\": \"org.jasig.cas.client.authentication.AttributePrincipalImpl\", " - + "\"name\": \"assertName\", " + "\"attributes\": {\"@class\": \"java.util.Collections$EmptyMap\"}, " - + "\"proxyGrantingTicket\": null, " + "\"proxyRetriever\": null" + "}, " - + "\"validFromDate\": [\"java.util.Date\", " + START_DATE.getTime() + "], " - + "\"validUntilDate\": [\"java.util.Date\", " + END_DATE.getTime() + "]," - + "\"authenticationDate\": [\"java.util.Date\", " + START_DATE.getTime() + "], " - + "\"attributes\": {\"@class\": \"java.util.Collections$EmptyMap\"}" + "}" + "}"; - - private static final String CAS_TOKEN_CLEARED_JSON = CAS_TOKEN_JSON.replaceFirst(PASSWORD, "null"); - - protected ObjectMapper mapper; - - @BeforeEach - public void setup() { - this.mapper = new ObjectMapper(); - ClassLoader loader = getClass().getClassLoader(); - this.mapper.registerModules(SecurityJackson2Modules.getModules(loader)); - } - - @Test - public void serializeCasAuthenticationTest() throws JsonProcessingException, JSONException { - CasAuthenticationToken token = createCasAuthenticationToken(); - String actualJson = this.mapper.writeValueAsString(token); - JSONAssert.assertEquals(CAS_TOKEN_JSON, actualJson, true); - } - - @Test - public void serializeCasAuthenticationTestAfterEraseCredentialInvoked() - throws JsonProcessingException, JSONException { - CasAuthenticationToken token = createCasAuthenticationToken(); - token.eraseCredentials(); - String actualJson = this.mapper.writeValueAsString(token); - JSONAssert.assertEquals(CAS_TOKEN_CLEARED_JSON, actualJson, true); - } - - @Test - public void deserializeCasAuthenticationTestAfterEraseCredentialInvoked() throws Exception { - CasAuthenticationToken token = this.mapper.readValue(CAS_TOKEN_CLEARED_JSON, CasAuthenticationToken.class); - assertThat(((UserDetails) token.getPrincipal()).getPassword()).isNull(); - } - - @Test - public void deserializeCasAuthenticationTest() throws IOException { - CasAuthenticationToken token = this.mapper.readValue(CAS_TOKEN_JSON, CasAuthenticationToken.class); - assertThat(token).isNotNull(); - assertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class); - assertThat(((User) token.getPrincipal()).getUsername()).isEqualTo("admin"); - assertThat(((User) token.getPrincipal()).getPassword()).isEqualTo("1234"); - assertThat(token.getUserDetails()).isNotNull().isInstanceOf(User.class); - assertThat(token.getAssertion()).isNotNull().isInstanceOf(AssertionImpl.class); - assertThat(token.getKeyHash()).isEqualTo(KEY.hashCode()); - assertThat(token.getUserDetails().getAuthorities()).extracting(GrantedAuthority::getAuthority) - .containsOnly("ROLE_USER"); - assertThat(token.getAssertion().getAuthenticationDate()).isEqualTo(START_DATE); - assertThat(token.getAssertion().getValidFromDate()).isEqualTo(START_DATE); - assertThat(token.getAssertion().getValidUntilDate()).isEqualTo(END_DATE); - assertThat(token.getAssertion().getPrincipal().getName()).isEqualTo("assertName"); - assertThat(token.getAssertion().getAttributes()).hasSize(0); - } - - private CasAuthenticationToken createCasAuthenticationToken() { - User principal = new User("admin", "1234", Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))); - Collection authorities = Collections - .singletonList(new SimpleGrantedAuthority("ROLE_USER")); - Assertion assertion = new AssertionImpl(new AttributePrincipalImpl("assertName"), START_DATE, END_DATE, - START_DATE, Collections.emptyMap()); - return new CasAuthenticationToken(KEY, principal, principal.getPassword(), authorities, - new User("admin", "1234", authorities), assertion); - } - -} diff --git a/cas/src/test/java/org/springframework/security/cas/userdetails/GrantedAuthorityFromAssertionAttributesUserDetailsServiceTests.java b/cas/src/test/java/org/springframework/security/cas/userdetails/GrantedAuthorityFromAssertionAttributesUserDetailsServiceTests.java deleted file mode 100644 index 3db719dcc2..0000000000 --- a/cas/src/test/java/org/springframework/security/cas/userdetails/GrantedAuthorityFromAssertionAttributesUserDetailsServiceTests.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.userdetails; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import org.jasig.cas.client.authentication.AttributePrincipal; -import org.jasig.cas.client.validation.Assertion; -import org.junit.jupiter.api.Test; - -import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.userdetails.UserDetails; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * @author Luke Taylor - */ -public class GrantedAuthorityFromAssertionAttributesUserDetailsServiceTests { - - @Test - public void correctlyExtractsNamedAttributesFromAssertionAndConvertsThemToAuthorities() { - GrantedAuthorityFromAssertionAttributesUserDetailsService uds = new GrantedAuthorityFromAssertionAttributesUserDetailsService( - new String[] { "a", "b", "c", "d" }); - uds.setConvertToUpperCase(false); - Assertion assertion = mock(Assertion.class); - AttributePrincipal principal = mock(AttributePrincipal.class); - Map attributes = new HashMap<>(); - attributes.put("a", Arrays.asList("role_a1", "role_a2")); - attributes.put("b", "role_b"); - attributes.put("c", "role_c"); - attributes.put("d", null); - attributes.put("someother", "unused"); - given(assertion.getPrincipal()).willReturn(principal); - given(principal.getAttributes()).willReturn(attributes); - given(principal.getName()).willReturn("somebody"); - CasAssertionAuthenticationToken token = new CasAssertionAuthenticationToken(assertion, "ticket"); - UserDetails user = uds.loadUserDetails(token); - Set roles = AuthorityUtils.authorityListToSet(user.getAuthorities()); - assertThat(roles).containsExactlyInAnyOrder("role_a1", "role_a2", "role_b", "role_c"); - } - -} diff --git a/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationEntryPointTests.java b/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationEntryPointTests.java deleted file mode 100644 index 5fb0e7c7f8..0000000000 --- a/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationEntryPointTests.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.web; - -import java.net.URLEncoder; - -import org.junit.jupiter.api.Test; - -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.cas.ServiceProperties; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; - -/** - * Tests {@link CasAuthenticationEntryPoint}. - * - * @author Ben Alex - */ -public class CasAuthenticationEntryPointTests { - - @Test - public void testDetectsMissingLoginFormUrl() throws Exception { - CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint(); - ep.setServiceProperties(new ServiceProperties()); - assertThatIllegalArgumentException().isThrownBy(ep::afterPropertiesSet) - .withMessage("loginUrl must be specified"); - } - - @Test - public void testDetectsMissingServiceProperties() throws Exception { - CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint(); - ep.setLoginUrl("https://cas/login"); - assertThatIllegalArgumentException().isThrownBy(ep::afterPropertiesSet) - .withMessage("serviceProperties must be specified"); - } - - @Test - public void testGettersSetters() { - CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint(); - ep.setLoginUrl("https://cas/login"); - assertThat(ep.getLoginUrl()).isEqualTo("https://cas/login"); - ep.setServiceProperties(new ServiceProperties()); - assertThat(ep.getServiceProperties() != null).isTrue(); - } - - @Test - public void testNormalOperationWithRenewFalse() throws Exception { - ServiceProperties sp = new ServiceProperties(); - sp.setSendRenew(false); - sp.setService("https://mycompany.com/bigWebApp/login/cas"); - CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint(); - ep.setLoginUrl("https://cas/login"); - ep.setServiceProperties(sp); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setRequestURI("/some_path"); - MockHttpServletResponse response = new MockHttpServletResponse(); - ep.afterPropertiesSet(); - ep.commence(request, response, null); - assertThat( - "https://cas/login?service=" + URLEncoder.encode("https://mycompany.com/bigWebApp/login/cas", "UTF-8")) - .isEqualTo(response.getRedirectedUrl()); - } - - @Test - public void testNormalOperationWithRenewTrue() throws Exception { - ServiceProperties sp = new ServiceProperties(); - sp.setSendRenew(true); - sp.setService("https://mycompany.com/bigWebApp/login/cas"); - CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint(); - ep.setLoginUrl("https://cas/login"); - ep.setServiceProperties(sp); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setRequestURI("/some_path"); - MockHttpServletResponse response = new MockHttpServletResponse(); - ep.afterPropertiesSet(); - ep.commence(request, response, null); - assertThat("https://cas/login?service=" - + URLEncoder.encode("https://mycompany.com/bigWebApp/login/cas", "UTF-8") + "&renew=true") - .isEqualTo(response.getRedirectedUrl()); - } - -} diff --git a/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationFilterTests.java b/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationFilterTests.java deleted file mode 100644 index f19222baf2..0000000000 --- a/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationFilterTests.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.web; - -import jakarta.servlet.FilterChain; - -import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; - -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.cas.ServiceProperties; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.AuthenticationSuccessHandler; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; - -/** - * Tests {@link CasAuthenticationFilter}. - * - * @author Ben Alex - * @author Rob Winch - */ -public class CasAuthenticationFilterTests { - - @AfterEach - public void tearDown() { - SecurityContextHolder.clearContext(); - } - - @Test - public void testGettersSetters() { - CasAuthenticationFilter filter = new CasAuthenticationFilter(); - filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class)); - filter.setProxyReceptorUrl("/someurl"); - filter.setServiceProperties(new ServiceProperties()); - } - - @Test - public void testNormalOperation() throws Exception { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setServletPath("/login/cas"); - request.addParameter("ticket", "ST-0-ER94xMJmn6pha35CQRoZ"); - CasAuthenticationFilter filter = new CasAuthenticationFilter(); - filter.setAuthenticationManager((a) -> a); - assertThat(filter.requiresAuthentication(request, new MockHttpServletResponse())).isTrue(); - Authentication result = filter.attemptAuthentication(request, new MockHttpServletResponse()); - assertThat(result != null).isTrue(); - } - - @Test - public void testNullServiceTicketHandledGracefully() throws Exception { - CasAuthenticationFilter filter = new CasAuthenticationFilter(); - filter.setAuthenticationManager((a) -> { - throw new BadCredentialsException("Rejected"); - }); - assertThatExceptionOfType(AuthenticationException.class).isThrownBy( - () -> filter.attemptAuthentication(new MockHttpServletRequest(), new MockHttpServletResponse())); - } - - @Test - public void testRequiresAuthenticationFilterProcessUrl() { - String url = "/login/cas"; - CasAuthenticationFilter filter = new CasAuthenticationFilter(); - filter.setFilterProcessesUrl(url); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - request.setServletPath(url); - assertThat(filter.requiresAuthentication(request, response)).isTrue(); - } - - @Test - public void testRequiresAuthenticationProxyRequest() { - CasAuthenticationFilter filter = new CasAuthenticationFilter(); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - request.setServletPath("/pgtCallback"); - assertThat(filter.requiresAuthentication(request, response)).isFalse(); - filter.setProxyReceptorUrl(request.getServletPath()); - assertThat(filter.requiresAuthentication(request, response)).isFalse(); - filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class)); - assertThat(filter.requiresAuthentication(request, response)).isTrue(); - request.setServletPath("/other"); - assertThat(filter.requiresAuthentication(request, response)).isFalse(); - } - - @Test - public void testRequiresAuthenticationAuthAll() { - ServiceProperties properties = new ServiceProperties(); - properties.setAuthenticateAllArtifacts(true); - String url = "/login/cas"; - CasAuthenticationFilter filter = new CasAuthenticationFilter(); - filter.setFilterProcessesUrl(url); - filter.setServiceProperties(properties); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - request.setServletPath(url); - assertThat(filter.requiresAuthentication(request, response)).isTrue(); - request.setServletPath("/other"); - assertThat(filter.requiresAuthentication(request, response)).isFalse(); - request.setParameter(properties.getArtifactParameter(), "value"); - assertThat(filter.requiresAuthentication(request, response)).isTrue(); - SecurityContextHolder.getContext().setAuthentication(new AnonymousAuthenticationToken("key", "principal", - AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"))); - assertThat(filter.requiresAuthentication(request, response)).isTrue(); - SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("un", "principal")); - assertThat(filter.requiresAuthentication(request, response)).isTrue(); - SecurityContextHolder.getContext() - .setAuthentication(new TestingAuthenticationToken("un", "principal", "ROLE_ANONYMOUS")); - assertThat(filter.requiresAuthentication(request, response)).isFalse(); - } - - @Test - public void testAuthenticateProxyUrl() throws Exception { - CasAuthenticationFilter filter = new CasAuthenticationFilter(); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - request.setServletPath("/pgtCallback"); - filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class)); - filter.setProxyReceptorUrl(request.getServletPath()); - assertThat(filter.attemptAuthentication(request, response)).isNull(); - } - - @Test - public void testDoFilterAuthenticateAll() throws Exception { - AuthenticationSuccessHandler successHandler = mock(AuthenticationSuccessHandler.class); - AuthenticationManager manager = mock(AuthenticationManager.class); - Authentication authentication = new TestingAuthenticationToken("un", "pwd", "ROLE_USER"); - given(manager.authenticate(any(Authentication.class))).willReturn(authentication); - ServiceProperties serviceProperties = new ServiceProperties(); - serviceProperties.setAuthenticateAllArtifacts(true); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setParameter("ticket", "ST-1-123"); - request.setServletPath("/authenticate"); - MockHttpServletResponse response = new MockHttpServletResponse(); - FilterChain chain = mock(FilterChain.class); - CasAuthenticationFilter filter = new CasAuthenticationFilter(); - filter.setServiceProperties(serviceProperties); - filter.setAuthenticationSuccessHandler(successHandler); - filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class)); - filter.setAuthenticationManager(manager); - filter.afterPropertiesSet(); - filter.doFilter(request, response, chain); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull() - .withFailMessage("Authentication should not be null"); - verify(chain).doFilter(request, response); - verifyZeroInteractions(successHandler); - // validate for when the filterProcessUrl matches - filter.setFilterProcessesUrl(request.getServletPath()); - SecurityContextHolder.clearContext(); - filter.doFilter(request, response, chain); - verifyNoMoreInteractions(chain); - verify(successHandler).onAuthenticationSuccess(request, response, authentication); - } - - // SEC-1592 - @Test - public void testChainNotInvokedForProxyReceptor() throws Exception { - CasAuthenticationFilter filter = new CasAuthenticationFilter(); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - FilterChain chain = mock(FilterChain.class); - request.setServletPath("/pgtCallback"); - filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class)); - filter.setProxyReceptorUrl(request.getServletPath()); - filter.doFilter(request, response, chain); - verifyZeroInteractions(chain); - } - -} diff --git a/cas/src/test/java/org/springframework/security/cas/web/ServicePropertiesTests.java b/cas/src/test/java/org/springframework/security/cas/web/ServicePropertiesTests.java deleted file mode 100644 index fc52e1d6a5..0000000000 --- a/cas/src/test/java/org/springframework/security/cas/web/ServicePropertiesTests.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.web; - -import org.junit.jupiter.api.Test; - -import org.springframework.security.cas.SamlServiceProperties; -import org.springframework.security.cas.ServiceProperties; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; - -/** - * Tests {@link ServiceProperties}. - * - * @author Ben Alex - */ -public class ServicePropertiesTests { - - @Test - public void detectsMissingService() throws Exception { - ServiceProperties sp = new ServiceProperties(); - assertThatIllegalArgumentException().isThrownBy(sp::afterPropertiesSet); - } - - @Test - public void nullServiceWhenAuthenticateAllTokens() throws Exception { - ServiceProperties sp = new ServiceProperties(); - sp.setAuthenticateAllArtifacts(true); - assertThatIllegalArgumentException().isThrownBy(sp::afterPropertiesSet); - sp.setAuthenticateAllArtifacts(false); - assertThatIllegalArgumentException().isThrownBy(sp::afterPropertiesSet); - } - - @Test - public void testGettersSetters() throws Exception { - ServiceProperties[] sps = { new ServiceProperties(), new SamlServiceProperties() }; - for (ServiceProperties sp : sps) { - sp.setSendRenew(false); - assertThat(sp.isSendRenew()).isFalse(); - sp.setSendRenew(true); - assertThat(sp.isSendRenew()).isTrue(); - sp.setArtifactParameter("notticket"); - assertThat(sp.getArtifactParameter()).isEqualTo("notticket"); - sp.setServiceParameter("notservice"); - assertThat(sp.getServiceParameter()).isEqualTo("notservice"); - sp.setService("https://mycompany.com/service"); - assertThat(sp.getService()).isEqualTo("https://mycompany.com/service"); - sp.afterPropertiesSet(); - } - } - -} diff --git a/cas/src/test/java/org/springframework/security/cas/web/authentication/DefaultServiceAuthenticationDetailsTests.java b/cas/src/test/java/org/springframework/security/cas/web/authentication/DefaultServiceAuthenticationDetailsTests.java deleted file mode 100644 index 893d256775..0000000000 --- a/cas/src/test/java/org/springframework/security/cas/web/authentication/DefaultServiceAuthenticationDetailsTests.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2011-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.cas.web.authentication; - -import java.util.regex.Pattern; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.support.GenericXmlApplicationContext; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.security.cas.ServiceProperties; -import org.springframework.security.web.util.UrlUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Rob Winch - */ -public class DefaultServiceAuthenticationDetailsTests { - - private DefaultServiceAuthenticationDetails details; - - private MockHttpServletRequest request; - - private Pattern artifactPattern; - - private String casServiceUrl; - - private ConfigurableApplicationContext context; - - @BeforeEach - public void setUp() { - this.casServiceUrl = "https://localhost:8443/j_spring_security_cas"; - this.request = new MockHttpServletRequest(); - this.request.setScheme("https"); - this.request.setServerName("localhost"); - this.request.setServerPort(8443); - this.request.setRequestURI("/cas-sample/secure/"); - this.artifactPattern = DefaultServiceAuthenticationDetails - .createArtifactPattern(ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER); - } - - @AfterEach - public void cleanup() { - if (this.context != null) { - this.context.close(); - } - } - - @Test - public void getServiceUrlNullQuery() throws Exception { - this.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern); - assertThat(this.details.getServiceUrl()).isEqualTo(UrlUtils.buildFullRequestUrl(this.request)); - } - - @Test - public void getServiceUrlTicketOnlyParam() throws Exception { - this.request.setQueryString("ticket=123"); - this.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern); - String serviceUrl = this.details.getServiceUrl(); - this.request.setQueryString(null); - assertThat(serviceUrl).isEqualTo(UrlUtils.buildFullRequestUrl(this.request)); - } - - @Test - public void getServiceUrlTicketFirstMultiParam() throws Exception { - this.request.setQueryString("ticket=123&other=value"); - this.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern); - String serviceUrl = this.details.getServiceUrl(); - this.request.setQueryString("other=value"); - assertThat(serviceUrl).isEqualTo(UrlUtils.buildFullRequestUrl(this.request)); - } - - @Test - public void getServiceUrlTicketLastMultiParam() throws Exception { - this.request.setQueryString("other=value&ticket=123"); - this.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern); - String serviceUrl = this.details.getServiceUrl(); - this.request.setQueryString("other=value"); - assertThat(serviceUrl).isEqualTo(UrlUtils.buildFullRequestUrl(this.request)); - } - - @Test - public void getServiceUrlTicketMiddleMultiParam() throws Exception { - this.request.setQueryString("other=value&ticket=123&last=this"); - this.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern); - String serviceUrl = this.details.getServiceUrl(); - this.request.setQueryString("other=value&last=this"); - assertThat(serviceUrl).isEqualTo(UrlUtils.buildFullRequestUrl(this.request)); - } - - @Test - public void getServiceUrlDoesNotUseHostHeader() throws Exception { - this.casServiceUrl = "https://example.com/j_spring_security_cas"; - this.request.setServerName("evil.com"); - this.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern); - assertThat(this.details.getServiceUrl()).isEqualTo("https://example.com/cas-sample/secure/"); - } - - @Test - public void getServiceUrlDoesNotUseHostHeaderExplicit() { - this.casServiceUrl = "https://example.com/j_spring_security_cas"; - this.request.setServerName("evil.com"); - ServiceAuthenticationDetails details = loadServiceAuthenticationDetails( - "defaultserviceauthenticationdetails-explicit.xml"); - assertThat(details.getServiceUrl()).isEqualTo("https://example.com/cas-sample/secure/"); - } - - private ServiceAuthenticationDetails loadServiceAuthenticationDetails(String resourceName) { - this.context = new GenericXmlApplicationContext(getClass(), resourceName); - ServiceAuthenticationDetailsSource source = this.context.getBean(ServiceAuthenticationDetailsSource.class); - return source.buildDetails(this.request); - } - -} diff --git a/cas/src/test/resources/logback-test.xml b/cas/src/test/resources/logback-test.xml deleted file mode 100644 index 2d51ba4180..0000000000 --- a/cas/src/test/resources/logback-test.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - - - diff --git a/cas/src/test/resources/org/springframework/security/cas/web/authentication/defaultserviceauthenticationdetails-explicit.xml b/cas/src/test/resources/org/springframework/security/cas/web/authentication/defaultserviceauthenticationdetails-explicit.xml deleted file mode 100644 index c7d5346179..0000000000 --- a/cas/src/test/resources/org/springframework/security/cas/web/authentication/defaultserviceauthenticationdetails-explicit.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/cas/src/test/resources/org/springframework/security/cas/web/authentication/defaultserviceauthenticationdetails-passivity.xml b/cas/src/test/resources/org/springframework/security/cas/web/authentication/defaultserviceauthenticationdetails-passivity.xml deleted file mode 100644 index 0fe950ff2b..0000000000 --- a/cas/src/test/resources/org/springframework/security/cas/web/authentication/defaultserviceauthenticationdetails-passivity.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/config/spring-security-config.gradle b/config/spring-security-config.gradle index afd02853c1..fa04b5286d 100644 --- a/config/spring-security-config.gradle +++ b/config/spring-security-config.gradle @@ -42,7 +42,6 @@ dependencies { provided 'jakarta.servlet:jakarta.servlet-api' testImplementation project(':spring-security-aspects') - testImplementation project(':spring-security-cas') testImplementation project(':spring-security-test') testImplementation project(path : ':spring-security-core', configuration : 'tests') testImplementation project(path : ':spring-security-ldap', configuration : 'tests') diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/builders/HttpConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/builders/HttpConfigurationTests.java index 5afcf873fa..445dd6cc3b 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/builders/HttpConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/builders/HttpConfigurationTests.java @@ -20,8 +20,6 @@ import java.io.IOException; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -30,7 +28,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.cas.web.CasAuthenticationFilter; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @@ -41,9 +38,6 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.filter.OncePerRequestFilter; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -70,16 +64,6 @@ public class HttpConfigurationTests { + " Consider using addFilterBefore or addFilterAfter instead."); } - // https://github.com/spring-projects/spring-security-javaconfig/issues/104 - @Test - public void configureWhenAddFilterCasAuthenticationFilterThenFilterAdded() throws Exception { - CasAuthenticationFilterConfig.CAS_AUTHENTICATION_FILTER = spy(new CasAuthenticationFilter()); - this.spring.register(CasAuthenticationFilterConfig.class).autowire(); - this.mockMvc.perform(get("/")); - verify(CasAuthenticationFilterConfig.CAS_AUTHENTICATION_FILTER).doFilter(any(ServletRequest.class), - any(ServletResponse.class), any(FilterChain.class)); - } - @Test public void configureWhenConfigIsRequestMatchersJavadocThenAuthorizationApplied() throws Exception { this.spring.register(RequestMatcherRegistryConfigs.class).autowire(); @@ -121,21 +105,6 @@ public class HttpConfigurationTests { } - @EnableWebSecurity - static class CasAuthenticationFilterConfig extends WebSecurityConfigurerAdapter { - - static CasAuthenticationFilter CAS_AUTHENTICATION_FILTER; - - @Override - protected void configure(HttpSecurity http) { - // @formatter:off - http - .addFilter(CAS_AUTHENTICATION_FILTER); - // @formatter:on - } - - } - @EnableWebSecurity static class RequestMatcherRegistryConfigs extends WebSecurityConfigurerAdapter { diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index f08028dcdf..f174114a7f 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -42,7 +42,6 @@ *** xref:servlet/authentication/anonymous.adoc[Anonymous] *** xref:servlet/authentication/preauth.adoc[Pre-Authentication] *** xref:servlet/authentication/jaas.adoc[JAAS] -*** xref:servlet/authentication/cas.adoc[CAS] *** xref:servlet/authentication/x509.adoc[X509] *** xref:servlet/authentication/runas.adoc[Run-As] *** xref:servlet/authentication/logout.adoc[Logout] diff --git a/docs/modules/ROOT/pages/servlet/authentication/cas.adoc b/docs/modules/ROOT/pages/servlet/authentication/cas.adoc deleted file mode 100644 index 5b6dd7617e..0000000000 --- a/docs/modules/ROOT/pages/servlet/authentication/cas.adoc +++ /dev/null @@ -1,463 +0,0 @@ -[[servlet-cas]] -= CAS Authentication - -[[cas-overview]] -== Overview -JA-SIG produces an enterprise-wide single sign on system known as CAS. -Unlike other initiatives, JA-SIG's Central Authentication Service is open source, widely used, simple to understand, platform independent, and supports proxy capabilities. -Spring Security fully supports CAS, and provides an easy migration path from single-application deployments of Spring Security through to multiple-application deployments secured by an enterprise-wide CAS server. - -You can learn more about CAS at https://www.apereo.org. -You will also need to visit this site to download the CAS Server files. - -[[cas-how-it-works]] -== How CAS Works -Whilst the CAS web site contains documents that detail the architecture of CAS, we present the general overview again here within the context of Spring Security. -Spring Security 3.x supports CAS 3. -At the time of writing, the CAS server was at version 3.4. - -Somewhere in your enterprise you will need to setup a CAS server. -The CAS server is simply a standard WAR file, so there isn't anything difficult about setting up your server. -Inside the WAR file you will customise the login and other single sign on pages displayed to users. - -When deploying a CAS 3.4 server, you will also need to specify an `AuthenticationHandler` in the `deployerConfigContext.xml` included with CAS. -The `AuthenticationHandler` has a simple method that returns a boolean as to whether a given set of Credentials is valid. -Your `AuthenticationHandler` implementation will need to link into some type of backend authentication repository, such as an LDAP server or database. -CAS itself includes numerous ``AuthenticationHandler``s out of the box to assist with this. -When you download and deploy the server war file, it is set up to successfully authenticate users who enter a password matching their username, which is useful for testing. - -Apart from the CAS server itself, the other key players are of course the secure web applications deployed throughout your enterprise. -These web applications are known as "services". -There are three types of services. -Those that authenticate service tickets, those that can obtain proxy tickets, and those that authenticate proxy tickets. -Authenticating a proxy ticket differs because the list of proxies must be validated and often times a proxy ticket can be reused. - - -[[cas-sequence]] -=== Spring Security and CAS Interaction Sequence -The basic interaction between a web browser, CAS server and a Spring Security-secured service is as follows: - -* The web user is browsing the service's public pages. -CAS or Spring Security is not involved. -* The user eventually requests a page that is either secure or one of the beans it uses is secure. -Spring Security's `ExceptionTranslationFilter` will detect the `AccessDeniedException` or `AuthenticationException`. -* Because the user's `Authentication` object (or lack thereof) caused an `AuthenticationException`, the `ExceptionTranslationFilter` will call the configured `AuthenticationEntryPoint`. -If using CAS, this will be the `CasAuthenticationEntryPoint` class. -* The `CasAuthenticationEntryPoint` will redirect the user's browser to the CAS server. -It will also indicate a `service` parameter, which is the callback URL for the Spring Security service (your application). -For example, the URL to which the browser is redirected might be https://my.company.com/cas/login?service=https%3A%2F%2Fserver3.company.com%2Fwebapp%2Flogin/cas. -* After the user's browser redirects to CAS, they will be prompted for their username and password. -If the user presents a session cookie which indicates they've previously logged on, they will not be prompted to login again (there is an exception to this procedure, which we'll cover later). -CAS will use the `PasswordHandler` (or `AuthenticationHandler` if using CAS 3.0) discussed above to decide whether the username and password is valid. -* Upon successful login, CAS will redirect the user's browser back to the original service. -It will also include a `ticket` parameter, which is an opaque string representing the "service ticket". -Continuing our earlier example, the URL the browser is redirected to might be https://server3.company.com/webapp/login/cas?ticket=ST-0-ER94xMJmn6pha35CQRoZ. -* Back in the service web application, the `CasAuthenticationFilter` is always listening for requests to `/login/cas` (this is configurable, but we'll use the defaults in this introduction). -The processing filter will construct a `UsernamePasswordAuthenticationToken` representing the service ticket. -The principal will be equal to `CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER`, whilst the credentials will be the service ticket opaque value. -This authentication request will then be handed to the configured `AuthenticationManager`. -* The `AuthenticationManager` implementation will be the `ProviderManager`, which is in turn configured with the `CasAuthenticationProvider`. -The `CasAuthenticationProvider` only responds to ``UsernamePasswordAuthenticationToken``s containing the CAS-specific principal (such as `CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER`) and ``CasAuthenticationToken``s (discussed later). -* `CasAuthenticationProvider` will validate the service ticket using a `TicketValidator` implementation. -This will typically be a `Cas20ServiceTicketValidator` which is one of the classes included in the CAS client library. -In the event the application needs to validate proxy tickets, the `Cas20ProxyTicketValidator` is used. -The `TicketValidator` makes an HTTPS request to the CAS server in order to validate the service ticket. -It may also include a proxy callback URL, which is included in this example: https://my.company.com/cas/proxyValidate?service=https%3A%2F%2Fserver3.company.com%2Fwebapp%2Flogin/cas&ticket=ST-0-ER94xMJmn6pha35CQRoZ&pgtUrl=https://server3.company.com/webapp/login/cas/proxyreceptor. -* Back on the CAS server, the validation request will be received. -If the presented service ticket matches the service URL the ticket was issued to, CAS will provide an affirmative response in XML indicating the username. -If any proxy was involved in the authentication (discussed below), the list of proxies is also included in the XML response. -* [OPTIONAL] If the request to the CAS validation service included the proxy callback URL (in the `pgtUrl` parameter), CAS will include a `pgtIou` string in the XML response. -This `pgtIou` represents a proxy-granting ticket IOU. -The CAS server will then create its own HTTPS connection back to the `pgtUrl`. -This is to mutually authenticate the CAS server and the claimed service URL. -The HTTPS connection will be used to send a proxy granting ticket to the original web application. -For example, https://server3.company.com/webapp/login/cas/proxyreceptor?pgtIou=PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt&pgtId=PGT-1-si9YkkHLrtACBo64rmsi3v2nf7cpCResXg5MpESZFArbaZiOKH. -* The `Cas20TicketValidator` will parse the XML received from the CAS server. -It will return to the `CasAuthenticationProvider` a `TicketResponse`, which includes the username (mandatory), proxy list (if any were involved), and proxy-granting ticket IOU (if the proxy callback was requested). -* Next `CasAuthenticationProvider` will call a configured `CasProxyDecider`. -The `CasProxyDecider` indicates whether the proxy list in the `TicketResponse` is acceptable to the service. -Several implementations are provided with Spring Security: `RejectProxyTickets`, `AcceptAnyCasProxy` and `NamedCasProxyDecider`. -These names are largely self-explanatory, except `NamedCasProxyDecider` which allows a `List` of trusted proxies to be provided. -* `CasAuthenticationProvider` will next request a `AuthenticationUserDetailsService` to load the `GrantedAuthority` objects that apply to the user contained in the `Assertion`. -* If there were no problems, `CasAuthenticationProvider` constructs a `CasAuthenticationToken` including the details contained in the `TicketResponse` and the ``GrantedAuthority``s. -* Control then returns to `CasAuthenticationFilter`, which places the created `CasAuthenticationToken` in the security context. -* The user's browser is redirected to the original page that caused the `AuthenticationException` (or a custom destination depending on the configuration). - -It's good that you're still here! -Let's now look at how this is configured - -[[cas-client]] -== Configuration of CAS Client -The web application side of CAS is made easy due to Spring Security. -It is assumed you already know the basics of using Spring Security, so these are not covered again below. -We'll assume a namespace based configuration is being used and add in the CAS beans as required. -Each section builds upon the previous section. -A full CAS sample application can be found in the Spring Security xref:samples.adoc#samples[Samples]. - - -[[cas-st]] -=== Service Ticket Authentication -This section describes how to setup Spring Security to authenticate Service Tickets. -Often times this is all a web application requires. -You will need to add a `ServiceProperties` bean to your application context. -This represents your CAS service: - -[source,xml] ----- - - - - ----- - -The `service` must equal a URL that will be monitored by the `CasAuthenticationFilter`. -The `sendRenew` defaults to false, but should be set to true if your application is particularly sensitive. -What this parameter does is tell the CAS login service that a single sign on login is unacceptable. -Instead, the user will need to re-enter their username and password in order to gain access to the service. - -The following beans should be configured to commence the CAS authentication process (assuming you're using a namespace configuration): - -[source,xml] ----- - -... - - - - - - - - - - - ----- - -For CAS to operate, the `ExceptionTranslationFilter` must have its `authenticationEntryPoint` property set to the `CasAuthenticationEntryPoint` bean. -This can easily be done using xref:servlet/appendix/namespace.adoc#nsa-http-entry-point-ref[entry-point-ref] as is done in the example above. -The `CasAuthenticationEntryPoint` must refer to the `ServiceProperties` bean (discussed above), which provides the URL to the enterprise's CAS login server. -This is where the user's browser will be redirected. - -The `CasAuthenticationFilter` has very similar properties to the `UsernamePasswordAuthenticationFilter` (used for form-based logins). -You can use these properties to customize things like behavior for authentication success and failure. - -Next you need to add a `CasAuthenticationProvider` and its collaborators: - -[source,xml,attrs="-attributes"] ----- - - - - - - - - - - - - - - - - - - - - - - -... - ----- - -The `CasAuthenticationProvider` uses a `UserDetailsService` instance to load the authorities for a user, once they have been authenticated by CAS. -We've shown a simple in-memory setup here. -Note that the `CasAuthenticationProvider` does not actually use the password for authentication, but it does use the authorities. - -The beans are all reasonably self-explanatory if you refer back to the <> section. - -This completes the most basic configuration for CAS. -If you haven't made any mistakes, your web application should happily work within the framework of CAS single sign on. -No other parts of Spring Security need to be concerned about the fact CAS handled authentication. -In the following sections we will discuss some (optional) more advanced configurations. - - -[[cas-singlelogout]] -=== Single Logout -The CAS protocol supports Single Logout and can be easily added to your Spring Security configuration. -Below are updates to the Spring Security configuration that handle Single Logout - -[source,xml] ----- - -... - - - - - - - - - - - - - - - - ----- - -The `logout` element logs the user out of the local application, but does not end the session with the CAS server or any other applications that have been logged into. -The `requestSingleLogoutFilter` filter will allow the URL of `/spring_security_cas_logout` to be requested to redirect the application to the configured CAS Server logout URL. -Then the CAS Server will send a Single Logout request to all the services that were signed into. -The `singleLogoutFilter` handles the Single Logout request by looking up the `HttpSession` in a static `Map` and then invalidating it. - -It might be confusing why both the `logout` element and the `singleLogoutFilter` are needed. -It is considered best practice to logout locally first since the `SingleSignOutFilter` just stores the `HttpSession` in a static `Map` in order to call invalidate on it. -With the configuration above, the flow of logout would be: - -* The user requests `/logout` which would log the user out of the local application and send the user to the logout success page. -* The logout success page, `/cas-logout.jsp`, should instruct the user to click a link pointing to `/logout/cas` in order to logout out of all applications. -* When the user clicks the link, the user is redirected to the CAS single logout URL (https://localhost:9443/cas/logout). -* On the CAS Server side, the CAS single logout URL then submits single logout requests to all the CAS Services. -On the CAS Service side, JASIG's `SingleSignOutFilter` processes the logout request by invalidating the original session. - - - -The next step is to add the following to your web.xml - -[source,xml] ----- - -characterEncodingFilter - - org.springframework.web.filter.CharacterEncodingFilter - - - encoding - UTF-8 - - - -characterEncodingFilter -/* - - - - org.jasig.cas.client.session.SingleSignOutHttpSessionListener - - ----- - -When using the SingleSignOutFilter you might encounter some encoding issues. -Therefore it is recommended to add the `CharacterEncodingFilter` to ensure that the character encoding is correct when using the `SingleSignOutFilter`. -Again, refer to JASIG's documentation for details. -The `SingleSignOutHttpSessionListener` ensures that when an `HttpSession` expires, the mapping used for single logout is removed. - - -[[cas-pt-client]] -=== Authenticating to a Stateless Service with CAS -This section describes how to authenticate to a service using CAS. -In other words, this section discusses how to setup a client that uses a service that authenticates with CAS. -The next section describes how to setup a stateless service to Authenticate using CAS. - - -[[cas-pt-client-config]] -==== Configuring CAS to Obtain Proxy Granting Tickets -In order to authenticate to a stateless service, the application needs to obtain a proxy granting ticket (PGT). -This section describes how to configure Spring Security to obtain a PGT building upon thencas-st[Service Ticket Authentication] configuration. - -The first step is to include a `ProxyGrantingTicketStorage` in your Spring Security configuration. -This is used to store PGT's that are obtained by the `CasAuthenticationFilter` so that they can be used to obtain proxy tickets. -An example configuration is shown below - -[source,xml] ----- - - ----- - -The next step is to update the `CasAuthenticationProvider` to be able to obtain proxy tickets. -To do this replace the `Cas20ServiceTicketValidator` with a `Cas20ProxyTicketValidator`. -The `proxyCallbackUrl` should be set to a URL that the application will receive PGT's at. -Last, the configuration should also reference the `ProxyGrantingTicketStorage` so it can use a PGT to obtain proxy tickets. -You can find an example of the configuration changes that should be made below. - -[source,xml] ----- - -... - - - - - - - - ----- - -The last step is to update the `CasAuthenticationFilter` to accept PGT and to store them in the `ProxyGrantingTicketStorage`. -It is important the `proxyReceptorUrl` matches the `proxyCallbackUrl` of the `Cas20ProxyTicketValidator`. -An example configuration is shown below. - -[source,xml] ----- - - - ... - - - - ----- - -[[cas-pt-client-sample]] -==== Calling a Stateless Service Using a Proxy Ticket -Now that Spring Security obtains PGTs, you can use them to create proxy tickets which can be used to authenticate to a stateless service. -The CAS xref:samples.adoc#samples[sample application] contains a working example in the `ProxyTicketSampleServlet`. -Example code can be found below: - -==== -.Java -[source,java,role="primary"] ----- -protected void doGet(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { -// NOTE: The CasAuthenticationToken can also be obtained using -// SecurityContextHolder.getContext().getAuthentication() -final CasAuthenticationToken token = (CasAuthenticationToken) request.getUserPrincipal(); -// proxyTicket could be reused to make calls to the CAS service even if the -// target url differs -final String proxyTicket = token.getAssertion().getPrincipal().getProxyTicketFor(targetUrl); - -// Make a remote call using the proxy ticket -final String serviceUrl = targetUrl+"?ticket="+URLEncoder.encode(proxyTicket, "UTF-8"); -String proxyResponse = CommonUtils.getResponseFromServer(serviceUrl, "UTF-8"); -... -} ----- - -.Kotlin -[source,kotlin,role="secondary"] ----- -protected fun doGet(request: HttpServletRequest, response: HttpServletResponse?) { - // NOTE: The CasAuthenticationToken can also be obtained using - // SecurityContextHolder.getContext().getAuthentication() - val token = request.userPrincipal as CasAuthenticationToken - // proxyTicket could be reused to make calls to the CAS service even if the - // target url differs - val proxyTicket = token.assertion.principal.getProxyTicketFor(targetUrl) - - // Make a remote call using the proxy ticket - val serviceUrl: String = targetUrl + "?ticket=" + URLEncoder.encode(proxyTicket, "UTF-8") - val proxyResponse = CommonUtils.getResponseFromServer(serviceUrl, "UTF-8") -} ----- -==== - -[[cas-pt]] -=== Proxy Ticket Authentication -The `CasAuthenticationProvider` distinguishes between stateful and stateless clients. -A stateful client is considered any that submits to the `filterProcessUrl` of the `CasAuthenticationFilter`. -A stateless client is any that presents an authentication request to `CasAuthenticationFilter` on a URL other than the `filterProcessUrl`. - -Because remoting protocols have no way of presenting themselves within the context of an `HttpSession`, it isn't possible to rely on the default practice of storing the security context in the session between requests. -Furthermore, because the CAS server invalidates a ticket after it has been validated by the `TicketValidator`, presenting the same proxy ticket on subsequent requests will not work. - -One obvious option is to not use CAS at all for remoting protocol clients. -However, this would eliminate many of the desirable features of CAS. -As a middle-ground, the `CasAuthenticationProvider` uses a `StatelessTicketCache`. -This is used solely for stateless clients which use a principal equal to `CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER`. -What happens is the `CasAuthenticationProvider` will store the resulting `CasAuthenticationToken` in the `StatelessTicketCache`, keyed on the proxy ticket. -Accordingly, remoting protocol clients can present the same proxy ticket and the `CasAuthenticationProvider` will not need to contact the CAS server for validation (aside from the first request). -Once authenticated, the proxy ticket could be used for URLs other than the original target service. - -This section builds upon the previous sections to accommodate proxy ticket authentication. -The first step is to specify to authenticate all artifacts as shown below. - -[source,xml] ----- - -... - - ----- - -The next step is to specify `serviceProperties` and the `authenticationDetailsSource` for the `CasAuthenticationFilter`. -The `serviceProperties` property instructs the `CasAuthenticationFilter` to attempt to authenticate all artifacts instead of only ones present on the `filterProcessUrl`. -The `ServiceAuthenticationDetailsSource` creates a `ServiceAuthenticationDetails` that ensures the current URL, based upon the `HttpServletRequest`, is used as the service URL when validating the ticket. -The method for generating the service URL can be customized by injecting a custom `AuthenticationDetailsSource` that returns a custom `ServiceAuthenticationDetails`. - -[source,xml] ----- - -... - - - - - - - ----- - -You will also need to update the `CasAuthenticationProvider` to handle proxy tickets. -To do this replace the `Cas20ServiceTicketValidator` with a `Cas20ProxyTicketValidator`. -You will need to configure the `statelessTicketCache` and which proxies you want to accept. -You can find an example of the updates required to accept all proxies below. - -[source,xml] ----- - - -... - - - - - - - - - - - - - - - - - - - - - ----- diff --git a/docs/modules/ROOT/pages/servlet/authentication/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/index.adoc index ceb08df6cb..6170211e3d 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/index.adoc @@ -16,7 +16,6 @@ These sections focus on specific ways you may want to authenticate and point bac * xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd[Username and Password] - how to authenticate with a username/password * xref:servlet/oauth2/oauth2-login.adoc#oauth2login[OAuth 2.0 Login] - OAuth 2.0 Log In with OpenID Connect and non-standard OAuth 2.0 Login (i.e. GitHub) * xref:servlet/saml2/index.adoc#servlet-saml2[SAML 2.0 Login] - SAML 2.0 Log In -* xref:servlet/authentication/cas.adoc#servlet-cas[Central Authentication Server (CAS)] - Central Authentication Server (CAS) Support * xref:servlet/authentication/rememberme.adoc#servlet-rememberme[Remember Me] - how to remember a user past session expiration * xref:servlet/authentication/jaas.adoc#servlet-jaas[JAAS Authentication] - authenticate with JAAS * xref:servlet/authentication/openid.adoc#servlet-openid[OpenID] - OpenID Authentication (not to be confused with OpenID Connect) diff --git a/docs/modules/ROOT/pages/servlet/authentication/logout.adoc b/docs/modules/ROOT/pages/servlet/authentication/logout.adoc index 909f1c3d75..7c611c9c8e 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/logout.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/logout.adoc @@ -144,5 +144,4 @@ If not configured a status code 200 will be returned by default. - xref:servlet/integrations/servlet-api.adoc#servletapi-logout[ HttpServletRequest.logout()] - xref:servlet/authentication/rememberme.adoc#remember-me-impls[Remember-Me Interfaces and Implementations] - xref:servlet/exploits/csrf.adoc#servlet-considerations-csrf-logout[ Logging Out] in section CSRF Caveats -- Section xref:servlet/authentication/cas.adoc#cas-singlelogout[ Single Logout] (CAS protocol) - Documentation for the xref:servlet/appendix/namespace.adoc#nsa-logout[ logout element] in the Spring Security XML Namespace section