/*******************************************************************************
* 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.security;
import com.fasterxml.jackson.core.type.TypeReference;
import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal;
import org.cloudfoundry.identity.uaa.oauth.UaaOauth2Authentication;
import org.cloudfoundry.identity.uaa.oauth.jwt.JwtHelper;
import org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants;
import org.cloudfoundry.identity.uaa.util.JsonUtils;
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.cloudfoundry.identity.uaa.zone.ZoneManagementScopes;
import org.springframework.security.core.Authentication;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.security.oauth2.provider.expression.OAuth2SecurityExpressionMethods;
import org.springframework.util.StringUtils;
import java.util.Map;
public class ContextSensitiveOAuth2SecurityExpressionMethods extends OAuth2SecurityExpressionMethods {
private final IdentityZone identityZone;
private final Authentication authentication;
private String replaceContext(String role) {
IdentityZone zone = IdentityZoneHolder.get();
return role.replace(ZoneManagementScopes.ZONE_ID_MATCH, zone.getId());
}
private String[] replaceContext(String[] roles) {
if (roles==null || roles.length==0) {
return roles;
}
String[] adjusted = new String[roles.length];
for (int i=0; i<roles.length; i++) {
adjusted[i] = replaceContext(roles[i]);
}
return adjusted;
}
public ContextSensitiveOAuth2SecurityExpressionMethods(Authentication authentication) {
this(authentication, IdentityZone.getUaa());
}
public ContextSensitiveOAuth2SecurityExpressionMethods(Authentication authentication, IdentityZone authenticationZone) {
super(authentication);
this.authentication = authentication;
this.identityZone = authenticationZone;
}
@Override
public boolean clientHasRole(String role) {
return super.clientHasRole(replaceContext(role));
}
@Override
public boolean clientHasAnyRole(String... roles) {
return super.clientHasAnyRole(replaceContext(roles));
}
private boolean isUaaAdmin() {
return super.hasAnyScope("uaa.admin");
}
@Override
public boolean hasAnyScope(String... scopes) {
return isUaaAdmin() || super.hasAnyScope(replaceContext(scopes));
}
@Override
public boolean hasAnyScopeMatching(String... scopesRegex) {
return isUaaAdmin() || super.hasAnyScopeMatching(replaceContext(scopesRegex));
}
public boolean hasScopeInAuthZone(String scope) {
boolean hasScope = hasScope(scope);
String authZoneId = getAuthenticationZoneId();
hasScope = hasScope && StringUtils.hasText(authZoneId);
if (hasScope) {
hasScope = identityZone != null && identityZone.getId().equals(authZoneId);
}
return hasScope;
}
public String getAuthenticationZoneId() {
if (authentication.getPrincipal() instanceof UaaPrincipal) {
return ((UaaPrincipal) authentication.getPrincipal()).getZoneId();
} else if (authentication instanceof UaaOauth2Authentication) {
return ((UaaOauth2Authentication)authentication).getZoneId();
} else if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
String tokenValue = ((OAuth2AuthenticationDetails)authentication.getDetails()).getTokenValue();
return getZoneIdFromToken(tokenValue);
} else {
return null;
}
}
private String getZoneIdFromToken(String token) {
Jwt tokenJwt;
try {
tokenJwt = JwtHelper.decode(token);
} catch (Throwable t) {
throw new IllegalStateException("Cannot decode token", t);
}
Map<String, Object> claims;
try {
claims = JsonUtils.readValue(tokenJwt.getClaims(), new TypeReference<Map<String, Object>>() {});
} catch (JsonUtils.JsonUtilException e) {
throw new IllegalStateException("Cannot read token claims", e);
}
return (String)claims.get(ClaimConstants.ZONE_ID);
}
}