/* * ***************************************************************************** * Cloud Foundry * Copyright (c) [2009-2016] Pivotal Software, Inc. All Rights Reserved. * * This product is licensed to you under the Apache License, Version 2.0 (the "License"). * You may not use this product except in compliance with the License. * * This product includes a number of subcomponents with * separate copyright notices and license terms. Your use of these * subcomponents is subject to the terms and conditions of the * subcomponent's license, as noted in the LICENSE file. * ***************************************************************************** */ package org.cloudfoundry.identity.uaa.oauth.token; import org.cloudfoundry.identity.uaa.authentication.UaaAuthentication; import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal; import org.cloudfoundry.identity.uaa.constants.OriginKeys; import org.cloudfoundry.identity.uaa.oauth.DisallowedIdpException; import org.cloudfoundry.identity.uaa.oauth.UaaOauth2Authentication; import org.cloudfoundry.identity.uaa.oauth.client.ClientConstants; import org.cloudfoundry.identity.uaa.user.UaaUser; import org.cloudfoundry.identity.uaa.user.UaaUserDatabase; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.opensaml.Configuration; import org.opensaml.DefaultBootstrap; import org.opensaml.saml2.core.Assertion; import org.opensaml.saml2.core.impl.AssertionMarshaller; import org.opensaml.saml2.metadata.EntityDescriptor; import org.opensaml.xml.ConfigurationException; import org.opensaml.xml.XMLObject; import org.opensaml.xml.io.Unmarshaller; import org.opensaml.xml.io.UnmarshallerFactory; import org.opensaml.xml.io.UnmarshallingException; import org.opensaml.xml.parse.BasicParserPool; import org.opensaml.xml.parse.XMLParserException; import org.opensaml.xml.util.XMLHelper; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken; import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; import org.springframework.security.oauth2.common.exceptions.UnauthorizedClientException; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.OAuth2RequestFactory; import org.springframework.security.oauth2.provider.TokenRequest; import org.springframework.security.oauth2.provider.client.BaseClientDetails; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.security.saml.SAMLAuthenticationToken; import org.springframework.security.saml.context.SAMLMessageContext; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.util.StringUtils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.w3c.dom.Document; import org.w3c.dom.Element; import java.io.ByteArrayInputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.security.Security; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.JTI; import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.GRANT_TYPE_SAML2_BEARER; import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.USER_TOKEN_REQUESTING_CLIENT_ID; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.springframework.security.oauth2.common.util.OAuth2Utils.CLIENT_ID; import static org.springframework.security.oauth2.common.util.OAuth2Utils.GRANT_TYPE; public class Saml2TokenGranterTest { private Saml2TokenGranter granter; private Saml2TokenGranter mockedgranter; private AuthorizationServerTokenServices tokenServices; private ClientDetailsService clientDetailsService; private OAuth2RequestFactory requestFactory; private UaaOauth2Authentication authentication; private TokenRequest tokenRequest; private UaaAuthentication userAuthentication; private Map<String,String> requestParameters; private BaseClientDetails requestingClient; private BaseClientDetails receivingClient; private BaseClientDetails passwordClient; private SAMLAuthenticationToken samltoken; private SAMLMessageContext samlcontext; private UaaUserDatabase uaaUserDatabase = mock(UaaUserDatabase.class); @Before public void setup() { try { DefaultBootstrap.bootstrap(); } catch (ConfigurationException e) { } tokenServices = mock(AuthorizationServerTokenServices.class); clientDetailsService = mock(ClientDetailsService.class); requestFactory = mock(OAuth2RequestFactory.class); authentication = mock(UaaOauth2Authentication.class); samlcontext = mock(SAMLMessageContext.class); MockHttpServletRequest request = new MockHttpServletRequest(); ServletRequestAttributes attrs = new ServletRequestAttributes(request); RequestContextHolder.setRequestAttributes(attrs); Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); userAuthentication = mock(UaaAuthentication.class); granter = new Saml2TokenGranter( tokenServices, clientDetailsService, requestFactory); samltoken = new SAMLAuthenticationToken(samlcontext); SecurityContextHolder.getContext().setAuthentication(authentication); requestingClient = new BaseClientDetails("requestingId",null,"uaa.user",GRANT_TYPE_SAML2_BEARER, null); receivingClient = new BaseClientDetails("receivingId",null,"test.scope",GRANT_TYPE_SAML2_BEARER, null); passwordClient = new BaseClientDetails("pwdId",null,"test.scope","password", null); when(clientDetailsService.loadClientByClientId(eq(requestingClient.getClientId()))).thenReturn(requestingClient); when(clientDetailsService.loadClientByClientId(eq(receivingClient.getClientId()))).thenReturn(receivingClient); requestParameters = new HashMap<>(); requestParameters.put(USER_TOKEN_REQUESTING_CLIENT_ID, requestingClient.getClientId()); requestParameters.put(GRANT_TYPE, GRANT_TYPE_SAML2_BEARER); requestParameters.put(CLIENT_ID, receivingClient.getClientId()); tokenRequest = new PublicTokenRequest(); tokenRequest.setRequestParameters(requestParameters); } @After public void teardown() { SecurityContextHolder.clearContext(); } @Test public void test_not_authenticated() throws Exception { when(authentication.isAuthenticated()).thenReturn(false); granter.validateRequest(tokenRequest); } @Test public void test_not_a_user_authentication() throws Exception { when(authentication.isAuthenticated()).thenReturn(true); when(authentication.getUserAuthentication()).thenReturn(null); granter.validateRequest(tokenRequest); } @Test(expected = InvalidGrantException.class) public void test_no_grant_type() throws Exception { missing_parameter(GRANT_TYPE); } @Test public void test_ensure_that_access_token_is_deleted_and_modified() { String tokenId = "access_token"; DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(tokenId); DefaultOAuth2RefreshToken refreshToken = new DefaultOAuth2RefreshToken("refresh_token"); Map<String,Object> info = new HashMap(token.getAdditionalInformation()); info.put(JTI, token.getValue()); token.setAdditionalInformation(info); token.setRefreshToken(refreshToken); token.setExpiration(new Date()); } @Test public void test_getAccessToken() { Collection me = AuthorityUtils.commaSeparatedStringToAuthorityList("openid,foo.bar,uaa.user,one.read"); OAuth2Request myReq = new OAuth2Request(requestParameters, receivingClient.getClientId(), receivingClient.getAuthorities(), true, receivingClient.getScope(), receivingClient.getResourceIds(), null, null, null); requestingClient.setScope(StringUtils.commaDelimitedListToSet("openid,foo.bar")); when(userAuthentication.getAuthorities()).thenReturn(me); tokenRequest.setClientId(receivingClient.getClientId()); when(authentication.isAuthenticated()).thenReturn(true); when(authentication.getUserAuthentication()).thenReturn(null); when(authentication.getUserAuthentication()).thenReturn(userAuthentication); when(userAuthentication.isAuthenticated()).thenReturn(true); when(requestFactory.createOAuth2Request(receivingClient, tokenRequest)).thenReturn(myReq); ReflectionTestUtils.setField(granter, "requestFactory", requestFactory); granter.getAccessToken(receivingClient, tokenRequest); } @Test public void test_grant() { tokenRequest.setGrantType(requestParameters.get(GRANT_TYPE)); granter.grant(GRANT_TYPE, tokenRequest); } @Test(expected = UnauthorizedClientException.class) @Ignore public void test_oauth2_authentication_with_invalid_allowed_provider() { OAuth2Request myReq = new OAuth2Request(requestParameters, receivingClient.getClientId(), receivingClient.getAuthorities(), true, receivingClient.getScope(), receivingClient.getResourceIds(), null, null, null); UaaUser user = new UaaUser("testid", "testuser","","test@test.org",AuthorityUtils.commaSeparatedStringToAuthorityList("foo.bar,spam.baz,space.1.developer,space.2.developer,space.1.admin"),"givenname", "familyname", null, null, OriginKeys.UAA, null, true, IdentityZone.getUaa().getId(), "testid", new Date()); UaaPrincipal uaaPrincipal = new UaaPrincipal(user); when(uaaUserDatabase.retrieveUserById(anyString())).thenReturn(user); BaseClientDetails myClient = new BaseClientDetails(requestingClient); List<String> allowedProviders = new LinkedList<String>(); allowedProviders.add("anyIDP"); Map<String, Object> additionalInformation = new LinkedHashMap<String, Object>(); Collection me = AuthorityUtils.commaSeparatedStringToAuthorityList("openid,foo.bar,uaa.user,one.read"); //when(new DefaultSecurityContextAccessor()).thenReturn((DefaultSecurityContextAccessor) securityContextAccessor); mockedgranter = mock(Saml2TokenGranter.class); when(mockedgranter.validateRequest(tokenRequest)).thenReturn(userAuthentication); when(mockedgranter.getOAuth2Authentication((ClientDetails)myClient, (TokenRequest)tokenRequest)).thenCallRealMethod(); myClient.setScope(StringUtils.commaDelimitedListToSet("openid,foo.bar")); additionalInformation.put(ClientConstants.ALLOWED_PROVIDERS, allowedProviders); myClient.setAdditionalInformation(additionalInformation); when(userAuthentication.getAuthorities()).thenReturn(me); when(requestFactory.createOAuth2Request(receivingClient, tokenRequest)).thenReturn(myReq); when(userAuthentication.getPrincipal()).thenReturn(uaaPrincipal); when(mockedgranter.getRequestFactory()).thenReturn(requestFactory); mockedgranter.getOAuth2Authentication(myClient, tokenRequest); } @Test(expected = DisallowedIdpException.class) @Ignore public void test_oauth2_authentication_with_disallowed_provider() { OAuth2Request myReq = new OAuth2Request(requestParameters, receivingClient.getClientId(), receivingClient.getAuthorities(), true, receivingClient.getScope(), receivingClient.getResourceIds(), null, null, null); UaaUser user = new UaaUser("testid", "testuser","","test@test.org",AuthorityUtils.commaSeparatedStringToAuthorityList("foo.bar,spam.baz,space.1.developer,space.2.developer,space.1.admin"),"givenname", "familyname", null, null, OriginKeys.UAA, null, true, IdentityZone.getUaa().getId(), "testid", new Date()); UaaPrincipal uaaPrincipal = new UaaPrincipal(user); when(uaaUserDatabase.retrieveUserById(anyString())).thenReturn(user); BaseClientDetails myClient = new BaseClientDetails(requestingClient); List<String> allowedProviders = new LinkedList<String>(); allowedProviders.add("anyIDP"); Map<String, Object> additionalInformation = new LinkedHashMap<String, Object>(); Collection me = AuthorityUtils.commaSeparatedStringToAuthorityList("openid,foo.bar,uaa.user,one.read"); //when(new DefaultSecurityContextAccessor()).thenReturn((DefaultSecurityContextAccessor) securityContextAccessor); mockedgranter = mock(Saml2TokenGranter.class); when(mockedgranter.validateRequest(tokenRequest)).thenReturn(userAuthentication); when(mockedgranter.getOAuth2Authentication(myClient, tokenRequest)).thenCallRealMethod(); myClient.setScope(StringUtils.commaDelimitedListToSet("openid,foo.bar")); additionalInformation.put(ClientConstants.ALLOWED_PROVIDERS, allowedProviders); myClient.setAdditionalInformation(additionalInformation); when(userAuthentication.getAuthorities()).thenReturn(me); when(requestFactory.createOAuth2Request(receivingClient, tokenRequest)).thenReturn(myReq); when(userAuthentication.getPrincipal()).thenReturn(uaaPrincipal); //when(identityProviderProvisioning.retrieveByOrigin("uaa","uaa")).thenThrow(new EmptyResultDataAccessException(0)); when(mockedgranter.getRequestFactory()).thenReturn(requestFactory); mockedgranter.getOAuth2Authentication(myClient, tokenRequest); } @Test public void test_oauth2_authentication_with_empty_allowed() { OAuth2Request myReq = new OAuth2Request(requestParameters, receivingClient.getClientId(), receivingClient.getAuthorities(), true, receivingClient.getScope(), receivingClient.getResourceIds(), null, null, null); BaseClientDetails myClient = new BaseClientDetails(requestingClient); List<String> allowedProviders = new LinkedList<String>(); Map<String, Object> additionalInformation = new LinkedHashMap<>(); Collection me = AuthorityUtils.commaSeparatedStringToAuthorityList("openid,foo.bar,uaa.user,one.read"); //when(new DefaultSecurityContextAccessor()).thenReturn((DefaultSecurityContextAccessor) securityContextAccessor); mockedgranter = mock(Saml2TokenGranter.class); when(mockedgranter.getRequestFactory()).thenReturn(requestFactory); when(mockedgranter.validateRequest(tokenRequest)).thenReturn(userAuthentication); when(mockedgranter.getOAuth2Authentication(myClient, tokenRequest)).thenCallRealMethod(); myClient.setScope(StringUtils.commaDelimitedListToSet("openid,foo.bar")); additionalInformation.put(ClientConstants.ALLOWED_PROVIDERS, allowedProviders); myClient.setAdditionalInformation(additionalInformation); when(userAuthentication.getAuthorities()).thenReturn(me); when(requestFactory.createOAuth2Request(receivingClient, tokenRequest)).thenReturn(myReq); mockedgranter.getOAuth2Authentication(myClient, tokenRequest); } @Test(expected = InvalidGrantException.class) public void test_missing_token_Request() { granter.validateRequest(null); } @Test public void happy_day() { missing_parameter("non existent"); } protected void missing_parameter(String parameter) { tokenRequest.setClientId(receivingClient.getClientId()); when(authentication.isAuthenticated()).thenReturn(true); when(authentication.getUserAuthentication()).thenReturn(null); when(authentication.getUserAuthentication()).thenReturn(userAuthentication); when(userAuthentication.isAuthenticated()).thenReturn(true); requestParameters.remove(parameter); tokenRequest.setGrantType(requestParameters.get(GRANT_TYPE)); granter.validateRequest(tokenRequest); } public static class PublicTokenRequest extends TokenRequest { public PublicTokenRequest() { } } EntityDescriptor getMetadata(String xml) { try { return (EntityDescriptor)unmarshallObject(xml); } catch(Exception e) { } return null; } Assertion getAssertion(String xml) { try { return (Assertion)unmarshallObject(xml); } catch(Exception e) { } return null; } String getAssertionXml(Assertion assertion) { try { AssertionMarshaller marshaller = new AssertionMarshaller(); Element plaintextElement = marshaller.marshall(assertion); String serializedElement = XMLHelper.nodeToString(plaintextElement); return serializedElement; } catch(Exception e) { } return null; } /* * Unmarshall XML string to OpenSAML XMLObject */ private XMLObject unmarshallObject(String xmlString) throws UnmarshallingException, XMLParserException, UnsupportedEncodingException { BasicParserPool parser = new BasicParserPool(); parser.setNamespaceAware(true); /* Base64URL encoded */ byte bytes[] = xmlString.getBytes("utf-8"); if (bytes == null || bytes.length == 0) throw new InsufficientAuthenticationException("Invalid assertion encoding"); Reader reader = new InputStreamReader(new ByteArrayInputStream(bytes)); Document doc = parser.parse(reader); Element samlElement = doc.getDocumentElement(); UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory(); Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(samlElement); if (unmarshaller == null) { throw new InsufficientAuthenticationException("Failed to unmarshal assertion string"); } return unmarshaller.unmarshall(samlElement); } }