package org.apereo.cas.support.saml.authentication.principal; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.io.FileUtils; import org.apereo.cas.authentication.CoreAuthenticationTestUtils; import org.apereo.cas.authentication.principal.DefaultResponse; import org.apereo.cas.authentication.principal.Response; import org.apereo.cas.authentication.principal.ResponseBuilder; import org.apereo.cas.authentication.principal.Service; import org.apereo.cas.authentication.principal.ServiceFactory; import org.apereo.cas.config.CasCoreAuthenticationConfiguration; import org.apereo.cas.config.CasCoreAuthenticationHandlersConfiguration; import org.apereo.cas.config.CasCoreAuthenticationMetadataConfiguration; import org.apereo.cas.config.CasCoreAuthenticationPolicyConfiguration; import org.apereo.cas.config.CasCoreAuthenticationPrincipalConfiguration; import org.apereo.cas.config.CasCoreAuthenticationServiceSelectionStrategyConfiguration; import org.apereo.cas.config.CasCoreAuthenticationSupportConfiguration; import org.apereo.cas.config.CasCoreConfiguration; import org.apereo.cas.config.CasCoreHttpConfiguration; import org.apereo.cas.config.CasCoreServicesConfiguration; import org.apereo.cas.config.CasCoreTicketIdGeneratorsConfiguration; import org.apereo.cas.config.CasCoreTicketsConfiguration; import org.apereo.cas.config.CasCoreUtilConfiguration; import org.apereo.cas.config.CasCoreWebConfiguration; import org.apereo.cas.config.CasDefaultServiceTicketIdGeneratorsConfiguration; import org.apereo.cas.config.CasPersonDirectoryConfiguration; import org.apereo.cas.config.CasCoreTicketCatalogConfiguration; import org.apereo.cas.config.CoreSamlConfiguration; import org.apereo.cas.config.support.CasWebApplicationServiceFactoryConfiguration; import org.apereo.cas.config.support.EnvironmentConversionServiceInitializer; import org.apereo.cas.logout.config.CasCoreLogoutConfiguration; import org.apereo.cas.services.DefaultRegisteredServiceUsernameProvider; import org.apereo.cas.services.RegisteredService; import org.apereo.cas.services.ServicesManager; import org.apereo.cas.support.saml.AbstractOpenSamlTests; import org.apereo.cas.support.saml.SamlProtocolConstants; import org.apereo.cas.support.saml.config.SamlGoogleAppsConfiguration; import org.apereo.cas.util.CompressionUtils; import org.apereo.cas.util.SchedulingUtils; import org.apereo.cas.validation.config.CasCoreValidationConfiguration; import org.apereo.cas.web.config.CasCookieConfiguration; import org.apereo.cas.web.config.CasProtocolViewsConfiguration; import org.apereo.cas.web.config.CasValidationConfiguration; import org.apereo.cas.web.flow.config.CasCoreWebflowConfiguration; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.PostConstruct; import java.io.File; import java.io.IOException; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.junit.Assert.*; import static org.mockito.Mockito.*; /** * @author Scott Battaglia * @since 3.1 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = { GoogleAccountsServiceTests.CasTestConfiguration.class, SamlGoogleAppsConfiguration.class, CasCoreAuthenticationConfiguration.class, CasCoreAuthenticationPolicyConfiguration.class, CasCoreAuthenticationPrincipalConfiguration.class, CasCoreAuthenticationMetadataConfiguration.class, CasCoreAuthenticationSupportConfiguration.class, CasCoreAuthenticationHandlersConfiguration.class, CasDefaultServiceTicketIdGeneratorsConfiguration.class, CasCoreTicketIdGeneratorsConfiguration.class, CasWebApplicationServiceFactoryConfiguration.class, CasCoreHttpConfiguration.class, CasCoreServicesConfiguration.class, CoreSamlConfiguration.class, CasCoreWebConfiguration.class, CasCoreWebflowConfiguration.class, RefreshAutoConfiguration.class, AopAutoConfiguration.class, CasCookieConfiguration.class, CasCoreAuthenticationConfiguration.class, CasCoreTicketsConfiguration.class, CasCoreLogoutConfiguration.class, CasValidationConfiguration.class, CasProtocolViewsConfiguration.class, CasCoreValidationConfiguration.class, CasCoreConfiguration.class, CasCoreAuthenticationServiceSelectionStrategyConfiguration.class, CasPersonDirectoryConfiguration.class, CasCoreTicketCatalogConfiguration.class, CasCoreUtilConfiguration.class}) @TestPropertySource(locations = "classpath:/gapps.properties") @ContextConfiguration(initializers = EnvironmentConversionServiceInitializer.class) public class GoogleAccountsServiceTests extends AbstractOpenSamlTests { private static final File FILE = new File(FileUtils.getTempDirectoryPath(), "service.json"); private static final ObjectMapper MAPPER = new ObjectMapper(); @Autowired @Qualifier("googleAccountsServiceFactory") private ServiceFactory factory; @Autowired @Qualifier("googleAccountsServiceResponseBuilder") private ResponseBuilder<GoogleAccountsService> googleAccountsServiceResponseBuilder; private GoogleAccountsService googleAccountsService; @TestConfiguration public static class CasTestConfiguration { @Autowired protected ApplicationContext applicationContext; @PostConstruct public void init() { SchedulingUtils.prepScheduledAnnotationBeanPostProcessor(applicationContext); } } public GoogleAccountsService getGoogleAccountsService() throws Exception { final MockHttpServletRequest request = new MockHttpServletRequest(); final String samlRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<samlp:AuthnRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" " + "ID=\"5545454455\" Version=\"2.0\" IssueInstant=\"Value\" " + "ProtocolBinding=\"urn:oasis:names.tc:SAML:2.0:bindings:HTTP-Redirect\" " + "ProviderName=\"https://localhost:8443/myRutgers\" AssertionConsumerServiceURL=\"https://localhost:8443/myRutgers\"/>"; request.setParameter(SamlProtocolConstants.PARAMETER_SAML_REQUEST, encodeMessage(samlRequest)); request.setParameter(SamlProtocolConstants.PARAMETER_SAML_RELAY_STATE, "RelayStateAddedHere"); final RegisteredService regSvc = mock(RegisteredService.class); when(regSvc.getUsernameAttributeProvider()).thenReturn(new DefaultRegisteredServiceUsernameProvider()); final ServicesManager servicesManager = mock(ServicesManager.class); when(servicesManager.findServiceBy(any(Service.class))).thenReturn(regSvc); return (GoogleAccountsService) factory.createService(request); } @Before public void setUp() throws Exception { this.googleAccountsService = getGoogleAccountsService(); this.googleAccountsService.setPrincipal(CoreAuthenticationTestUtils.getPrincipal()); } @Test public void verifyResponse() { final Response resp = googleAccountsServiceResponseBuilder.build(googleAccountsService, "SAMPLE_TICKET"); assertEquals(resp.getResponseType(), DefaultResponse.ResponseType.POST); final String response = resp.getAttributes().get(SamlProtocolConstants.PARAMETER_SAML_RESPONSE); assertNotNull(response); assertTrue(response.contains("NotOnOrAfter")); final Pattern pattern = Pattern.compile("NotOnOrAfter\\s*=\\s*\"(.+Z)\""); final Matcher matcher = pattern.matcher(response); final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC); while (matcher.find()) { final String onOrAfter = matcher.group(1); final ZonedDateTime dt = ZonedDateTime.parse(onOrAfter); assertTrue(dt.isAfter(now)); } assertTrue(resp.getAttributes().containsKey(SamlProtocolConstants.PARAMETER_SAML_RELAY_STATE)); } private static String encodeMessage(final String xmlString) throws IOException { return CompressionUtils.deflate(xmlString); } @Test public void serializeGoogleAccountService() throws Exception { final GoogleAccountsService service = getGoogleAccountsService(); MAPPER.writeValue(FILE, service); final GoogleAccountsService service2 = MAPPER.readValue(FILE, GoogleAccountsService.class); assertEquals(service, service2); } }