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}