Skip to content

Commit

Permalink
We should reject authentication tokens that we do not know about
Browse files Browse the repository at this point in the history
In the login server, this would create a user with origin=passcode
In the merged system, it will reject the any request (like token)
due to the user not being found.
https://www.pivotaltracker.com/story/show/86334056
[#86334056]
  • Loading branch information
fhanik committed Jan 30, 2015
1 parent 7e8f3d0 commit 021ee78
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 31 deletions.
Expand Up @@ -22,8 +22,10 @@ public class Origin {




public static String getUserId(Authentication authentication) { public static String getUserId(Authentication authentication) {
String id=null; String id;
if (authentication instanceof RemoteUserAuthentication) { if (authentication.getPrincipal() instanceof UaaPrincipal) {
return ((UaaPrincipal)authentication.getPrincipal()).getId();
} else if (authentication instanceof RemoteUserAuthentication) {
RemoteUserAuthentication remoteUserAuthentication = (RemoteUserAuthentication)authentication; RemoteUserAuthentication remoteUserAuthentication = (RemoteUserAuthentication)authentication;
return remoteUserAuthentication.getId(); return remoteUserAuthentication.getId();
} else if (authentication instanceof UaaAuthentication) { } else if (authentication instanceof UaaAuthentication) {
Expand Down
Expand Up @@ -40,6 +40,7 @@


import org.cloudfoundry.identity.uaa.authentication.AuthzAuthenticationRequest; import org.cloudfoundry.identity.uaa.authentication.AuthzAuthenticationRequest;
import org.cloudfoundry.identity.uaa.authentication.Origin; import org.cloudfoundry.identity.uaa.authentication.Origin;
import org.cloudfoundry.identity.uaa.authentication.UaaAuthentication;
import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal; import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal;
import org.cloudfoundry.identity.uaa.client.SocialClientUserDetails; import org.cloudfoundry.identity.uaa.client.SocialClientUserDetails;
import org.cloudfoundry.identity.uaa.codestore.ExpiringCode; import org.cloudfoundry.identity.uaa.codestore.ExpiringCode;
Expand All @@ -59,6 +60,7 @@
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
Expand Down Expand Up @@ -295,28 +297,32 @@ public AutologinResponse generateAutologinCode(@RequestBody AutologinRequest req
} }


@RequestMapping(value = { "/passcode" }, method = RequestMethod.GET) @RequestMapping(value = { "/passcode" }, method = RequestMethod.GET)
public String generatePasscode(@RequestHeader HttpHeaders headers, Map<String, Object> model, Principal principal) public String generatePasscode(Map<String, Object> model, Principal principal)
throws NoSuchAlgorithmException, IOException, JsonMappingException { throws NoSuchAlgorithmException, IOException {
String username = null, origin = null, userId = NotANumber; String username, origin, userId = NotANumber;
Map<String, Object> authorizationParameters = null; Map<String, Object> authorizationParameters = null;


if (principal instanceof LoginSamlAuthenticationToken) { if (principal instanceof UaaPrincipal) {
UaaPrincipal uaaPrincipal = (UaaPrincipal)principal;
username = uaaPrincipal.getName();
origin = uaaPrincipal.getOrigin();
userId = uaaPrincipal.getId();
} else if (principal instanceof UaaAuthentication) {
UaaPrincipal uaaPrincipal = ((UaaAuthentication)principal).getPrincipal();
username = uaaPrincipal.getName();
origin = uaaPrincipal.getOrigin();
userId = uaaPrincipal.getId();
} else if (principal instanceof LoginSamlAuthenticationToken) {
username = principal.getName(); username = principal.getName();
origin = ((LoginSamlAuthenticationToken)principal).getUaaPrincipal().getOrigin(); origin = ((LoginSamlAuthenticationToken) principal).getUaaPrincipal().getOrigin();
userId = ((LoginSamlAuthenticationToken)principal).getUaaPrincipal().getId(); userId = ((LoginSamlAuthenticationToken) principal).getUaaPrincipal().getId();
//TODO collect authorities here? } else if (principal instanceof Authentication && ((Authentication)principal).getPrincipal() instanceof UaaPrincipal) {
} else if (principal instanceof ExpiringUsernameAuthenticationToken) { UaaPrincipal uaaPrincipal = (UaaPrincipal)((Authentication)principal).getPrincipal();
username = ((SamlUserDetails) ((ExpiringUsernameAuthenticationToken) principal).getPrincipal()).getUsername(); username = uaaPrincipal.getName();
origin = "login-saml"; origin = uaaPrincipal.getOrigin();
Collection<GrantedAuthority> authorities = ((SamlUserDetails) (((ExpiringUsernameAuthenticationToken) principal) userId = uaaPrincipal.getId();
.getPrincipal())).getAuthorities();
if (authorities != null) {
authorizationParameters = new LinkedHashMap<>();
authorizationParameters.put("authorities", authorities);
}
} else { } else {
username = principal.getName(); throw new UnknownPrincipalException();
origin = "passcode";
} }


PasscodeInformation pi = new PasscodeInformation(userId, username, null, origin, authorizationParameters); PasscodeInformation pi = new PasscodeInformation(userId, username, null, origin, authorizationParameters);
Expand Down Expand Up @@ -401,4 +407,7 @@ protected String extractPath(HttpServletRequest request) {
return path; return path;
} }


@ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "Unknown authentication token type, unable to derive user ID.")
public static final class UnknownPrincipalException extends RuntimeException {}

} }
@@ -1,29 +1,45 @@
package org.cloudfoundry.identity.uaa.authentication.login; package org.cloudfoundry.identity.uaa.authentication.login;


import java.util.ArrayList; import org.cloudfoundry.identity.uaa.authentication.Origin;
import java.util.Collections; import org.cloudfoundry.identity.uaa.authentication.UaaAuthentication;

import org.cloudfoundry.identity.uaa.authentication.UaaAuthenticationDetails;
import junit.framework.TestCase; import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal;

import org.cloudfoundry.identity.uaa.codestore.ExpiringCodeStore;
import org.cloudfoundry.identity.uaa.codestore.InMemoryExpiringCodeStore;
import org.cloudfoundry.identity.uaa.login.saml.IdentityProviderDefinition; import org.cloudfoundry.identity.uaa.login.saml.IdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.login.saml.LoginSamlAuthenticationToken;
import org.cloudfoundry.identity.uaa.oauth.RemoteUserAuthentication;
import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.mock.env.MockEnvironment; import org.springframework.mock.env.MockEnvironment;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.providers.ExpiringUsernameAuthenticationToken;
import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model; import org.springframework.ui.Model;


public class LoginInfoEndpointTest extends TestCase { import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

public class LoginInfoEndpointTest {

private UaaPrincipal marissa;


@Before @Before
public void setUp() { public void setUpPrincipal() {
IdentityZoneHolder.clear(); marissa = new UaaPrincipal("marissa-id","marissa","marissa@test.org","origin",null, IdentityZoneHolder.get().getId());
} }


@Before
@After @After
public void tearDown() { public void clearZoneHolder() {
IdentityZoneHolder.clear(); IdentityZoneHolder.clear();
} }


Expand All @@ -33,7 +49,7 @@ public void testLoginReturnsSystemZone() throws Exception {
Model model = new ExtendedModelMap(); Model model = new ExtendedModelMap();
assertFalse(model.containsAttribute("zone_name")); assertFalse(model.containsAttribute("zone_name"));
endpoint.loginForHtml(model, null); endpoint.loginForHtml(model, null);
assertEquals("uaa", model.asMap().get("zone_name")); assertEquals(Origin.UAA, model.asMap().get("zone_name"));
} }


@Test @Test
Expand All @@ -47,7 +63,33 @@ public void testLoginReturnsOtherZone() throws Exception {
endpoint.loginForHtml(model, null); endpoint.loginForHtml(model, null);
assertEquals("some_other_zone", model.asMap().get("zone_name")); assertEquals("some_other_zone", model.asMap().get("zone_name"));
} }


@Test
public void testGeneratePasscodeForKnownUaaPrincipal() throws Exception {
Map<String,Object> model = new HashMap<>();
ExpiringCodeStore store = new InMemoryExpiringCodeStore();
LoginInfoEndpoint endpoint = getEndpoint();
endpoint.setExpiringCodeStore(store);
assertEquals("passcode", endpoint.generatePasscode(model, marissa));
UaaAuthentication uaaAuthentication = new UaaAuthentication(marissa, new ArrayList<GrantedAuthority>(),new UaaAuthenticationDetails(new MockHttpServletRequest()));
assertEquals("passcode", endpoint.generatePasscode(model, uaaAuthentication));
ExpiringUsernameAuthenticationToken expiringUsernameAuthenticationToken = new ExpiringUsernameAuthenticationToken(marissa,"");
LoginSamlAuthenticationToken samlAuthenticationToken = new LoginSamlAuthenticationToken(marissa, expiringUsernameAuthenticationToken);
assertEquals("passcode", endpoint.generatePasscode(model, samlAuthenticationToken));
//token with a UaaPrincipal should always work
assertEquals("passcode", endpoint.generatePasscode(model, expiringUsernameAuthenticationToken));

}

@Test(expected = LoginInfoEndpoint.UnknownPrincipalException.class)
public void testGeneratePasscodeForUnknownUaaPrincipal() throws Exception {
Map<String,Object> model = new HashMap<>();
LoginInfoEndpoint endpoint = getEndpoint();
ExpiringUsernameAuthenticationToken token = new ExpiringUsernameAuthenticationToken("princpal", "");
assertEquals("passcode", endpoint.generatePasscode(model, token));
}


private LoginInfoEndpoint getEndpoint() { private LoginInfoEndpoint getEndpoint() {
LoginInfoEndpoint endpoint = new LoginInfoEndpoint(); LoginInfoEndpoint endpoint = new LoginInfoEndpoint();
endpoint.setBaseUrl("http://someurl"); endpoint.setBaseUrl("http://someurl");
Expand Down
Expand Up @@ -2,21 +2,26 @@


import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.cloudfoundry.identity.uaa.authentication.Origin; import org.cloudfoundry.identity.uaa.authentication.Origin;
import org.cloudfoundry.identity.uaa.authentication.UaaAuthentication;
import org.cloudfoundry.identity.uaa.authentication.UaaAuthenticationDetails;
import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal; import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal;
import org.cloudfoundry.identity.uaa.config.YamlServletProfileInitializer; import org.cloudfoundry.identity.uaa.config.YamlServletProfileInitializer;
import org.cloudfoundry.identity.uaa.login.saml.LoginSamlAuthenticationToken; import org.cloudfoundry.identity.uaa.login.saml.LoginSamlAuthenticationToken;
import org.cloudfoundry.identity.uaa.oauth.RemoteUserAuthentication;
import org.cloudfoundry.identity.uaa.security.web.UaaRequestMatcher; import org.cloudfoundry.identity.uaa.security.web.UaaRequestMatcher;
import org.cloudfoundry.identity.uaa.user.UaaUserDatabase; import org.cloudfoundry.identity.uaa.user.UaaUserDatabase;
import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.ObjectMapper;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.mock.env.MockEnvironment; import org.springframework.mock.env.MockEnvironment;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession; import org.springframework.mock.web.MockHttpSession;
import org.springframework.mock.web.MockServletConfig; import org.springframework.mock.web.MockServletConfig;
import org.springframework.mock.web.MockServletContext; import org.springframework.mock.web.MockServletContext;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Authentication;
Expand All @@ -36,6 +41,7 @@
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
Expand Down Expand Up @@ -169,6 +175,103 @@ public void testLoginUsingPasscodeWithSamlToken() throws Exception {
assertEquals(marissa.getOrigin(), ((UaaPrincipal)authentication.getPrincipal()).getOrigin()); assertEquals(marissa.getOrigin(), ((UaaPrincipal)authentication.getPrincipal()).getOrigin());
} }


@Test
public void testLoginUsingPasscodeWithUaaToken() throws Exception {
UaaAuthenticationDetails details = new UaaAuthenticationDetails(new MockHttpServletRequest());
UaaAuthentication uaaAuthentication = new UaaAuthentication(marissa, new ArrayList<GrantedAuthority>(),details);

final MockSecurityContext mockSecurityContext = new MockSecurityContext(uaaAuthentication);

SecurityContextHolder.setContext(mockSecurityContext);
MockHttpSession session = new MockHttpSession();

session.setAttribute(
HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
mockSecurityContext
);



MockHttpServletRequestBuilder get = get("/passcode")
.accept(APPLICATION_JSON)
.session(session);

String passcode = new ObjectMapper().readValue(
mockMvc.perform(get)
.andExpect(status().isOk())
.andDo(print())
.andReturn().getResponse().getContentAsString(),
String.class);

mockSecurityContext.setAuthentication(null);
session = new MockHttpSession();
session.setAttribute(
HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
mockSecurityContext
);

String basicDigestHeaderValue = "Basic " + new String(Base64.encodeBase64(("cf:").getBytes()));
MockHttpServletRequestBuilder post = post("/oauth/token")
.accept(APPLICATION_JSON)
.contentType(APPLICATION_FORM_URLENCODED)
.header("Authorization", basicDigestHeaderValue)
.param("grant_type", "password")
.param("passcode", passcode)
.param("response_type", "token")
.session(session);


Map accessToken =
new ObjectMapper().readValue(
mockMvc.perform(post)
.andDo(print())
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
Map.class);
assertEquals("bearer", accessToken.get("token_type"));
assertNotNull(accessToken.get("access_token"));
assertNotNull(accessToken.get("refresh_token"));
String[] scopes = ((String) accessToken.get("scope")).split(" ");
assertThat(Arrays.asList(scopes), containsInAnyOrder("scim.userids", "password.write", "cloud_controller.write", "openid", "cloud_controller.read"));

Authentication authentication = captureSecurityContextFilter.getAuthentication();
assertNotNull(authentication);
assertTrue(authentication instanceof OAuth2Authentication);
assertTrue(((OAuth2Authentication)authentication).getUserAuthentication() instanceof UsernamePasswordAuthenticationToken);
assertTrue(authentication.getPrincipal() instanceof UaaPrincipal);
assertEquals(marissa.getOrigin(), ((UaaPrincipal)authentication.getPrincipal()).getOrigin());
}


@Test
public void testLoginUsingPasscodeWithUnknownToken() throws Exception {
RemoteUserAuthentication userAuthentication = new RemoteUserAuthentication(
marissa.getId(),
marissa.getName(),
marissa.getEmail(),
new ArrayList<GrantedAuthority>()
);
final MockSecurityContext mockSecurityContext = new MockSecurityContext(userAuthentication);

SecurityContextHolder.setContext(mockSecurityContext);
MockHttpSession session = new MockHttpSession();

session.setAttribute(
HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
mockSecurityContext
);


MockHttpServletRequestBuilder get = get("/passcode")
.accept(APPLICATION_JSON)
.session(session);

mockMvc.perform(get)
.andExpect(status().isForbidden())
.andDo(print());

}

public static class MockSecurityContext implements SecurityContext { public static class MockSecurityContext implements SecurityContext {


private static final long serialVersionUID = -1386535243513362694L; private static final long serialVersionUID = -1386535243513362694L;
Expand Down

0 comments on commit 021ee78

Please sign in to comment.