Search in sources :

Example 1 with AccessTokenRequest

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;
}
Also used : IdToken(com.yahoo.athenz.auth.token.IdToken) DataCache(com.yahoo.athenz.zts.cache.DataCache) X509Certificate(java.security.cert.X509Certificate) AccessToken(com.yahoo.athenz.auth.token.AccessToken) AccessTokenRequest(com.yahoo.athenz.zts.token.AccessTokenRequest) SimplePrincipal(com.yahoo.athenz.auth.impl.SimplePrincipal)

Aggregations

SimplePrincipal (com.yahoo.athenz.auth.impl.SimplePrincipal)1 AccessToken (com.yahoo.athenz.auth.token.AccessToken)1 IdToken (com.yahoo.athenz.auth.token.IdToken)1 DataCache (com.yahoo.athenz.zts.cache.DataCache)1 AccessTokenRequest (com.yahoo.athenz.zts.token.AccessTokenRequest)1 X509Certificate (java.security.cert.X509Certificate)1