001/******************************************************************************* 002 * Copyright 2017 The MIT Internet Trust Consortium 003 * 004 * Portions copyright 2011-2013 The MITRE Corporation 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 *******************************************************************************/ 018package org.mitre.oauth2.web; 019 020import java.security.Principal; 021import java.util.List; 022import java.util.Set; 023 024import org.mitre.oauth2.model.ClientDetailsEntity; 025import org.mitre.oauth2.model.OAuth2AccessTokenEntity; 026import org.mitre.oauth2.model.OAuth2RefreshTokenEntity; 027import org.mitre.oauth2.service.ClientDetailsEntityService; 028import org.mitre.oauth2.service.OAuth2TokenEntityService; 029import org.mitre.oauth2.view.TokenApiView; 030import org.mitre.openid.connect.service.OIDCTokenService; 031import org.mitre.openid.connect.view.HttpCodeView; 032import org.mitre.openid.connect.view.JsonEntityView; 033import org.mitre.openid.connect.view.JsonErrorView; 034import org.mitre.openid.connect.web.RootController; 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037import org.springframework.beans.factory.annotation.Autowired; 038import org.springframework.http.HttpStatus; 039import org.springframework.http.MediaType; 040import org.springframework.security.access.prepost.PreAuthorize; 041import org.springframework.stereotype.Controller; 042import org.springframework.ui.ModelMap; 043import org.springframework.web.bind.annotation.PathVariable; 044import org.springframework.web.bind.annotation.RequestMapping; 045import org.springframework.web.bind.annotation.RequestMethod; 046 047/** 048 * REST-ish API for managing access tokens (GET/DELETE only) 049 * @author Amanda Anganes 050 * 051 */ 052@Controller 053@RequestMapping("/" + TokenAPI.URL) 054@PreAuthorize("hasRole('ROLE_USER')") 055public class TokenAPI { 056 057 public static final String URL = RootController.API_URL + "/tokens"; 058 059 @Autowired 060 private OAuth2TokenEntityService tokenService; 061 062 @Autowired 063 private ClientDetailsEntityService clientService; 064 065 @Autowired 066 private OIDCTokenService oidcTokenService; 067 068 /** 069 * Logger for this class 070 */ 071 private static final Logger logger = LoggerFactory.getLogger(TokenAPI.class); 072 073 @RequestMapping(value = "/access", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) 074 public String getAllAccessTokens(ModelMap m, Principal p) { 075 076 Set<OAuth2AccessTokenEntity> allTokens = tokenService.getAllAccessTokensForUser(p.getName()); 077 m.put(JsonEntityView.ENTITY, allTokens); 078 return TokenApiView.VIEWNAME; 079 } 080 081 @RequestMapping(value = "/access/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) 082 public String getAccessTokenById(@PathVariable("id") Long id, ModelMap m, Principal p) { 083 084 OAuth2AccessTokenEntity token = tokenService.getAccessTokenById(id); 085 086 if (token == null) { 087 logger.error("getToken failed; token not found: " + id); 088 m.put(HttpCodeView.CODE, HttpStatus.NOT_FOUND); 089 m.put(JsonErrorView.ERROR_MESSAGE, "The requested token with id " + id + " could not be found."); 090 return JsonErrorView.VIEWNAME; 091 } else if (!token.getAuthenticationHolder().getAuthentication().getName().equals(p.getName())) { 092 logger.error("getToken failed; token does not belong to principal " + p.getName()); 093 m.put(HttpCodeView.CODE, HttpStatus.FORBIDDEN); 094 m.put(JsonErrorView.ERROR_MESSAGE, "You do not have permission to view this token"); 095 return JsonErrorView.VIEWNAME; 096 } else { 097 m.put(JsonEntityView.ENTITY, token); 098 return TokenApiView.VIEWNAME; 099 } 100 } 101 102 @RequestMapping(value = "/access/{id}", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE) 103 public String deleteAccessTokenById(@PathVariable("id") Long id, ModelMap m, Principal p) { 104 105 OAuth2AccessTokenEntity token = tokenService.getAccessTokenById(id); 106 107 if (token == null) { 108 logger.error("getToken failed; token not found: " + id); 109 m.put(HttpCodeView.CODE, HttpStatus.NOT_FOUND); 110 m.put(JsonErrorView.ERROR_MESSAGE, "The requested token with id " + id + " could not be found."); 111 return JsonErrorView.VIEWNAME; 112 } else if (!token.getAuthenticationHolder().getAuthentication().getName().equals(p.getName())) { 113 logger.error("getToken failed; token does not belong to principal " + p.getName()); 114 m.put(HttpCodeView.CODE, HttpStatus.FORBIDDEN); 115 m.put(JsonErrorView.ERROR_MESSAGE, "You do not have permission to view this token"); 116 return JsonErrorView.VIEWNAME; 117 } else { 118 tokenService.revokeAccessToken(token); 119 120 return HttpCodeView.VIEWNAME; 121 } 122 } 123 124 @PreAuthorize("hasRole('ROLE_ADMIN')") 125 @RequestMapping(value = "/client/{clientId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) 126 public String getAccessTokensByClientId(@PathVariable("clientId") String clientId, ModelMap m, Principal p) { 127 128 ClientDetailsEntity client = clientService.loadClientByClientId(clientId); 129 130 if (client != null) { 131 List<OAuth2AccessTokenEntity> tokens = tokenService.getAccessTokensForClient(client); 132 m.put(JsonEntityView.ENTITY, tokens); 133 return TokenApiView.VIEWNAME; 134 } else { 135 // client not found 136 m.put(HttpCodeView.CODE, HttpStatus.NOT_FOUND); 137 m.put(JsonErrorView.ERROR_MESSAGE, "The requested client with id " + clientId + " could not be found."); 138 return JsonErrorView.VIEWNAME; 139 } 140 141 } 142 143 @PreAuthorize("hasRole('ROLE_ADMIN')") 144 @RequestMapping(value = "/registration/{clientId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) 145 public String getRegistrationTokenByClientId(@PathVariable("clientId") String clientId, ModelMap m, Principal p) { 146 147 ClientDetailsEntity client = clientService.loadClientByClientId(clientId); 148 149 if (client != null) { 150 OAuth2AccessTokenEntity token = tokenService.getRegistrationAccessTokenForClient(client); 151 if (token != null) { 152 m.put(JsonEntityView.ENTITY, token); 153 return TokenApiView.VIEWNAME; 154 } else { 155 m.put(HttpCodeView.CODE, HttpStatus.NOT_FOUND); 156 m.put(JsonErrorView.ERROR_MESSAGE, "No registration token could be found."); 157 return JsonErrorView.VIEWNAME; 158 } 159 } else { 160 // client not found 161 m.put(HttpCodeView.CODE, HttpStatus.NOT_FOUND); 162 m.put(JsonErrorView.ERROR_MESSAGE, "The requested client with id " + clientId + " could not be found."); 163 return JsonErrorView.VIEWNAME; 164 } 165 166 } 167 168 @PreAuthorize("hasRole('ROLE_ADMIN')") 169 @RequestMapping(value = "/registration/{clientId}", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE) 170 public String rotateRegistrationTokenByClientId(@PathVariable("clientId") String clientId, ModelMap m, Principal p) { 171 ClientDetailsEntity client = clientService.loadClientByClientId(clientId); 172 173 if (client != null) { 174 OAuth2AccessTokenEntity token = oidcTokenService.rotateRegistrationAccessTokenForClient(client); 175 token = tokenService.saveAccessToken(token); 176 177 if (token != null) { 178 m.put(JsonEntityView.ENTITY, token); 179 return TokenApiView.VIEWNAME; 180 } else { 181 m.put(HttpCodeView.CODE, HttpStatus.NOT_FOUND); 182 m.put(JsonErrorView.ERROR_MESSAGE, "No registration token could be found."); 183 return JsonErrorView.VIEWNAME; 184 } 185 } else { 186 // client not found 187 m.put(HttpCodeView.CODE, HttpStatus.NOT_FOUND); 188 m.put(JsonErrorView.ERROR_MESSAGE, "The requested client with id " + clientId + " could not be found."); 189 return JsonErrorView.VIEWNAME; 190 } 191 192 } 193 194 @RequestMapping(value = "/refresh", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) 195 public String getAllRefreshTokens(ModelMap m, Principal p) { 196 197 Set<OAuth2RefreshTokenEntity> allTokens = tokenService.getAllRefreshTokensForUser(p.getName()); 198 m.put(JsonEntityView.ENTITY, allTokens); 199 return TokenApiView.VIEWNAME; 200 201 202 } 203 204 @RequestMapping(value = "/refresh/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) 205 public String getRefreshTokenById(@PathVariable("id") Long id, ModelMap m, Principal p) { 206 207 OAuth2RefreshTokenEntity token = tokenService.getRefreshTokenById(id); 208 209 if (token == null) { 210 logger.error("refresh token not found: " + id); 211 m.put(HttpCodeView.CODE, HttpStatus.NOT_FOUND); 212 m.put(JsonErrorView.ERROR_MESSAGE, "The requested token with id " + id + " could not be found."); 213 return JsonErrorView.VIEWNAME; 214 } else if (!token.getAuthenticationHolder().getAuthentication().getName().equals(p.getName())) { 215 logger.error("refresh token " + id + " does not belong to principal " + p.getName()); 216 m.put(HttpCodeView.CODE, HttpStatus.FORBIDDEN); 217 m.put(JsonErrorView.ERROR_MESSAGE, "You do not have permission to view this token"); 218 return JsonErrorView.VIEWNAME; 219 } else { 220 m.put(JsonEntityView.ENTITY, token); 221 return TokenApiView.VIEWNAME; 222 } 223 } 224 225 @RequestMapping(value = "/refresh/{id}", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE) 226 public String deleteRefreshTokenById(@PathVariable("id") Long id, ModelMap m, Principal p) { 227 228 OAuth2RefreshTokenEntity token = tokenService.getRefreshTokenById(id); 229 230 if (token == null) { 231 logger.error("refresh token not found: " + id); 232 m.put(HttpCodeView.CODE, HttpStatus.NOT_FOUND); 233 m.put(JsonErrorView.ERROR_MESSAGE, "The requested token with id " + id + " could not be found."); 234 return JsonErrorView.VIEWNAME; 235 } else if (!token.getAuthenticationHolder().getAuthentication().getName().equals(p.getName())) { 236 logger.error("refresh token " + id + " does not belong to principal " + p.getName()); 237 m.put(HttpCodeView.CODE, HttpStatus.FORBIDDEN); 238 m.put(JsonErrorView.ERROR_MESSAGE, "You do not have permission to view this token"); 239 return JsonErrorView.VIEWNAME; 240 } else { 241 tokenService.revokeRefreshToken(token); 242 243 return HttpCodeView.VIEWNAME; 244 } 245 } 246}