org.cloudfoundry.identity.uaa.oauth.TokenAdminEndpoints.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudfoundry.identity.uaa.oauth.TokenAdminEndpoints.java

Source

/*
 * Cloud Foundry 2012.02.03 Beta
 * Copyright (c) [2009-2012] VMware, 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.oauth;

import java.security.Principal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.cloudfoundry.identity.uaa.rest.SimpleMessage;
import org.cloudfoundry.identity.uaa.scim.ScimResourceNotFoundException;
import org.cloudfoundry.identity.uaa.scim.ScimUser;
import org.cloudfoundry.identity.uaa.scim.ScimUserProvisioning;
import org.cloudfoundry.identity.uaa.user.UaaAuthority;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * Controller for listing and manipulating access tokens.
 * 
 * @author Dave Syer
 */
@Controller
public class TokenAdminEndpoints {

    private ConsumerTokenServices tokenServices;

    private ScimUserProvisioning scimProvisioning;

    private PasswordEncoder encoder = new StandardPasswordEncoder();

    @RequestMapping("/oauth/users/{user}/tokens")
    @ResponseBody
    public Collection<OAuth2AccessToken> listTokensForUser(@PathVariable String user, Principal principal,
            @RequestParam(required = false, defaultValue = "true") boolean lookup) throws Exception {
        String username = lookup ? getUserName(user) : user;
        checkResourceOwner(username, principal);
        return enhance(tokenServices.findTokensByUserName(username));
    }

    @RequestMapping(value = "/oauth/users/{user}/tokens/{token}", method = RequestMethod.DELETE)
    @ResponseBody
    public SimpleMessage revokeUserToken(@PathVariable String user, @PathVariable String token, Principal principal,
            @RequestParam(required = false, defaultValue = "true") boolean lookup) throws Exception {
        String username = lookup ? getUserName(user) : user;
        checkResourceOwner(username, principal);
        String tokenValue = getTokenValue(tokenServices.findTokensByUserName(username), token);
        if (tokenValue != null && tokenServices.revokeToken(tokenValue)) {
            return new SimpleMessage("ok", "user token revoked");
        }
        throw new NoSuchTokenException("Token not found");
    }

    @RequestMapping("/oauth/clients/{client}/tokens")
    @ResponseBody
    public Collection<OAuth2AccessToken> listTokensForClient(@PathVariable String client, Principal principal)
            throws Exception {
        checkClient(client, principal);
        return enhance(tokenServices.findTokensByClientId(client));
    }

    @RequestMapping(value = "/oauth/clients/{client}/tokens/{token}", method = RequestMethod.DELETE)
    @ResponseBody
    public SimpleMessage revokeClientToken(@PathVariable String client, @PathVariable String token,
            Principal principal) throws Exception {
        checkClient(client, principal);
        String tokenValue = getTokenValue(tokenServices.findTokensByClientId(client), token);
        if (tokenValue != null && tokenServices.revokeToken(tokenValue)) {
            return new SimpleMessage("ok", "client token revoked");
        }
        throw new NoSuchTokenException("Token not found");
    }

    @ExceptionHandler(NoSuchTokenException.class)
    public ResponseEntity<Void> handleNoSuchToken(NoSuchTokenException e) {
        return new ResponseEntity<Void>(HttpStatus.NOT_FOUND);
    }

    private String getUserName(String user) {
        if (scimProvisioning == null) {
            return user;
        }
        String username = user;
        try {
            if (scimProvisioning != null) {
                // If the request came in for a user by id we should be able to retrieve the username
                ScimUser scimUser = scimProvisioning.retrieveUser(username);
                if (scimUser != null) {
                    username = scimUser.getUserName();
                }
            }
        } catch (ScimResourceNotFoundException e) {
            // ignore
        }
        return username;
    }

    private String getTokenValue(Collection<OAuth2AccessToken> tokens, String hash) {
        for (OAuth2AccessToken token : tokens) {
            try {
                if (token.getAdditionalInformation().containsKey(JwtTokenEnhancer.TOKEN_ID)
                        && hash.equals(token.getAdditionalInformation().get(JwtTokenEnhancer.TOKEN_ID))
                        || encoder.matches(token.getValue(), hash)) {
                    return token.getValue();
                }
            } catch (Exception e) {
                // it doesn't match
            }
        }
        return null;
    }

    private Collection<OAuth2AccessToken> enhance(Collection<OAuth2AccessToken> tokens) {
        Collection<OAuth2AccessToken> result = new ArrayList<OAuth2AccessToken>();
        for (OAuth2AccessToken prototype : tokens) {
            DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(prototype);
            Map<String, Object> map = new HashMap<String, Object>(token.getAdditionalInformation());
            if (!map.containsKey(JwtTokenEnhancer.TOKEN_ID)) {
                // The token doesn't have an ID in the token service, but we need one for the endpoint, so add one here
                map.put(JwtTokenEnhancer.TOKEN_ID, encoder.encode(token.getValue()));
            }
            try {
                String clientId = tokenServices.getClientId(token.getValue());
                if (clientId != null) {
                    map.put("client_id", clientId);
                }
            } catch (InvalidTokenException e) {
                // Ignore defensively in case of bugs in token services
            }
            token.setAdditionalInformation(map);
            result.add(token);
        }
        return result;
    }

    private void checkResourceOwner(String user, Principal principal) {
        if (principal instanceof OAuth2Authentication) {
            OAuth2Authentication authentication = (OAuth2Authentication) principal;
            if (!authentication.isClientOnly() && !user.equals(principal.getName())) {
                throw new AccessDeniedException(
                        String.format("User '%s' cannot obtain tokens for user '%s'", principal.getName(), user));
            }
        } else if (!user.equals(principal.getName())) {
            throw new AccessDeniedException(
                    String.format("User '%s' cannot obtain tokens for user '%s'", principal.getName(), user));
        }

    }

    private void checkClient(String client, Principal principal) {
        if (principal instanceof OAuth2Authentication) {
            OAuth2Authentication authentication = (OAuth2Authentication) principal;
            if (!authentication.isClientOnly() || !client.equals(principal.getName()) && !isAdmin(principal)) {
                throw new AccessDeniedException(String.format("Client '%s' cannot obtain tokens for client '%s'",
                        principal.getName(), client));
            }
        }
    }

    private boolean isAdmin(Principal principal) {
        return AuthorityUtils.authorityListToSet(((Authentication) principal).getAuthorities())
                .contains(UaaAuthority.UAA_ADMIN.toString());
    }

    /**
     * @param tokenServices the consumerTokenServices to set
     */
    public void setTokenServices(ConsumerTokenServices tokenServices) {
        this.tokenServices = tokenServices;
    }

    /**
     * @param scimProvisioning the scimProvisioning to set
     */
    public void setScimUserProvisioning(ScimUserProvisioning scimProvisioning) {
        this.scimProvisioning = scimProvisioning;
    }

}