Java tutorial
/** * 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"); } }