blob: 38e747479c8eda1a3463f1a0410e982e1b8ef270 [file] [log] [blame]
margaretha31a9f522018-04-03 20:40:45 +02001package de.ids_mannheim.korap.web.controller;
2
margaretha230effb2018-11-29 17:28:18 +01003import java.util.List;
4
margaretha31a9f522018-04-03 20:40:45 +02005import javax.ws.rs.Consumes;
margaretha8d804f62018-04-10 12:39:56 +02006import javax.ws.rs.DELETE;
7import javax.ws.rs.FormParam;
margaretha835178d2018-08-15 19:04:03 +02008import javax.ws.rs.GET;
margaretha31a9f522018-04-03 20:40:45 +02009import javax.ws.rs.POST;
10import javax.ws.rs.Path;
margaretha80ea0dd2018-07-03 14:22:59 +020011import javax.ws.rs.PathParam;
margaretha31a9f522018-04-03 20:40:45 +020012import javax.ws.rs.Produces;
13import javax.ws.rs.core.Context;
14import javax.ws.rs.core.MediaType;
margaretha8d804f62018-04-10 12:39:56 +020015import javax.ws.rs.core.Response;
margaretha31a9f522018-04-03 20:40:45 +020016import javax.ws.rs.core.SecurityContext;
17
18import org.springframework.beans.factory.annotation.Autowired;
19import org.springframework.stereotype.Controller;
20
21import com.sun.jersey.spi.container.ResourceFilters;
22
margaretha2df06602018-11-14 19:10:30 +010023import de.ids_mannheim.korap.constant.OAuth2Scope;
margaretha31a9f522018-04-03 20:40:45 +020024import de.ids_mannheim.korap.exceptions.KustvaktException;
margaretha835178d2018-08-15 19:04:03 +020025import de.ids_mannheim.korap.oauth2.dto.OAuth2ClientDto;
26import de.ids_mannheim.korap.oauth2.dto.OAuth2ClientInfoDto;
margaretha230effb2018-11-29 17:28:18 +010027import de.ids_mannheim.korap.oauth2.dto.OAuth2UserClientDto;
margarethaa452c5e2018-04-25 22:48:09 +020028import de.ids_mannheim.korap.oauth2.service.OAuth2ClientService;
margaretha835178d2018-08-15 19:04:03 +020029import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
margaretha0e8f4e72018-04-05 14:11:52 +020030import de.ids_mannheim.korap.security.context.TokenContext;
margarethaf839dde2018-04-16 17:52:57 +020031import de.ids_mannheim.korap.web.OAuth2ResponseHandler;
margarethaee0cbfe2018-08-28 17:47:14 +020032import de.ids_mannheim.korap.web.APIVersionFilter;
margaretha31a9f522018-04-03 20:40:45 +020033import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
34import de.ids_mannheim.korap.web.filter.BlockingFilter;
35import de.ids_mannheim.korap.web.input.OAuth2ClientJson;
36
margarethaec247dd2018-06-12 21:55:46 +020037/**
38 * Defines controllers for OAuth2 clients, namely applications
margaretha7f5071f2018-08-14 15:58:51 +020039 * performing actions such as searching and retrieving match
40 * information on behalf of users.
margarethaa0486272018-04-12 19:59:31 +020041 *
margaretha835178d2018-08-15 19:04:03 +020042 * <br /><br />
43 * According to its privileges, clients are categorized into super and
44 * normal clients. Super clients are intended only for clients that
45 * are part of KorAP. They has special privileges to use controllers
46 * that usually are not allowed for normal clients, for instance using
47 * OAuth2 password grant to obtain access tokens.
48 *
49 * <br /><br />
50 * By default, clients are set as normal clients. Super clients has to
51 * be set manually by an admin, e.g by using
52 * {@link #updateClientPrivilege(SecurityContext, String, boolean)}
53 * controller. Only confidential clients are allowed to be super
54 * clients.
55 *
margarethaa0486272018-04-12 19:59:31 +020056 * @author margaretha
57 *
58 */
margaretha31a9f522018-04-03 20:40:45 +020059@Controller
margarethaee0cbfe2018-08-28 17:47:14 +020060@Path("{version}/oauth2/client")
margaretha230effb2018-11-29 17:28:18 +010061@ResourceFilters({ APIVersionFilter.class, AuthenticationFilter.class,
62 BlockingFilter.class })
margaretha31a9f522018-04-03 20:40:45 +020063public class OAuthClientController {
64
65 @Autowired
66 private OAuth2ClientService clientService;
67 @Autowired
margaretha835178d2018-08-15 19:04:03 +020068 private OAuth2ScopeService scopeService;
69 @Autowired
margarethaf839dde2018-04-16 17:52:57 +020070 private OAuth2ResponseHandler responseHandler;
margaretha31a9f522018-04-03 20:40:45 +020071
margarethaec247dd2018-06-12 21:55:46 +020072 /**
73 * Registers a client application. Before starting an OAuth
margarethae4034a82018-07-02 14:46:59 +020074 * process, client applications have to be registered first. Only
75 * registered users are allowed to register client applications.
76 *
77 * After registration, the client receives a client_id and a
78 * client_secret, if the client is confidential (capable of
79 * storing the client_secret), that are needed in the
80 * authorization process.
margaretha31a9f522018-04-03 20:40:45 +020081 *
margaretha0e8f4e72018-04-05 14:11:52 +020082 * From RFC 6749:
margarethaec247dd2018-06-12 21:55:46 +020083 * The authorization server SHOULD document the size of any
margarethae4034a82018-07-02 14:46:59 +020084 * identifier it issues.
margaretha31a9f522018-04-03 20:40:45 +020085 *
86 * @param context
margarethaec247dd2018-06-12 21:55:46 +020087 * @param clientJson
88 * a JSON object describing the client
89 * @return client_id and client_secret if the client type is
90 * confidential
margarethaa0486272018-04-12 19:59:31 +020091 *
92 * @see OAuth2ClientJson
margaretha31a9f522018-04-03 20:40:45 +020093 */
94 @POST
95 @Path("register")
96 @Consumes(MediaType.APPLICATION_JSON)
margaretha0e8f4e72018-04-05 14:11:52 +020097 @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
margaretha0e8f4e72018-04-05 14:11:52 +020098 public OAuth2ClientDto registerClient (
99 @Context SecurityContext securityContext,
margaretha31a9f522018-04-03 20:40:45 +0200100 OAuth2ClientJson clientJson) {
margaretha0e8f4e72018-04-05 14:11:52 +0200101 TokenContext context =
102 (TokenContext) securityContext.getUserPrincipal();
margaretha31a9f522018-04-03 20:40:45 +0200103 try {
margaretha835178d2018-08-15 19:04:03 +0200104 scopeService.verifyScope(context, OAuth2Scope.REGISTER_CLIENT);
margaretha0e8f4e72018-04-05 14:11:52 +0200105 return clientService.registerClient(clientJson,
106 context.getUsername());
margaretha31a9f522018-04-03 20:40:45 +0200107 }
108 catch (KustvaktException e) {
margaretha0e8f4e72018-04-05 14:11:52 +0200109 throw responseHandler.throwit(e);
margaretha31a9f522018-04-03 20:40:45 +0200110 }
margaretha31a9f522018-04-03 20:40:45 +0200111 }
112
margarethaec247dd2018-06-12 21:55:46 +0200113 /**
margaretha80ea0dd2018-07-03 14:22:59 +0200114 * Deregisters a client requires client owner authentication. For
115 * confidential clients, client authentication is also required.
margaretha8d804f62018-04-10 12:39:56 +0200116 *
117 *
118 * @param securityContext
margarethaec247dd2018-06-12 21:55:46 +0200119 * @param clientId
120 * the client id
margaretha80ea0dd2018-07-03 14:22:59 +0200121 * @param clientSecret
122 * the client secret
margaretha8d804f62018-04-10 12:39:56 +0200123 * @return HTTP Response OK if successful.
124 */
125 @DELETE
margaretha80ea0dd2018-07-03 14:22:59 +0200126 @Path("deregister/{client_id}")
margaretha8d804f62018-04-10 12:39:56 +0200127 @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
margarethafb1e0992018-04-10 14:58:28 +0200128 public Response deregisterPublicClient (
margaretha8d804f62018-04-10 12:39:56 +0200129 @Context SecurityContext securityContext,
margaretha80ea0dd2018-07-03 14:22:59 +0200130 @PathParam("client_id") String clientId,
131 @FormParam("client_secret") String clientSecret) {
margaretha8d804f62018-04-10 12:39:56 +0200132 TokenContext context =
133 (TokenContext) securityContext.getUserPrincipal();
134 try {
margaretha835178d2018-08-15 19:04:03 +0200135 scopeService.verifyScope(context, OAuth2Scope.DEREGISTER_CLIENT);
margaretha80ea0dd2018-07-03 14:22:59 +0200136 clientService.deregisterClient(clientId, clientSecret,
margaretha8d804f62018-04-10 12:39:56 +0200137 context.getUsername());
138 return Response.ok().build();
139 }
140 catch (KustvaktException e) {
141 throw responseHandler.throwit(e);
142 }
143 }
margaretha7f5071f2018-08-14 15:58:51 +0200144
145 /**
146 * Resets client secret of the given client. This controller
147 * requires client owner and client authentication. Only
148 * confidential clients are issued client secrets.
149 *
150 * @param securityContext
151 * @param clientId
152 * @param clientSecret
153 * @return a new client secret
154 */
155 @POST
156 @Path("reset")
157 @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
158 @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
margaretha7f5071f2018-08-14 15:58:51 +0200159 public OAuth2ClientDto resetClientSecret (
160 @Context SecurityContext securityContext,
161 @FormParam("client_id") String clientId,
162 @FormParam("client_secret") String clientSecret) {
163 TokenContext context =
164 (TokenContext) securityContext.getUserPrincipal();
165 try {
margaretha835178d2018-08-15 19:04:03 +0200166 scopeService.verifyScope(context, OAuth2Scope.RESET_CLIENT_SECRET);
margaretha7f5071f2018-08-14 15:58:51 +0200167 return clientService.resetSecret(clientId, clientSecret,
168 context.getUsername());
169 }
170 catch (KustvaktException e) {
171 throw responseHandler.throwit(e);
172 }
173 }
174
margaretha835178d2018-08-15 19:04:03 +0200175 /**
176 * Facilitates editing client privileges for admin purposes, e.g.
margarethabdddbaf2018-08-15 19:08:33 +0200177 * setting a specific client to be a super client.
margaretha835178d2018-08-15 19:04:03 +0200178 * Only confidential clients are allowed to be super clients.
179 *
margarethaf0085122018-08-16 16:19:53 +0200180 * When upgrading clients to super clients, existing access tokens
181 * and authorization codes retain their scopes.
182 *
183 * When degrading super clients, all existing tokens and
184 * authorization codes are invalidated.
185 *
margaretha835178d2018-08-15 19:04:03 +0200186 * @param securityContext
margaretha230effb2018-11-29 17:28:18 +0100187 * @param clientId
188 * OAuth2 client id
margarethaf0085122018-08-16 16:19:53 +0200189 * @param super
190 * true indicating super client, false otherwise
191 * @return Response status OK, if successful
margaretha835178d2018-08-15 19:04:03 +0200192 */
193 @POST
194 @Path("privilege")
195 @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
196 public Response updateClientPrivilege (
197 @Context SecurityContext securityContext,
198 @FormParam("client_id") String clientId,
199 @FormParam("super") String isSuper) {
200 TokenContext context =
201 (TokenContext) securityContext.getUserPrincipal();
202 try {
203 scopeService.verifyScope(context, OAuth2Scope.ADMIN);
204 clientService.updatePrivilege(context.getUsername(), clientId,
margarethaf0085122018-08-16 16:19:53 +0200205 Boolean.valueOf(isSuper));
margaretha835178d2018-08-15 19:04:03 +0200206 return Response.ok().build();
207 }
208 catch (KustvaktException e) {
209 throw responseHandler.throwit(e);
210 }
211 }
212
213 @GET
214 @Path("info/{client_id}")
215 @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
216 public OAuth2ClientInfoDto retrieveClientInfo (
217 @Context SecurityContext securityContext,
218 @PathParam("client_id") String clientId) {
219 TokenContext context =
220 (TokenContext) securityContext.getUserPrincipal();
221 try {
222 scopeService.verifyScope(context, OAuth2Scope.CLIENT_INFO);
223 return clientService.retrieveClientInfo(context.getUsername(),
224 clientId);
225 }
226 catch (KustvaktException e) {
227 throw responseHandler.throwit(e);
228 }
229 }
margaretha230effb2018-11-29 17:28:18 +0100230
231 /**
margaretha5a2c34e2018-11-29 19:35:13 +0100232 * Lists user clients having active refresh tokens (not revoked,
233 * not expired), except super clients.
234 *
235 * This service is not part of the OAuth2 specification. It is
236 * intended to facilitate users revoking any suspicious and
237 * misused access or refresh tokens.
margaretha230effb2018-11-29 17:28:18 +0100238 *
239 * Only super clients are allowed to use this service. It requires
240 * user and client authentications.
241 *
242 * @param context
243 * @return a list of clients having refresh tokens of the
244 * given user
245 */
246 @POST
247 @Path("list")
248 @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
249 @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
250 @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
251 public List<OAuth2UserClientDto> listUserApp (
252 @Context SecurityContext context,
253 @FormParam("client_id") String clientId,
254 @FormParam("client_secret") String clientSecret) {
255
256 TokenContext tokenContext = (TokenContext) context.getUserPrincipal();
257 String username = tokenContext.getUsername();
258
259 try {
260 scopeService.verifyScope(tokenContext,
261 OAuth2Scope.LIST_USER_CLIENT);
262
263 return clientService.listUserClients(username, clientId,
264 clientSecret);
265 }
266 catch (KustvaktException e) {
267 throw responseHandler.throwit(e);
268 }
269 }
270
margaretha31a9f522018-04-03 20:40:45 +0200271}