org.apigw.authserver.web.controller.CertifiedClientsController.java Source code

Java tutorial

Introduction

Here is the source code for org.apigw.authserver.web.controller.CertifiedClientsController.java

Source

/**
 *   Copyright 2013 Stockholm County Council
 *
 *   This file is part of APIGW
 *
 *   APIGW is free software; you can redistribute it and/or modify
 *   it under the terms of version 2.1 of the GNU Lesser General Public
 *   License as published by the Free Software Foundation.
 *
 *   APIGW is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with APIGW; if not, write to the
 *   Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 *   Boston, MA 02111-1307  USA
 *
 */
package org.apigw.authserver.web.controller;

import org.apigw.authserver.svc.CertifiedClientDetailsService;
import org.apigw.authserver.svc.PermissionServices;
import org.apigw.authserver.svc.encrypted.EncryptedResourceOwnerServices;
import org.apigw.authserver.types.domain.CertifiedClient;
import org.apigw.authserver.types.domain.CertifiedClientIcon;
import org.apigw.authserver.types.domain.Permission;
import org.apigw.authserver.web.beans.CertifiedClientDetails;
import org.apigw.authserver.web.beans.UserDetail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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 java.text.SimpleDateFormat;
import java.util.*;

import static java.util.Collections.sort;

/**
 * Controller that handles CertifiedClients.
 * The controller can both list new clients and revoke grants for the current user.
 */
@Controller
public class CertifiedClientsController {
    private static final Logger log = LoggerFactory.getLogger(CertifiedClientsController.class);

    @Autowired
    private CertifiedClientDetailsService clientDetailsService;

    @Autowired
    ConsumerTokenServices consumerTokenServices;

    @Autowired
    private PermissionServices permissionServices;

    @Autowired
    private EncryptedResourceOwnerServices encryptedResourceOwnerServices;

    @RequestMapping(value = "/oauth/clients", method = RequestMethod.GET)
    public String listClients(Model model) {
        log.debug("listClients");
        Map<String, CertifiedClient> allCertifiedClients = new HashMap<String, CertifiedClient>();

        allCertifiedClients.putAll(findAllCertifiedClients());

        List<Map<String, Object>> availableClients = findAvailableClients(allCertifiedClients);
        sort(availableClients, createClientComparator());

        List<CertifiedClientDetails> certifiedClientsWithUsersWithConfirmedAccess = createCollectionOfWhichCertifiedClientsHasUsersWithConfirmedAccess();

        //Remove clients from available client as a client can not show up as installed client and an available client
        List<Map<String, Object>> copyOfAvailableClients = new ArrayList<Map<String, Object>>(); //To avoid ConcurrentModificationException
        copyOfAvailableClients.addAll(availableClients);
        for (CertifiedClientDetails certifiedClientDetails : certifiedClientsWithUsersWithConfirmedAccess) {
            for (Map<String, Object> availableClient : copyOfAvailableClients) {
                if (availableClient.containsValue(certifiedClientDetails.getClientId())) {
                    availableClients.remove(availableClient);
                }
            }
        }

        model.addAttribute("certifiedClients", certifiedClientsWithUsersWithConfirmedAccess);
        model.addAttribute("availableClients", availableClients);

        log.debug("certifiedClients: " + certifiedClientsWithUsersWithConfirmedAccess);
        log.debug("Available clients: " + availableClients);

        log.debug("returning from listClients");

        return ".listClients";
    }

    private UserDetails getUserDetailsFromSecurityContext() {
        return (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }

    private List<Map<String, Object>> findAvailableClients(Map<String, CertifiedClient> otherClients) {

        List<Map<String, Object>> availableClients = new ArrayList<Map<String, Object>>();

        for (CertifiedClient c : otherClients.values()) {
            Map<String, Object> details = new HashMap<String, Object>();
            details.put("clientId", c.getClientId());
            details.put("clientName", c.getName());
            details.put("organization", c.getOrganization());
            details.put("clientUrl", c.getClientUrl());
            details.put("description", c.getDescription());
            availableClients.add(details);
        }
        return availableClients;
    }

    private Map<String, CertifiedClient> findAllCertifiedClients() {
        Map<String, CertifiedClient> allClients = new HashMap<String, CertifiedClient>();
        List<CertifiedClient> certifiedClients = clientDetailsService.findAllClients();
        for (CertifiedClient c : certifiedClients) {
            allClients.put(c.getClientId(), c);
        }

        return allClients;
    }

    private List<CertifiedClientDetails> createCollectionOfWhichCertifiedClientsHasUsersWithConfirmedAccess() {
        Map<String, CertifiedClient> allCertifiedClients = new HashMap<String, CertifiedClient>();

        UserDetails user = getUserDetailsFromSecurityContext();

        allCertifiedClients.putAll(findAllCertifiedClients());

        //FindAllUsers
        Collection<String> residentIDs = encryptedResourceOwnerServices
                .findChildrenOfLegalGuardian(user.getUsername());
        residentIDs.add(user.getUsername()); //Add the legal guardian to the first position of this list

        //GetAllAccessTokens for a legalguardian and possible children. The TreeMap keeps the natural orderings of residentId strings
        Map<String, Collection<OAuth2AccessToken>> accessTokens = new TreeMap<String, Collection<OAuth2AccessToken>>();

        for (String residentId : residentIDs) {
            accessTokens.put(residentId, getAccessTokens(residentId));
        }

        //For each and every existing client match against the users accesstokens
        List<CertifiedClientDetails> allDetails = new ArrayList<CertifiedClientDetails>();
        for (Map.Entry<String, CertifiedClient> entry : allCertifiedClients.entrySet()) {
            CertifiedClientDetails certifiedClientDetails = retrieveUserDetailsForCertifiedClient(entry.getKey(),
                    accessTokens);
            if (certifiedClientDetails.getClientId() != null) {
                allDetails.add(certifiedClientDetails);
            }
        }

        return allDetails;
    }

    private CertifiedClientDetails retrieveUserDetailsForCertifiedClient(String clientID,
            Map<String, Collection<OAuth2AccessToken>> accessTokens) {
        SimpleDateFormat formatter = getTimestampFormatter();
        Date now = new Date();

        CertifiedClientDetails certifiedClientDetails = new CertifiedClientDetails();
        for (Map.Entry<String, Collection<OAuth2AccessToken>> entry : accessTokens.entrySet()) {
            //Find all users that match this client
            for (OAuth2AccessToken token : entry.getValue()) {
                if (token.getExpiration() == null || token.getExpiration().before(now)) {
                    continue;
                }
                String tokenValue = token.getValue();
                String userClientID = consumerTokenServices.getClientId(tokenValue);
                if (userClientID.equalsIgnoreCase(clientID)) {
                    UserDetail userDetails = new UserDetail();
                    userDetails.setResidentId(entry.getKey());

                    if (token.getExpiration() != null) {
                        userDetails.setExpires(formatter.format(token.getExpiration()));
                    }
                    String scopes = getScopesString(token.getScope());
                    userDetails.setScopes(scopes);

                    Map<String, Object> addInfo = token.getAdditionalInformation();
                    userDetails.setGrantId(addInfo.get("authorization_grant_id").toString());

                    if (addInfo != null && addInfo.get("issue_date") != null
                            && addInfo.get("issue_date") instanceof Date) {
                        userDetails.setIssued(formatter.format(addInfo.get("issue_date")));
                    }

                    if (certifiedClientDetails.getClientId() != null) {
                        certifiedClientDetails.getUserDetails().add(userDetails);
                    } else {
                        CertifiedClient client = (CertifiedClient) clientDetailsService
                                .loadClientByClientId(userClientID);
                        certifiedClientDetails.setClientId(clientID);
                        certifiedClientDetails.setClientName(client.getName());
                        certifiedClientDetails.setOrganization(client.getOrganization());
                        certifiedClientDetails.setDescription(client.getDescription());
                        certifiedClientDetails.getUserDetails().add(userDetails);
                    }
                }
            }
        }

        return certifiedClientDetails;
    }

    @RequestMapping(value = "/oauth/clients/{clientId}/icon", method = RequestMethod.GET)
    public ResponseEntity<?> getClientIcon(@PathVariable String clientId) {
        log.debug("getClientIcon");
        log.debug("Trying to load client icon");
        CertifiedClientIcon icon = clientDetailsService.findClientIconByClientId(clientId);
        HttpHeaders headers = new HttpHeaders();
        if (icon == null) {
            headers.setContentType(MediaType.TEXT_PLAIN);
            log.debug("getClientIcon: no icon found");
            return new ResponseEntity<String>("No icon found for client with clientId: " + clientId, headers,
                    HttpStatus.NOT_FOUND);
        } else {
            headers.setContentType(MediaType.valueOf(icon.getContentType()));
            log.debug("getClientIcon: returning with icon");
            return new ResponseEntity<byte[]>(icon.getIcon(), headers, HttpStatus.OK);
        }
    }

    Comparator<Map<String, Object>> createClientComparator() {
        return new Comparator<Map<String, Object>>() {
            @Override
            public int compare(Map<String, Object> o1, Map<String, Object> o2) {
                String clientName1 = (String) o1.get("clientName");
                String clientName2 = (String) o2.get("clientName");
                return clientName1.toLowerCase().compareTo(clientName2.toLowerCase());
            }
        };
    }

    private Collection<OAuth2AccessToken> getAccessTokens(String userName) {
        return consumerTokenServices.findTokensByUserName(userName);
    }

    private String getScopesString(Set<String> scopes) {
        StringBuilder scopesString = new StringBuilder();
        boolean first = true;
        for (String permissionName : scopes) {
            Permission permission = permissionServices.getPermissionByName(permissionName);

            if (permission != null) {

                if (!first) {
                    scopesString.append(", ");
                }

                scopesString.append(permission.getDescription());
                first = false;
            }
        }
        return scopesString.toString();
    }

    @RequestMapping(value = "/oauth/confirmRevoke", method = RequestMethod.POST)
    public String confirmRevoke(@RequestParam("clientId") String clientId, @RequestParam("grantId") long grantId,
            Model model) {
        log.debug("confirmRevoke");
        CertifiedClient client = (CertifiedClient) clientDetailsService.loadClientByClientId(clientId);

        model.addAttribute("grantId", grantId);
        model.addAttribute("clientId", client.getClientId());
        model.addAttribute("clientName", client.getName());
        model.addAttribute("organization", client.getOrganization());
        model.addAttribute("description", client.getDescription());
        log.debug("returning from confirmRevoke");
        return ".confirmRevoke";
    }

    @RequestMapping(value = "/oauth/revoke", method = RequestMethod.POST)
    public String revokeAuthorization(@RequestParam("grantId") long grantId) {
        log.debug("revokeAuthorization");
        UserDetails user = getUserDetailsFromSecurityContext();
        Collection<OAuth2AccessToken> tokens = getAccessTokens(user.getUsername());

        String tokenValue = null;
        for (OAuth2AccessToken t : tokens) {
            Map<String, Object> additionalInformation = t.getAdditionalInformation();
            long id = (Long) additionalInformation.get("authorization_grant_id");
            if (grantId == id) {
                tokenValue = t.getValue();
                break;
            }
        }
        if (tokenValue == null) {
            throw new RuntimeException("No token found for grantId=" + grantId); //TODO: throw something better?
        }
        try {
            consumerTokenServices.revokeToken(tokenValue);
        } catch (RuntimeException e) {
            log.error("Caught exception while trying to revoke token", e);
            throw (e);
        }
        log.debug("returning from revokeAuthorization");
        return "redirect:/oauth/clients";
    }

    public SimpleDateFormat getTimestampFormatter() {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm");
    }

}