blob: 5e5e2cfc42e3e594dea6279b7353123ea9f8790b [file] [log] [blame]
Michael Hanl19390652016-01-16 11:01:24 +01001package de.ids_mannheim.korap.web.service.full;
Michael Hanlfb839b92015-09-19 21:32:34 +02002
3import com.sun.jersey.spi.container.ContainerRequest;
4import com.sun.jersey.spi.container.ResourceFilters;
5import de.ids_mannheim.korap.config.*;
6import de.ids_mannheim.korap.exceptions.KustvaktException;
7import de.ids_mannheim.korap.exceptions.StatusCodes;
8import de.ids_mannheim.korap.handlers.OAuth2Handler;
9import de.ids_mannheim.korap.interfaces.AuthenticationManagerIface;
10import de.ids_mannheim.korap.interfaces.EncryptionIface;
Michael Hanlc2a9f622016-01-28 16:40:06 +010011import de.ids_mannheim.korap.user.*;
Michael Hanlfb839b92015-09-19 21:32:34 +020012import de.ids_mannheim.korap.utils.JsonUtils;
13import de.ids_mannheim.korap.utils.StringUtils;
14import de.ids_mannheim.korap.web.KustvaktServer;
15import de.ids_mannheim.korap.web.filter.AuthFilter;
Michael Hanl482f30d2015-09-25 12:39:46 +020016import de.ids_mannheim.korap.web.filter.BlockingFilter;
Michael Hanlfb839b92015-09-19 21:32:34 +020017import de.ids_mannheim.korap.web.filter.DefaultFilter;
18import de.ids_mannheim.korap.web.filter.PiwikFilter;
19import de.ids_mannheim.korap.web.utils.FormRequestWrapper;
Michael Hanl482f30d2015-09-25 12:39:46 +020020import de.ids_mannheim.korap.web.utils.KustvaktResponseHandler;
Michael Hanlfb839b92015-09-19 21:32:34 +020021import org.apache.oltu.oauth2.as.issuer.MD5Generator;
22import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
23import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
24import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
25import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
26import org.apache.oltu.oauth2.as.response.OAuthASResponse;
27import org.apache.oltu.oauth2.common.OAuth;
28import org.apache.oltu.oauth2.common.error.OAuthError;
29import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
30import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
31import org.apache.oltu.oauth2.common.message.OAuthResponse;
32import org.apache.oltu.oauth2.common.message.types.GrantType;
33import org.apache.oltu.oauth2.common.message.types.ResponseType;
34import org.apache.oltu.oauth2.common.message.types.TokenType;
35import org.apache.oltu.oauth2.common.utils.OAuthUtils;
36
37import javax.servlet.http.HttpServletRequest;
38import javax.servlet.http.HttpServletResponse;
39import javax.ws.rs.*;
40import javax.ws.rs.core.Context;
41import javax.ws.rs.core.MultivaluedMap;
42import javax.ws.rs.core.Response;
43import javax.ws.rs.core.SecurityContext;
44import java.net.URI;
45import java.net.URISyntaxException;
46import java.util.Collection;
47import java.util.HashMap;
48import java.util.Map;
49import java.util.Set;
50
51/**
52 * @author hanl
53 * @date 07/06/2014
54 */
55//todo: only allow oauth2 access_token requests GET methods?
56//todo: allow refresh tokens
57@Path(KustvaktServer.API_VERSION + "/oauth2")
58//@ResourceFilters({ AccessLevelFilter.class, PiwikFilter.class })
59public class OAuthService {
60
61 private OAuth2Handler handler;
62 private AuthenticationManagerIface controller;
63 private EncryptionIface crypto;
64 private KustvaktConfiguration config;
65
66 public OAuthService() {
67 this.handler = new OAuth2Handler(
68 BeanConfiguration.getBeans().getPersistenceClient());
69 this.controller = BeanConfiguration.getBeans()
70 .getAuthenticationManager();
71 this.crypto = BeanConfiguration.getBeans().getEncryption();
72 this.config = BeanConfiguration.getBeans().getConfiguration();
73 }
74
75 @POST
76 @Path("unregister")
Michael Hanl482f30d2015-09-25 12:39:46 +020077 @ResourceFilters({ AuthFilter.class, BlockingFilter.class })
Michael Hanlfb839b92015-09-19 21:32:34 +020078 public Response unregisterClient(@Context SecurityContext context,
79 @HeaderParam("Host") String host,
80 @QueryParam("client_secret") String secret,
81 @QueryParam("client_id") String client_id) {
82 ClientInfo info = new ClientInfo(client_id, secret);
83 info.setUrl(host);
84 TokenContext ctx = (TokenContext) context.getUserPrincipal();
85 try {
86 this.handler.removeClient(info,
87 this.controller.getUser(ctx.getUsername()));
88 }catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +020089 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +020090 }
91 return Response.ok().build();
92 }
93
94 @POST
95 @Path("register")
Michael Hanl482f30d2015-09-25 12:39:46 +020096 @ResourceFilters({ AuthFilter.class, BlockingFilter.class })
Michael Hanlfb839b92015-09-19 21:32:34 +020097 public Response registerClient(@Context SecurityContext context,
98 @HeaderParam("Host") String host,
99 @QueryParam("redirect_url") String rurl) {
100 ClientInfo info = new ClientInfo(crypto.createID(),
101 crypto.createToken());
102 info.setUrl(host);
103 if (rurl == null)
Michael Hanl482f30d2015-09-25 12:39:46 +0200104 throw KustvaktResponseHandler
Michael Hanlfb839b92015-09-19 21:32:34 +0200105 .throwit(StatusCodes.ILLEGAL_ARGUMENT, "Missing parameter!",
106 "redirect_url");
107 info.setRedirect_uri(rurl);
108 TokenContext ctx = (TokenContext) context.getUserPrincipal();
109 try {
110 this.handler.registerClient(info,
111 this.controller.getUser(ctx.getUsername()));
112 }catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200113 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200114 }
115 return Response.ok(info.toJSON()).build();
116 }
117
118 @GET
119 @Path("info")
120 @ResourceFilters({ AuthFilter.class, DefaultFilter.class,
121 PiwikFilter.class })
122 public Response getStatus(@Context SecurityContext context,
Michael Hanl19390652016-01-16 11:01:24 +0100123 @QueryParam("scope") String scopes) {
Michael Hanlfb839b92015-09-19 21:32:34 +0200124 TokenContext ctx = (TokenContext) context.getUserPrincipal();
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100125 Map<String, Object> details;
Michael Hanlfb839b92015-09-19 21:32:34 +0200126 try {
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100127 User user = this.controller.getUser(ctx.getUsername());
Michael Hanlc2a9f622016-01-28 16:40:06 +0100128 Userdata data = this.controller
Michael Hanl5dd931a2016-01-29 16:40:38 +0100129 .getUserData(user, UserDetails.class);
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100130 details = data.fields();
Michael Hanl19390652016-01-16 11:01:24 +0100131 Set<String> base_scope = StringUtils.toSet(scopes, " ");
Michael Hanlfb839b92015-09-19 21:32:34 +0200132 base_scope.retainAll(StringUtils.toSet(scopes));
133 scopes = StringUtils.toString(base_scope);
134 }catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200135 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200136 }
137 // json format with scope callback parameter
Michael Hanl19390652016-01-16 11:01:24 +0100138 // todo: add other scopes as well!
Michael Hanlc2a9f622016-01-28 16:40:06 +0100139 return Response.ok(JsonUtils
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100140 .toJSON(Scopes.mapScopes(scopes, details))).build();
Michael Hanlfb839b92015-09-19 21:32:34 +0200141 }
142
143 @GET
144 @Path("authorizations")
Michael Hanl482f30d2015-09-25 12:39:46 +0200145 @ResourceFilters({ AuthFilter.class, BlockingFilter.class })
Michael Hanlfb839b92015-09-19 21:32:34 +0200146 public Response getAuthorizations(@Context SecurityContext context,
147 @HeaderParam(ContainerRequest.USER_AGENT) String agent,
148 @HeaderParam(ContainerRequest.HOST) String host) {
149 // works on all tokens, but access to native apps cannot be revoked!
150 // check secret and id and retrieve access tokens
151 TokenContext ctx = (TokenContext) context.getUserPrincipal();
152 try {
153 User user = this.controller.getUser(ctx.getUsername());
154 Collection auths = this.handler.getAuthorizedClients(user.getId());
155 if (auths.isEmpty())
156 return Response.noContent().build();
157 return Response.ok(JsonUtils.toJSON(auths)).build();
158 }catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200159 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200160 }
161 }
162
163 // todo: scopes for access_token are defined here
Michael Hanl482f30d2015-09-25 12:39:46 +0200164 // todo: if user already has an access token registered for client and application, then redirect to token endpoint to retrieve that token
Michael Hanlfb839b92015-09-19 21:32:34 +0200165 // todo: demo account should be disabled for this function --> if authentication failed, client must redirect to login url (little login window)
166 @POST
167 @Path("authorize")
168 @Consumes("application/x-www-form-urlencoded")
169 @Produces("application/json")
Michael Hanl482f30d2015-09-25 12:39:46 +0200170 @ResourceFilters({ BlockingFilter.class })
Michael Hanlfb839b92015-09-19 21:32:34 +0200171 public Response authorize(@Context HttpServletRequest request,
172 @Context SecurityContext context,
173 @HeaderParam(ContainerRequest.USER_AGENT) String agent,
174 @HeaderParam(ContainerRequest.HOST) String host,
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100175 MultivaluedMap<String, Object> form)
Michael Hanlfb839b92015-09-19 21:32:34 +0200176 throws OAuthSystemException, URISyntaxException {
177 // user needs to be authenticated to this service!
178 TokenContext c = (TokenContext) context.getUserPrincipal();
179
180 try {
181 OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(
182 new FormRequestWrapper(request, form));
183 OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(
184 new MD5Generator());
185 User user;
186
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100187 Map<String, Object> attr = new HashMap<>();
Michael Hanlfb839b92015-09-19 21:32:34 +0200188 attr.put(Attributes.HOST, host);
189 attr.put(Attributes.USER_AGENT, agent);
190 attr.put(Attributes.USERNAME, c.getUsername());
191 // also extractable via authorization header
192 attr.put(Attributes.CLIENT_ID, oauthRequest.getClientId());
193 attr.put(Attributes.CLIENT_SECRET, oauthRequest.getClientSecret());
194 StringBuilder scopes = new StringBuilder();
195 for (String scope : oauthRequest.getScopes())
196 scopes.append(scope + " ");
197 attr.put(Attributes.SCOPES, scopes.toString());
198
199 try {
200 user = controller.getUser(c.getUsername());
Michael Hanlc2a9f622016-01-28 16:40:06 +0100201 Userdata data = controller
Michael Hanl5dd931a2016-01-29 16:40:38 +0100202 .getUserData(user, UserDetails.class);
Michael Hanlc2a9f622016-01-28 16:40:06 +0100203 user.addUserData(data);
Michael Hanlfb839b92015-09-19 21:32:34 +0200204 }catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200205 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200206 }
207
208 // register response according to response_type
209 String responseType = oauthRequest
210 .getParam(OAuth.OAUTH_RESPONSE_TYPE);
211
Michael Hanlfb839b92015-09-19 21:32:34 +0200212 final String authorizationCode = oauthIssuerImpl
213 .authorizationCode();
214 ClientInfo info = this.handler
215 .getClient(oauthRequest.getClientId());
216
217 if (info == null || !info.getClient_secret()
218 .equals(oauthRequest.getClientSecret())) {
219 OAuthResponse res = OAuthASResponse
220 .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
221 .setError(OAuthError.CodeResponse.UNAUTHORIZED_CLIENT)
222 .setErrorDescription("Unauthorized client!\n")
223 .buildJSONMessage();
224 return Response.status(res.getResponseStatus())
225 .entity(res.getBody()).build();
226 }
227
228 if (!info.getRedirect_uri()
229 .contains(oauthRequest.getRedirectURI())) {
230 OAuthResponse res = OAuthASResponse
231 .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
232 .setError(OAuthError.CodeResponse.INVALID_REQUEST)
233 .setErrorDescription("Unauthorized redirect!\n")
234 .buildJSONMessage();
235 return Response.status(res.getResponseStatus())
236 .entity(res.getBody()).build();
237 }
238
239 String accessToken = this.handler
240 .getToken(oauthRequest.getClientId(), user.getId());
Michael Hanl482f30d2015-09-25 12:39:46 +0200241
242 //todo: test correct redirect and parameters
Michael Hanlfb839b92015-09-19 21:32:34 +0200243 if (accessToken != null) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200244 // fixme: correct status code?
245 OAuthASResponse.OAuthResponseBuilder builder = OAuthASResponse
246 .status(HttpServletResponse.SC_FOUND);
Michael Hanlfb839b92015-09-19 21:32:34 +0200247 final OAuthResponse response = builder.location("/oauth2/token")
248 .setParam(OAuth.OAUTH_CLIENT_ID,
249 oauthRequest.getClientId())
250 .setParam(OAuth.OAUTH_CLIENT_SECRET,
251 oauthRequest.getClientSecret())
252 .buildQueryMessage();
253 return Response.status(response.getResponseStatus())
254 .location(new URI(response.getLocationUri())).build();
255 }
256
Michael Hanl482f30d2015-09-25 12:39:46 +0200257 final OAuthResponse response;
258 String redirectURI = oauthRequest.getRedirectURI();
259 if (OAuthUtils.isEmpty(redirectURI)) {
260 throw new WebApplicationException(
261 Response.status(HttpServletResponse.SC_BAD_REQUEST)
262 .entity("OAuth callback url needs to be provided by client!!!\n")
263 .build());
264 }
265
Michael Hanlfb839b92015-09-19 21:32:34 +0200266 if (responseType.equals(ResponseType.CODE.toString())) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200267 OAuthASResponse.OAuthAuthorizationResponseBuilder builder = OAuthASResponse
268 .authorizationResponse(request,
269 HttpServletResponse.SC_FOUND);
270 builder.location(redirectURI);
271
Michael Hanlfb839b92015-09-19 21:32:34 +0200272 try {
273 AuthCodeInfo codeInfo = new AuthCodeInfo(
274 info.getClient_id(), authorizationCode);
275 codeInfo.setScopes(StringUtils
276 .toString(oauthRequest.getScopes(), " "));
277 this.handler.authorize(codeInfo, user);
278 }catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200279 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200280 }
281 builder.setParam(OAuth.OAUTH_RESPONSE_TYPE,
282 ResponseType.CODE.toString());
283 builder.setCode(authorizationCode);
Michael Hanl482f30d2015-09-25 12:39:46 +0200284 response = builder.buildBodyMessage();
Michael Hanlfb839b92015-09-19 21:32:34 +0200285
286 }else if (responseType.contains(ResponseType.TOKEN.toString())) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200287 OAuthASResponse.OAuthTokenResponseBuilder builder = OAuthASResponse
288 .tokenResponse(HttpServletResponse.SC_OK);
Michael Hanlfb839b92015-09-19 21:32:34 +0200289 builder.setParam(OAuth.OAUTH_RESPONSE_TYPE,
290 ResponseType.TOKEN.toString());
Michael Hanl482f30d2015-09-25 12:39:46 +0200291 builder.location(redirectURI);
Michael Hanlfb839b92015-09-19 21:32:34 +0200292
293 String token = oauthIssuerImpl.accessToken();
Michael Hanl482f30d2015-09-25 12:39:46 +0200294 String refresh = oauthIssuerImpl.refreshToken();
295
296 this.handler.addToken(token, refresh, user.getId(),
297 oauthRequest.getClientId(),
298 StringUtils.toString(oauthRequest.getScopes(), " "),
Michael Hanlfb839b92015-09-19 21:32:34 +0200299 config.getLongTokenTTL());
300 builder.setAccessToken(token);
Michael Hanl482f30d2015-09-25 12:39:46 +0200301 builder.setRefreshToken(refresh);
302 builder.setExpiresIn(String.valueOf(config.getLongTokenTTL()));
Michael Hanlfb839b92015-09-19 21:32:34 +0200303
304 // skips authorization code type and returns id_token and access token directly
305 if (oauthRequest.getScopes().contains("openid")) {
306 try {
307 TokenContext new_context = this.controller
308 .createTokenContext(user, attr, null);
309 builder.setParam(new_context.getTokenType(),
310 new_context.getToken());
311 }catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200312 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200313 }
314 }
Michael Hanl482f30d2015-09-25 12:39:46 +0200315 response = builder.buildBodyMessage();
Michael Hanlfb839b92015-09-19 21:32:34 +0200316 }else {
317 OAuthResponse res = OAuthASResponse
318 .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
319 .setError(
320 OAuthError.CodeResponse.UNSUPPORTED_RESPONSE_TYPE)
321 .setErrorDescription("Unsupported Response type!\n")
322 .buildJSONMessage();
323 return Response.status(res.getResponseStatus())
324 .entity(res.getBody()).build();
325 }
Michael Hanl482f30d2015-09-25 12:39:46 +0200326 //
327 // String redirectURI = oauthRequest.getRedirectURI();
328 //
329 // // enables state parameter to disable cross-site scripting attacks
330 // final OAuthResponse response = builder.location(redirectURI)
331 // .buildQueryMessage();
332 // if (OAuthUtils.isEmpty(redirectURI)) {
333 // throw new WebApplicationException(
334 // Response.status(HttpServletResponse.SC_BAD_REQUEST)
335 // .entity("OAuth callback url needs to be provided by client!!!\n")
336 // .build());
337 // }
Michael Hanlfb839b92015-09-19 21:32:34 +0200338
339 return Response.status(response.getResponseStatus())
340 .location(new URI(response.getLocationUri())).build();
341 }catch (OAuthProblemException e) {
Michael Hanlfb839b92015-09-19 21:32:34 +0200342 final Response.ResponseBuilder responseBuilder = Response
343 .status(HttpServletResponse.SC_BAD_REQUEST);
344 String redirectUri = e.getRedirectUri();
345
346 if (OAuthUtils.isEmpty(redirectUri))
347 throw new WebApplicationException(responseBuilder
348 .entity("OAuth callback url needs to be provided by client!!!\n")
349 .build());
350
351 final OAuthResponse response = OAuthASResponse
352 .errorResponse(HttpServletResponse.SC_BAD_REQUEST).error(e)
353 .location(redirectUri).buildQueryMessage();
354 final URI location = new URI(response.getLocationUri());
355 return responseBuilder.location(location).build();
356 }catch (OAuthSystemException | URISyntaxException | KustvaktException e) {
357 e.printStackTrace();
358 }
359 return Response.noContent().build();
360 }
361
362 @POST
363 @Path("revoke")
364 public Response revokeToken(@Context HttpServletRequest request,
365 @Context SecurityContext context,
366 @HeaderParam(ContainerRequest.USER_AGENT) String agent,
367 @HeaderParam(ContainerRequest.HOST) String host)
368 throws OAuthSystemException, URISyntaxException {
369 TokenContext ctx = (TokenContext) context.getUserPrincipal();
370 try {
371
372 if (!this.handler.revokeToken(ctx.getToken())) {
373 OAuthResponse res = OAuthASResponse
374 .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
375 .setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)
376 .setErrorDescription("Invalid access token!\n")
377 .buildJSONMessage();
378 return Response.status(res.getResponseStatus())
379 .entity(res.getBody()).build();
380 }
381
382 }catch (KustvaktException e) {
383 e.printStackTrace();
384 // fixme: do something
385 /**
386 * final Response.ResponseBuilder responseBuilder = Response
387 .status(HttpServletResponse.SC_FOUND);
388 String redirectUri = e.getRedirectUri();
389
390 final OAuthResponse response = OAuthASResponse
391 .errorResponse(HttpServletResponse.SC_FOUND).error(e)
392 .location(redirectUri).buildQueryMessage();
393 final URI location = new URI(response.getLocationUri());
394 return responseBuilder.location(location).build();
395 */
396 }
397
398 return Response.ok().build();
399 }
400
401 @POST
402 @Consumes("application/x-www-form-urlencoded")
403 @Produces("application/json")
404 @Path("token")
405 public Response requestToken(@Context HttpServletRequest request,
406 @HeaderParam(ContainerRequest.USER_AGENT) String agent,
407 @HeaderParam(ContainerRequest.HOST) String host,
408 MultivaluedMap form) throws OAuthSystemException {
409 boolean openid_valid = false;
410 User user = null;
411 OAuthTokenRequest oauthRequest;
412 OAuthASResponse.OAuthTokenResponseBuilder builder = OAuthASResponse
413 .tokenResponse(HttpServletResponse.SC_OK);
414
415 OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
416 ClientInfo info;
417 try {
418 oauthRequest = new OAuthTokenRequest(
419 new FormRequestWrapper(request, form));
420
421 if ((info = this.handler.getClient(oauthRequest.getClientId()))
422 == null) {
423 OAuthResponse res = OAuthASResponse
424 .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
425 .setError(OAuthError.TokenResponse.INVALID_CLIENT)
426 .setErrorDescription("Invalid client id!\n")
427 .buildJSONMessage();
428 return Response.status(res.getResponseStatus())
429 .entity(res.getBody()).build();
430 }else if (!info.getClient_secret()
431 .equals(oauthRequest.getClientSecret())) {
432 OAuthResponse res = OAuthASResponse
433 .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
434 .setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)
435 .setErrorDescription("Invalid client secret!\n")
436 .buildJSONMessage();
437 return Response.status(res.getResponseStatus())
438 .entity(res.getBody()).build();
439 }
440
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100441 Map<String, Object> attr = new HashMap<>();
Michael Hanlfb839b92015-09-19 21:32:34 +0200442 attr.put(Attributes.HOST, host);
443 attr.put(Attributes.USER_AGENT, agent);
444 attr.put(Attributes.SCOPES,
445 StringUtils.toString(oauthRequest.getScopes(), " "));
446
447 // support code (for external clients only) and password grant type
448 // password grant at this point is only allowed with trusted clients (korap frontend)
449 if (oauthRequest.getGrantType().equalsIgnoreCase(
450 GrantType.AUTHORIZATION_CODE.toString())) {
451 // validate auth code
452 AuthCodeInfo codeInfo;
453 try {
454 //can this be joined with the simple retrieval of access tokens?
455 // partially yes: auth code can be valid, even though no access token exists
456 // --> zero result set
457 codeInfo = this.handler
458 .getAuthorization(oauthRequest.getCode());
459 if (codeInfo == null) {
460 OAuthResponse res = OAuthASResponse.errorResponse(
461 HttpServletResponse.SC_UNAUTHORIZED).setError(
462 OAuthError.TokenResponse.INVALID_REQUEST)
463 .setErrorDescription(
464 "Invalid authorization code\n")
465 .buildJSONMessage();
466 return Response.status(res.getResponseStatus())
467 .entity(res.getBody()).build();
468 }else {
469 openid_valid = codeInfo.getScopes().contains("openid");
470 String accessToken = oauthIssuerImpl.accessToken();
Michael Hanl482f30d2015-09-25 12:39:46 +0200471 String refreshToken = oauthIssuerImpl.refreshToken();
Michael Hanlfb839b92015-09-19 21:32:34 +0200472 // auth code posesses the user reference. native apps access_tokens are directly associated with the user
473 this.handler
474 .addToken(oauthRequest.getCode(), accessToken,
Michael Hanl482f30d2015-09-25 12:39:46 +0200475 refreshToken, config.getTokenTTL());
Michael Hanlfb839b92015-09-19 21:32:34 +0200476
477 builder.setTokenType(TokenType.BEARER.toString());
478 builder.setExpiresIn(
479 String.valueOf(config.getLongTokenTTL()));
480 builder.setAccessToken(accessToken);
Michael Hanl482f30d2015-09-25 12:39:46 +0200481 builder.setRefreshToken(refreshToken);
Michael Hanlfb839b92015-09-19 21:32:34 +0200482 }
483 }catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200484 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200485 }
486 // todo: errors for invalid scopes or different scopes then during authorization request?
487 //todo ??
488 attr.put(Attributes.SCOPES, codeInfo.getScopes());
489
490 }else if (oauthRequest.getGrantType()
491 .equalsIgnoreCase(GrantType.PASSWORD.toString())) {
492 //fixme: via https; as basic auth header and only if client is native!
493 if (!info.isConfidential()) {
494 OAuthResponse res = OAuthASResponse
495 .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
496 .setError(
497 OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)
498 .setErrorDescription(
499 "Grant type not supported for client!\n")
500 .buildJSONMessage();
501 return Response.status(res.getResponseStatus())
502 .entity(res.getBody()).build();
503 }
504
505 openid_valid = true;
506 try {
507 user = controller
508 .authenticate(0, oauthRequest.getUsername(),
509 oauthRequest.getPassword(), attr);
510 }catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200511 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200512 }
513
514 try {
515 String accessToken = this.handler
516 .getToken(oauthRequest.getClientId(), user.getId());
517 if (accessToken == null) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200518 String refresh = oauthIssuerImpl.refreshToken();
Michael Hanlfb839b92015-09-19 21:32:34 +0200519 accessToken = oauthIssuerImpl.accessToken();
Michael Hanl482f30d2015-09-25 12:39:46 +0200520 this.handler
521 .addToken(accessToken, refresh, user.getId(),
522 oauthRequest.getClientId(), StringUtils
523 .toString(oauthRequest
524 .getScopes(), " "),
525 config.getLongTokenTTL());
526 builder.setRefreshToken(refresh);
Michael Hanlfb839b92015-09-19 21:32:34 +0200527 }
528 builder.setTokenType(TokenType.BEARER.toString());
529 builder.setExpiresIn(
530 String.valueOf(config.getLongTokenTTL()));
531 builder.setAccessToken(accessToken);
532
533 }catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200534 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200535 }
536 }
537
538 if (openid_valid && oauthRequest.getScopes()
539 .contains(Scopes.Scope.openid.toString())) {
540 try {
541 if (user == null)
542 user = controller
543 .authenticate(0, oauthRequest.getUsername(),
544 oauthRequest.getPassword(), attr);
Michael Hanlc2a9f622016-01-28 16:40:06 +0100545 Userdata data = controller
Michael Hanl5dd931a2016-01-29 16:40:38 +0100546 .getUserData(user, UserDetails.class);
Michael Hanlc2a9f622016-01-28 16:40:06 +0100547 user.addUserData(data);
548
Michael Hanlfb839b92015-09-19 21:32:34 +0200549 attr.put(Attributes.CLIENT_SECRET,
550 oauthRequest.getClientSecret());
551 TokenContext c = controller.createTokenContext(user, attr,
552 Attributes.OPENID_AUTHENTICATION);
553 builder.setParam(c.getTokenType(), c.getToken());
554 }catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200555 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200556 }
557 }
558
559 OAuthResponse r = builder.buildJSONMessage();
560 return Response.status(r.getResponseStatus()).entity(r.getBody())
561 .build();
562 }catch (OAuthProblemException ex) {
563 OAuthResponse r = OAuthResponse.errorResponse(401).error(ex)
564 .buildJSONMessage();
565 return Response.status(r.getResponseStatus()).entity(r.getBody())
566 .build();
567 }catch (OAuthSystemException e) {
568 e.printStackTrace();
569 // todo: throw error
570 }
571 return Response.noContent().build();
572 }
573
574}