use of com.yahoo.athenz.zts.token.AccessTokenRequest in project athenz by yahoo.
the class ZTSImpl method postAccessTokenRequest.
@Override
public AccessTokenResponse postAccessTokenRequest(ResourceContext ctx, String request) {
final String caller = ctx.getApiName();
final String principalDomain = logPrincipalAndGetDomain(ctx);
validateRequest(ctx.request(), principalDomain, caller);
// get our principal's name
final Principal principal = ((RsrcCtxWrapper) ctx).principal();
String principalName = principal.getFullName();
if (StringUtil.isEmpty(request)) {
throw requestError("Empty request body", caller, ZTSConsts.ZTS_UNKNOWN_DOMAIN, principalDomain);
}
// we want to log the request body in our access log so
// we know what is the client asking for but we'll just
// limit the request up to 1K
ctx.request().setAttribute(ACCESS_LOG_ADDL_QUERY, getQueryLogData(request));
// decode and store the attributes that could exist in our
// request body
String grantType = null;
String scope = null;
String proxyForPrincipal = null;
String authzDetails = null;
List<String> proxyPrincipalsSpiffeUris = null;
int expiryTime = 0;
boolean useOpenIDIssuer = false;
String[] comps = request.split("&");
for (String comp : comps) {
int idx = comp.indexOf('=');
if (idx == -1) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("postAccessTokenRequest: skipping invalid component: {}", comp);
}
continue;
}
final String key = decodeString(comp.substring(0, idx));
if (key == null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("postAccessTokenRequest: skipping invalid component: {}", comp);
}
continue;
}
final String value = decodeString(comp.substring(idx + 1));
if (value == null) {
continue;
}
switch(key) {
case KEY_GRANT_TYPE:
grantType = value.toLowerCase();
break;
case KEY_SCOPE:
scope = value.toLowerCase();
break;
case KEY_EXPIRES_IN:
expiryTime = ZTSUtils.parseInt(value, 0);
break;
case KEY_PROXY_FOR_PRINCIPAL:
proxyForPrincipal = getProxyForPrincipalValue(value.toLowerCase(), principalName, principalDomain, caller);
break;
case KEY_AUTHORIZATION_DETAILS:
authzDetails = value;
break;
case KEY_PROXY_PRINCIPAL_SPIFFE_URIS:
proxyPrincipalsSpiffeUris = getProxyPrincipalSpiffeUris(value.toLowerCase(), principalDomain, caller);
break;
case KEY_OPENID_ISSUER:
useOpenIDIssuer = Boolean.parseBoolean(value);
break;
}
}
if (!OAUTH_GRANT_CREDENTIALS.equals(grantType)) {
throw requestError("Invalid grant request: " + grantType, caller, principal.getDomain(), principalDomain);
}
if (scope == null || scope.isEmpty()) {
throw requestError("Invalid request: no scope provided", caller, principal.getDomain(), principalDomain);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("postAccessTokenRequest(principal: {}, grant-type: {}, scope: {}, expires-in: {}, proxy-for-principal: {})", principalName, grantType, scope, expiryTime, proxyForPrincipal);
}
// our scopes are space separated list of values
AccessTokenRequest tokenRequest = new AccessTokenRequest(scope);
// before using any of our values let's validate that they
// match our schema
final String domainName = tokenRequest.getDomainName();
setRequestDomain(ctx, domainName);
validate(domainName, TYPE_DOMAIN_NAME, principalDomain, caller);
String[] requestedRoles = tokenRequest.getRoleNames();
if (requestedRoles != null) {
for (String requestedRole : requestedRoles) {
validate(requestedRole, TYPE_ENTITY_NAME, principalDomain, caller);
}
}
// first retrieve our domain data object from the cache
DataCache data = dataStore.getDataCache(domainName);
if (data == null) {
setRequestDomain(ctx, ZTSConsts.ZTS_UNKNOWN_DOMAIN);
throw notFoundError("No such domain: " + domainName, caller, ZTSConsts.ZTS_UNKNOWN_DOMAIN, principalDomain);
}
// if we're given authorization details to be included in the
// token then we must have only role requested and we need
// to make sure the requested fields are valid according
// to our configured authorization details entity for the role
validateAuthorizationDetails(authzDetails, requestedRoles, data, caller, domainName, principalDomain);
// check if the authorized service domain matches to the
// requested domain name
checkRoleTokenAuthorizedServiceRequest(principal, domainName, caller);
// process our request and retrieve the roles for the principal
Set<String> roles = new HashSet<>();
dataStore.getAccessibleRoles(data, domainName, principalName, requestedRoles, roles, false);
if (roles.isEmpty()) {
throw forbiddenError(tokenErrorMessage(caller, principalName, domainName, requestedRoles), caller, domainName, principalDomain);
}
// if this is proxy for operation then we want to make sure that
// both principals have access to the same set of roles so we'll
// remove any roles that are authorized by only one of the principals
String proxyUser = null;
if (proxyForPrincipal != null) {
if (tokenRequest.isOpenIdScope()) {
throw requestError("Proxy Principal cannot request id tokens", caller, domainName, principalDomain);
}
// process the role lookup for the proxy principal
Set<String> rolesForProxy = new HashSet<>();
dataStore.getAccessibleRoles(data, domainName, proxyForPrincipal, requestedRoles, rolesForProxy, false);
roles.retainAll(rolesForProxy);
if (roles.isEmpty()) {
throw forbiddenError(tokenErrorMessage(caller, proxyForPrincipal, domainName, requestedRoles), caller, domainName, principalDomain);
}
// we need to switch our principal and proxy for user
proxyUser = principalName;
principalName = proxyForPrincipal;
}
if (!isPrincipalRoleCertificateAccessValid(principal, domainName, roles)) {
throw forbiddenError("Role based Principal does not include all roles", caller, domainName, principalDomain);
}
long tokenTimeout = determineTokenTimeout(data, roles, null, expiryTime);
long iat = System.currentTimeMillis() / 1000;
AccessToken accessToken = new AccessToken();
accessToken.setVersion(1);
accessToken.setJwtId(UUID.randomUUID().toString());
accessToken.setAudience(domainName);
accessToken.setClientId(principalName);
accessToken.setIssueTime(iat);
accessToken.setAuthTime(iat);
accessToken.setExpiryTime(iat + tokenTimeout);
accessToken.setUserId(principalName);
accessToken.setSubject(principalName);
accessToken.setIssuer(useOpenIDIssuer ? ztsOpenIDIssuer : ztsOAuthIssuer);
accessToken.setProxyPrincipal(proxyUser);
accessToken.setScope(new ArrayList<>(roles));
accessToken.setAuthorizationDetails(authzDetails);
// if we have a certificate used for mTLS authentication then
// we're going to bind the certificate to the access token
// and the optional proxy principals if specified
X509Certificate cert = principal.getX509Certificate();
if (cert != null) {
accessToken.setConfirmX509CertHash(cert);
if (proxyPrincipalsSpiffeUris != null) {
accessToken.setConfirmProxyPrincipalSpiffeUris(proxyPrincipalsSpiffeUris);
}
}
String accessJwts = accessToken.getSignedToken(privateKey.getKey(), privateKey.getId(), privateKey.getAlgorithm());
// now let's check to see if we need to create openid token
String idJwts = null;
if (tokenRequest.isOpenIdScope()) {
final String serviceName = tokenRequest.getServiceName();
validate(serviceName, TYPE_SIMPLE_NAME, principalDomain, caller);
IdToken idToken = new IdToken();
idToken.setVersion(1);
idToken.setAudience(tokenRequest.getDomainName() + "." + serviceName);
idToken.setSubject(principalName);
idToken.setIssuer(useOpenIDIssuer ? ztsOpenIDIssuer : ztsOAuthIssuer);
// id tokens are only valid for up to 12 hours max
// (value configured as a system property).
// we'll use the user specified timeout unless it's
// over the configured max
idToken.setIssueTime(iat);
idToken.setAuthTime(iat);
idToken.setExpiryTime(iat + determineIdTokenTimeout(tokenTimeout));
idJwts = idToken.getSignedToken(privateKey.getKey(), privateKey.getId(), privateKey.getAlgorithm());
}
AccessTokenResponse response = new AccessTokenResponse().setAccess_token(accessJwts).setToken_type(OAUTH_BEARER_TOKEN).setExpires_in((int) tokenTimeout).setId_token(idJwts);
if (tokenRequest.sendScopeResponse() || requestedRoles != null && requestedRoles.length != roles.size()) {
List<String> domainRoles = new ArrayList<>();
for (String role : roles) {
domainRoles.add(domainName + AccessTokenRequest.OBJECT_ROLE + role);
}
if (tokenRequest.isOpenIdScope()) {
domainRoles.add(AccessTokenRequest.OBJECT_OPENID);
}
response.setScope(String.join(" ", domainRoles));
}
return response;
}
Aggregations