All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cloudfoundry.identity.uaa.util.UaaTokenUtils Maven / Gradle / Ivy

The newest version!
/*
 * *****************************************************************************
 *      Cloud Foundry
 *      Copyright (c) [2009-2015] 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.util;

import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.codec.binary.Base64;
import org.cloudfoundry.identity.uaa.oauth.client.ClientConstants;
import org.cloudfoundry.identity.uaa.oauth.jwt.Jwt;
import org.cloudfoundry.identity.uaa.oauth.jwt.JwtHelper;
import org.cloudfoundry.identity.uaa.user.UaaUser;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.jwt.crypto.sign.Signer;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static java.util.Collections.emptySet;
import static java.util.Optional.ofNullable;
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.CID;
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.GRANT_TYPE;
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.SUB;
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.USER_ID;
import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.GRANT_TYPE_CLIENT_CREDENTIALS;
import static org.springframework.util.StringUtils.hasText;

public final class UaaTokenUtils {

    public static final Pattern jwtPattern = Pattern.compile("[a-zA-Z0-9_\\-\\\\=]*\\.[a-zA-Z0-9_\\-\\\\=]*\\.[a-zA-Z0-9_\\-\\\\=]*");

    private UaaTokenUtils() { }

    public static String getRevocationHash(List salts) {
        String result = "";
        for (String s : salts) {
            byte[] hashable = (result+ "###" + s).getBytes();
            result = Integer.toHexString(murmurhash3x8632(hashable, 0, hashable.length, 0xF0F0));
        }
        return result;
    }

    /**
     * This code is public domain.
     *
     *  The MurmurHash3 algorithm was created by Austin Appleby and put into the public domain.
     *  @see http://code.google.com/p/smhasher
     *  @see https://github.com/yonik/java_util/blob/master/src/util/hash/MurmurHash3.java
     */
    public static int murmurhash3x8632(byte[] data, int offset, int len, int seed) {

        int c1 = 0xcc9e2d51;
        int c2 = 0x1b873593;

        int h1 = seed;
        int roundedEnd = offset + (len & 0xfffffffc);  // round down to 4 byte block

        for (int i = offset; i < roundedEnd; i += 4) {
            // little endian load order
            int k1 = (data[i] & 0xff) | ((data[i + 1] & 0xff) << 8) | ((data[i + 2] & 0xff) << 16) | (data[i + 3] << 24);
            k1 *= c1;
            k1 = (k1 << 15) | (k1 >>> 17);  // ROTL32(k1,15);
            k1 *= c2;

            h1 ^= k1;
            h1 = (h1 << 13) | (h1 >>> 19);  // ROTL32(h1,13);
            h1 = h1 * 5 + 0xe6546b64;
        }

        // tail
        int k1 = 0;

        switch(len & 0x03) {
            case 3:
                k1 = (data[roundedEnd + 2] & 0xff) << 16;
                // fallthrough
            case 2:
                k1 |= (data[roundedEnd + 1] & 0xff) << 8;
                // fallthrough
            case 1:
                k1 |= data[roundedEnd] & 0xff;
                k1 *= c1;
                k1 = (k1 << 15) | (k1 >>> 17);  // ROTL32(k1,15);
                k1 *= c2;
                h1 ^= k1;
            default:
        }

        // finalization
        h1 ^= len;

        // fmix(h1);
        h1 ^= h1 >>> 16;
        h1 *= 0x85ebca6b;
        h1 ^= h1 >>> 13;
        h1 *= 0xc2b2ae35;
        h1 ^= h1 >>> 16;

        return h1;
    }

    public static Set retainAutoApprovedScopes(Collection requestedScopes, Set autoApprovedScopes) {
        HashSet result = new HashSet<>();
        if(autoApprovedScopes == null){
            return result;
        }
        if (autoApprovedScopes.contains("true")) {
            result.addAll(requestedScopes);
            return result;
        }
        Set autoApprovedScopePatterns = UaaStringUtils.constructWildcards(autoApprovedScopes);
        // Don't want to approve more than what's requested
        for (String scope : requestedScopes) {
            if (UaaStringUtils.matches(autoApprovedScopePatterns, scope)) {
                result.add(scope);
            }
        }
        return result;
    }

    public static boolean isUserToken(Map claims) {
        if (claims.get(GRANT_TYPE)!=null) {
            return !GRANT_TYPE_CLIENT_CREDENTIALS.equals(claims.get(GRANT_TYPE));
        }
        if (claims.get(SUB)!=null) {
            if (claims.get(SUB).equals(claims.get(USER_ID))) {
                return true;
            } else if (claims.get(SUB).equals(claims.get(CID))) {
                return false;
            }
        }
        //err on the side of caution
        return true;
    }

    public static String getRevocableTokenSignature(ClientDetails client, String clientSecret, UaaUser user) {
        String tokenSalt = (String) client.getAdditionalInformation().get(ClientConstants.TOKEN_SALT);
        String clientId = client.getClientId();

        return getRevocableTokenSignature(user, tokenSalt, clientId, clientSecret);
    }

    public static String getRevocableTokenSignature(UaaUser user, String tokenSalt, String clientId, String clientSecret) {
        String[] salts = new String[] {
            clientId,
            clientSecret,
            tokenSalt,
            user == null ? null : user.getId(),
            user == null ? null : user.getPassword(),
            user == null ? null : user.getSalt(),
            user == null ? null : user.getEmail(),
            user == null ? null : user.getUsername(),
        };
        List saltlist = new LinkedList<>();
        for (String s : salts) {
            if (s!=null) {
                saltlist.add(s);
            }
        }
        return getRevocationHash(saltlist);
    }

    public static String constructToken(Map header, Map claims, Signer signer) {
        byte[] headerJson = header == null ? new byte[0] : JsonUtils.writeValueAsBytes(header);
        byte[] claimsJson = claims == null ? new byte[0] : JsonUtils.writeValueAsBytes(claims);

        String headerBase64 = Base64.encodeBase64URLSafeString(headerJson);
        String claimsBase64 = Base64.encodeBase64URLSafeString(claimsJson);
        String headerAndClaims = headerBase64 + "." + claimsBase64;
        byte[] signature = signer.sign(headerAndClaims.getBytes());

        String signatureBase64 = Base64.encodeBase64URLSafeString(signature);

        return headerAndClaims + "." + signatureBase64;
    }

    public static boolean isJwtToken(String token) {
        return jwtPattern.matcher(token).matches();
    }

    public static String constructTokenEndpointUrl(String issuer) throws URISyntaxException {
        URI uri;
        if (!IdentityZoneHolder.isUaa()) {
            String zone_issuer = IdentityZoneHolder.get().getConfig() != null ? IdentityZoneHolder.get().getConfig().getIssuer() : null;
            if(zone_issuer != null) {
                uri = validateIssuer(zone_issuer);
                return UriComponentsBuilder.fromUri(uri).pathSegment("oauth/token").build().toUriString();
            }
        }
        uri = validateIssuer(issuer);
        String hostToUse = uri.getHost();
        if (hasText(IdentityZoneHolder.get().getSubdomain())) {
            hostToUse = IdentityZoneHolder.get().getSubdomain() + "." + hostToUse;
        }
        return UriComponentsBuilder.fromUri(uri).host(hostToUse).pathSegment("oauth/token").build().toUriString();
    }

    private static URI validateIssuer(String issuer) throws URISyntaxException {
        try {
            new URL(issuer);
        } catch (MalformedURLException x) {
            throw new URISyntaxException(issuer, x.getMessage());
        }
        return new URI(issuer);
    }

    public static boolean hasRequiredUserAuthorities(Collection requiredGroups, Collection userGroups) {
        return hasRequiredUserGroups(requiredGroups,
                                     ofNullable(userGroups).orElse(emptySet())
                                        .stream()
                                        .map(GrantedAuthority::getAuthority)
                                        .collect(Collectors.toList())
        );
    }

    public static boolean hasRequiredUserGroups(Collection requiredGroups, Collection userGroups) {
        return ofNullable(userGroups).orElse(emptySet())
            .containsAll(ofNullable(requiredGroups).orElse(emptySet()));
    }

    public static Map getClaims(String jwtToken) {
        Jwt jwt;
        try {
            jwt = JwtHelper.decode(jwtToken);
        } catch (Exception ex) {
            throw new InvalidTokenException("Invalid token (could not decode): " + jwtToken, ex);
        }

        Map claims;
        try {
            claims = JsonUtils.readValue(jwt.getClaims(), new TypeReference>() {
            });
        } catch (JsonUtils.JsonUtilException ex) {
            throw new InvalidTokenException("Invalid token (cannot read token claims): " + jwtToken, ex);
        }

        return claims != null ? claims : new HashMap<>();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy