Updated OAuth2 client authentication.
Change-Id: Ic13a38afd2d405fa2b450d80c4737261a4ab1edc
diff --git a/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java b/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
index c7a17b3..1e04eb0 100644
--- a/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
+++ b/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
@@ -23,6 +23,7 @@
public static final int INVALID_ARGUMENT = 107;
public static final int NOT_SUPPORTED = 108;
public static final int NOT_ALLOWED = 109;
+ public static final int HTTPS_REQUIRED = 110;
/**
* 300 status codes for query language and serialization
@@ -115,10 +116,12 @@
* 1800 Oauth2
*/
- public static final int CLIENT_REGISTRATION_FAILED = 1800;
- public static final int CLIENT_DEREGISTRATION_FAILED = 1801;
- public static final int CLIENT_AUTHENTICATION_FAILED = 1802;
- public static final int CLIENT_NOT_FOUND = 1803;
+ public static final int OAUTH2_SYSTEM_ERROR = 1800;
+
+ public static final int CLIENT_REGISTRATION_FAILED = 1801;
+ public static final int CLIENT_DEREGISTRATION_FAILED = 1802;
+ public static final int CLIENT_AUTHENTICATION_FAILED = 1803;
+ public static final int CLIENT_NOT_FOUND = 1804;
public static final int UNSUPPORTED_GRANT_TYPE = 1810;
diff --git a/full/src/main/java/de/ids_mannheim/korap/constant/OAuth2ClientType.java b/full/src/main/java/de/ids_mannheim/korap/constant/OAuth2ClientType.java
index 1584aef..3c4c57d 100644
--- a/full/src/main/java/de/ids_mannheim/korap/constant/OAuth2ClientType.java
+++ b/full/src/main/java/de/ids_mannheim/korap/constant/OAuth2ClientType.java
@@ -14,6 +14,7 @@
// credentials (e.g., clients executing on the device used by the
// resource owner, such as an installed native application or a web
// browser-based application), and incapable of secure client
-// authentication via any other means.
+// authentication via any other means. Mobile and Javascript apps
+// are considered public clients.
PUBLIC;
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/OAuth2ClientDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/OAuth2ClientDao.java
index 5ab083f..8d678b8 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dao/OAuth2ClientDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/OAuth2ClientDao.java
@@ -49,8 +49,10 @@
}
public OAuth2Client retrieveClientById (String clientId)
- throws KustvaktException {
+ throws Exception {
+ ParameterChecker.checkStringValue(clientId, "client_id");
+
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<OAuth2Client> query =
builder.createQuery(OAuth2Client.class);
@@ -60,13 +62,13 @@
query.where(builder.equal(root.get(OAuth2Client_.id), clientId));
Query q = entityManager.createQuery(query);
- try {
+// try {
return (OAuth2Client) q.getSingleResult();
- }
- catch (NoResultException e) {
- throw new KustvaktException(StatusCodes.CLIENT_NOT_FOUND,
- "Client with id " + clientId + "is not found");
- }
+// }
+// catch (NoResultException e) {
+// throw new KustvaktException(StatusCodes.CLIENT_NOT_FOUND,
+// "Client with id " + clientId + " is not found");
+// }
}
public void deregisterClient (OAuth2Client client) {
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/OAuth2ClientService.java b/full/src/main/java/de/ids_mannheim/korap/service/OAuth2ClientService.java
index fe27503..3a44e51 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/OAuth2ClientService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/OAuth2ClientService.java
@@ -2,8 +2,11 @@
import java.sql.SQLException;
+import javax.servlet.http.HttpServletResponse;
+
import org.apache.commons.validator.routines.UrlValidator;
-import org.apache.oltu.oauth2.common.message.types.GrantType;
+import org.apache.oltu.oauth2.common.error.OAuthError;
+import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -19,9 +22,23 @@
import de.ids_mannheim.korap.exceptions.KustvaktException;
import de.ids_mannheim.korap.exceptions.StatusCodes;
import de.ids_mannheim.korap.interfaces.EncryptionIface;
-import de.ids_mannheim.korap.security.context.TokenContext;
import de.ids_mannheim.korap.web.input.OAuth2ClientJson;
+/** According to RFC 6749, an authorization server MUST:
+ * <ul>
+ * <li>
+ * require client authentication for confidential clients or for any
+ * client that was issued client credentials (or with other authentication
+ * requirements),
+ * </li>
+ *
+ * <li>authenticate the client if client authentication is included
+ * </li>
+ * </ul>
+ *
+ * @author margaretha
+ *
+ */
@Service
public class OAuth2ClientService {
@@ -32,6 +49,8 @@
@Autowired
private UrlValidator urlValidator;
@Autowired
+ private UrlValidator httpsValidator;
+ @Autowired
private EncryptionIface encryption;
@Autowired
private HttpAuthorizationHandler authorizationHandler;
@@ -45,9 +64,10 @@
throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
clientJson.getUrl() + " is invalid.", clientJson.getUrl());
}
- if (!urlValidator.isValid(clientJson.getRedirectURI())) {
- throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
- clientJson.getRedirectURI() + " is invalid.",
+ if (!httpsValidator.isValid(clientJson.getRedirectURI())) {
+ throw new KustvaktException(StatusCodes.HTTPS_REQUIRED,
+ clientJson.getRedirectURI()
+ + " is invalid. RedirectURI requires https.",
clientJson.getRedirectURI());
}
@@ -95,9 +115,9 @@
public void deregisterPublicClient (String clientId, String username)
- throws KustvaktException {
+ throws KustvaktException, OAuthProblemException {
- OAuth2Client client = clientDao.retrieveClientById(clientId);
+ OAuth2Client client = retrieveClientById(clientId);
if (adminDao.isAdmin(username)) {
clientDao.deregisterClient(client);
}
@@ -119,47 +139,34 @@
public void deregisterConfidentialClient (String authorization,
- String clientId) throws KustvaktException {
- OAuth2Client client = authenticateClient(authorization, null, clientId);
+ String clientId) throws KustvaktException, OAuthProblemException {
+ OAuth2Client client =
+ authenticateClientByBasicAuthorization(authorization, clientId);
clientDao.deregisterClient(client);
}
- public TokenContext requestAccessTokenByClientCredentials (
- String authorization, String grantType) throws KustvaktException {
-
- return null;
+ public OAuth2Client authenticateClientById (String clientId)
+ throws OAuthProblemException {
+ if (clientId == null || clientId.isEmpty()) {
+ throw OAuthProblemException
+ .error(OAuthError.TokenResponse.INVALID_REQUEST)
+ .description("client_id is missing.")
+ .responseStatus(HttpServletResponse.SC_BAD_REQUEST);
+ }
+ else {
+ return retrieveClientById(clientId);
+ }
}
- /** According to RFC 6749, an authorization server MUST:
- * <ul>
- * <li>
- * require client authentication for confidential clients or for any
- * client that was issued client credentials (or with other authentication
- * requirements),
- * </li>
- *
- * <li>authenticate the client if client authentication is included
- * </li>
- * </ul>
- *
- * @param authorization
- * @param grantType
- * @param clientId
- * @return
- * @throws KustvaktException
- */
- public OAuth2Client authenticateClient (String authorization,
- GrantType grantType, String clientId) throws KustvaktException {
-
- OAuth2Client client = clientDao.retrieveClientById(clientId);
+ public OAuth2Client authenticateClientByBasicAuthorization (
+ String authorization, String clientId)
+ throws KustvaktException, OAuthProblemException {
if (authorization == null || authorization.isEmpty()) {
- if (client.getType().equals(OAuth2ClientType.CONFIDENTIAL)
- || grantType.equals(GrantType.CLIENT_CREDENTIALS)) {
- throw new KustvaktException(StatusCodes.AUTHENTICATION_FAILED,
- "Authorization header is not found.");
- }
- // OAuth2 does not require client authentication
+ throw OAuthProblemException
+ .error(OAuthError.TokenResponse.INVALID_REQUEST)
+ .description("Authorization header is not found.")
+ .responseStatus(HttpServletResponse.SC_BAD_REQUEST);
}
else {
AuthorizationData authData = authorizationHandler
@@ -167,24 +174,64 @@
if (authData.getAuthenticationScheme()
.equals(AuthenticationScheme.BASIC)) {
authorizationHandler.parseBasicToken(authData);
- if (!client.getId().equals(clientId)
- || !encryption.checkHash(authData.getPassword(),
- client.getSecret(),
- config.getPasscodeSaltField())) {
- throw new KustvaktException(
- StatusCodes.AUTHENTICATION_FAILED,
- "Client credentials are incorrect.");
- }
+ return verifyClientCredentials(clientId, authData);
}
else {
- throw new KustvaktException(
- StatusCodes.UNSUPPORTED_AUTHENTICATION_SCHEME,
- authData.getAuthenticationScheme().displayName()
- + "is unsupported for client authentication.",
- authData.getAuthenticationScheme().displayName());
+ throw OAuthProblemException
+ .error(OAuthError.TokenResponse.INVALID_CLIENT)
+ .description(
+ "Client authentication with "
+ + authData.getAuthenticationScheme()
+ .displayName()
+ + "is not supported")
+ .responseStatus(HttpServletResponse.SC_BAD_REQUEST);
}
}
- return client;
}
+ private OAuth2Client verifyClientCredentials (String clientId,
+ AuthorizationData authData) throws OAuthProblemException {
+
+ try {
+ OAuth2Client client = retrieveClientById(authData.getUsername());
+ // EM: not sure if this is necessary
+ if (clientId != null && !clientId.isEmpty()) {
+ if (!client.getId().equals(clientId)) {
+ throw new KustvaktException(
+ StatusCodes.CLIENT_AUTHENTICATION_FAILED);
+ }
+ }
+ if (!encryption.checkHash(authData.getPassword(),
+ client.getSecret(), config.getPasscodeSaltField())) {
+ throw new KustvaktException(
+ StatusCodes.CLIENT_AUTHENTICATION_FAILED);
+ }
+ return client;
+ }
+ catch (Exception e) {
+ throw OAuthProblemException
+ .error(OAuthError.TokenResponse.INVALID_CLIENT)
+ .description("Invalid client credentials.")
+ .responseStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+ }
+
+ public OAuth2Client retrieveClientById (String clientId)
+ throws OAuthProblemException {
+ try {
+ return clientDao.retrieveClientById(clientId);
+ }
+ catch (KustvaktException e) {
+ throw OAuthProblemException
+ .error(OAuthError.TokenResponse.INVALID_REQUEST)
+ .description(e.getMessage() + "is missing.")
+ .responseStatus(HttpServletResponse.SC_BAD_REQUEST);
+ }
+ catch (Exception e) {
+ throw OAuthProblemException
+ .error(OAuthError.TokenResponse.INVALID_CLIENT)
+ .description("Invalid client credentials.")
+ .responseStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+ }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/OAuth2Service.java b/full/src/main/java/de/ids_mannheim/korap/service/OAuth2Service.java
index aee6b13..b69b248 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/OAuth2Service.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/OAuth2Service.java
@@ -1,57 +1,194 @@
package de.ids_mannheim.korap.service;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.oltu.oauth2.as.issuer.MD5Generator;
+import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
+import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
+import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
+import org.apache.oltu.oauth2.as.response.OAuthASResponse;
+import org.apache.oltu.oauth2.common.error.OAuthError;
+import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
+import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
+import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.oltu.oauth2.common.message.types.GrantType;
+import org.apache.oltu.oauth2.common.message.types.TokenType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import de.ids_mannheim.korap.config.FullConfiguration;
+import de.ids_mannheim.korap.constant.OAuth2ClientType;
import de.ids_mannheim.korap.entity.OAuth2Client;
import de.ids_mannheim.korap.exceptions.KustvaktException;
-import de.ids_mannheim.korap.exceptions.StatusCodes;
@Service
public class OAuth2Service {
@Autowired
private OAuth2ClientService clientService;
+ @Autowired
+ private FullConfiguration config;
+
/**
* RFC 6749:
*
* If the client type is confidential or the client was issued client
* credentials, the client MUST authenticate with the authorization server.
+ * @param request
*
* @param authorization
* @param grantType
* @param scope
* @param password
* @param username
- * @param client_id
+ * @param clientId required for authorization_code grant, otherwise optional
* @param redirectURI
* @param authorizationCode
+ * @return
* @throws KustvaktException
+ * @throws OAuthProblemException
+ * @throws OAuthSystemException
*/
- public void requestAccessToken (String authorization, GrantType grantType,
- String authorizationCode, String redirectURI, String client_id,
- String username, String password, String scope)
- throws KustvaktException {
-
- OAuth2Client client = clientService.authenticateClient(authorization,
- grantType, client_id);
+ public OAuthResponse requestAccessToken (HttpServletRequest request,
+ String authorization, GrantType grantType, String authorizationCode,
+ String redirectURI, String clientId, String username,
+ String password, String scope)
+ throws KustvaktException, OAuthProblemException {
if (grantType.equals(GrantType.AUTHORIZATION_CODE)) {
-
+ return requestAccessTokenWithAuthorizationCode(authorization,
+ authorizationCode, redirectURI, clientId);
}
else if (grantType.equals(GrantType.PASSWORD)) {
-
+ return requestAccessTokenWithPassword(authorization, username,
+ password, scope);
}
else if (grantType.equals(GrantType.CLIENT_CREDENTIALS)) {
-
+ return requestAccessTokenWithClientCredentials(authorization,
+ scope);
}
else {
- throw new KustvaktException(StatusCodes.UNSUPPORTED_GRANT_TYPE,
- "Grant type " + grantType.name() + " is unsupported.",
- grantType.name());
+ throw OAuthProblemException
+ .error(OAuthError.TokenResponse.UNSUPPORTED_GRANT_TYPE)
+ .description(grantType.name() + "is not supported.")
+ .responseStatus(HttpServletResponse.SC_BAD_REQUEST);
+
}
}
+
+ /** Confidential clients must authenticate
+ *
+ * @param authorization
+ * @param authorizationCode
+ * @param redirectURI
+ * @param clientId required if there is no authorization header
+ * @return
+ * @throws OAuthSystemException
+ * @throws KustvaktException
+ * @throws OAuthProblemException
+ */
+ private OAuthResponse requestAccessTokenWithAuthorizationCode (
+ String authorization, String authorizationCode, String redirectURI,
+ String clientId) throws KustvaktException, OAuthProblemException {
+ OAuth2Client client;
+ if (authorization == null || authorization.isEmpty()) {
+ client = clientService.authenticateClientById(clientId);
+ if (client.getType().equals(OAuth2ClientType.CONFIDENTIAL)) {
+ throw OAuthProblemException
+ .error(OAuthError.TokenResponse.INVALID_CLIENT)
+ .description("Client authentication using "
+ + "authorization header is required.")
+ .responseStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+ }
+ else {
+ client = clientService.authenticateClientByBasicAuthorization(
+ authorization, clientId);
+ }
+
+ // TODO
+ return null;
+ }
+
+ /** Confidential clients must authenticate
+ *
+ * @param authorization
+ * @param username
+ * @param password
+ * @param scope
+ * @return
+ */
+ private OAuthResponse requestAccessTokenWithPassword (String authorization,
+ String username, String password, String scope) {
+
+
+
+ return null;
+ }
+
+ /** Clients must authenticate
+ *
+ * @param authorization
+ * @param scope
+ * @return
+ * @throws OAuthProblemException
+ * @throws KustvaktException
+ */
+ private OAuthResponse requestAccessTokenWithClientCredentials (
+ String authorization, String scope)
+ throws OAuthProblemException, KustvaktException {
+
+ if (authorization == null || authorization.isEmpty()) {
+ throw OAuthProblemException
+ .error(OAuthError.TokenResponse.INVALID_CLIENT)
+ .description("Client authentication using "
+ + "authorization header is required.")
+ .responseStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+ else {
+ OAuth2Client client =
+ clientService.authenticateClientByBasicAuthorization(
+ authorization, null);
+ //TODO
+ }
+ return null;
+ }
+
+
+ /**
+ * @param request
+ * @return
+ * @throws OAuthSystemException
+ *
+ */
+ private OAuthResponse createsAccessTokenResponse (
+ HttpServletRequest request) throws OAuthSystemException {
+ OAuthTokenRequest oauthRequest = null;
+ OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
+ OAuthResponse r = null;
+ try {
+ oauthRequest = new OAuthTokenRequest(request);
+ String authorizationCode = oauthRequest.getCode();
+
+ String accessToken = oauthIssuerImpl.accessToken();
+ String refreshToken = oauthIssuerImpl.refreshToken();
+
+ r = OAuthASResponse.tokenResponse(HttpServletResponse.SC_OK)
+ .setAccessToken(accessToken)
+ .setTokenType(TokenType.BEARER.name())
+ .setExpiresIn(String.valueOf(config.getLongTokenTTL()))
+ .setRefreshToken(refreshToken).buildJSONMessage();
+ // scope
+
+ }
+ catch (OAuthProblemException e) {
+ r = OAuthResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
+ .error(e).buildJSONMessage();
+ }
+
+ return r;
+ }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/FullResponseHandler.java b/full/src/main/java/de/ids_mannheim/korap/web/FullResponseHandler.java
index c2d8df5..d836ead 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/FullResponseHandler.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/FullResponseHandler.java
@@ -3,6 +3,9 @@
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
+import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
+import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
+import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.springframework.beans.factory.annotation.Autowired;
import de.ids_mannheim.korap.authentication.http.HttpUnauthorizedHandler;
@@ -45,6 +48,24 @@
return new WebApplicationException(r);
}
+ public WebApplicationException throwit (OAuthProblemException e) {
+ OAuthResponse or = null;
+ try {
+ or = OAuthResponse.errorResponse(e.getResponseStatus()).error(e)
+ .buildJSONMessage();
+ }
+ catch (OAuthSystemException e1) {
+ // return throwit(new KustvaktException(
+ // StatusCodes.OAUTH2_SYSTEM_ERROR, e1.getMessage(), e1));
+ return throwit(StatusCodes.OAUTH2_SYSTEM_ERROR, e1.getMessage());
+ }
+
+ Response r = Response.status(or.getResponseStatus())
+ .entity(or.getBody()).build();
+ return new WebApplicationException(r);
+
+ }
+
// public WebApplicationException throwAuthenticationException (
// String message) {
// String notification =
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
index 7cd3311..08d2a0a 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
@@ -1,5 +1,6 @@
package de.ids_mannheim.korap.web.controller;
+import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.HeaderParam;
@@ -9,8 +10,13 @@
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.SecurityContext;
+import org.apache.http.HttpHeaders;
+import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
+import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
+import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@@ -28,11 +34,28 @@
@Autowired
private OAuth2Service oauth2Service;
+ /** Grants a client an access token, namely a string used in authenticated
+ * requests representing user authorization for the client to access user
+ * resources.
+ *
+ * EM: should we allow client_secret in the request body?
+ *
+ * @param securityContext
+ * @param authorization
+ * @param grantType
+ * @param authorizationCode
+ * @param redirectURI
+ * @param client_id a client id required for authorization_code grant, otherwise optional
+ * @param username
+ * @param password
+ * @param scope
+ * @return
+ */
@POST
@Path("token")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
- public Response requestAccessToken (
+ public Response requestAccessToken (@Context HttpServletRequest request,
@Context SecurityContext securityContext,
@HeaderParam("Authorization") String authorization,
// required for all grants
@@ -41,21 +64,29 @@
@FormParam("code") String authorizationCode,
@FormParam("redirect_uri") String redirectURI,
@FormParam("client_id") String client_id,
- // required for Resource Owner Password Credentials Grant
+ // required for Resource Owner Password Grant
@FormParam("username") String username,
@FormParam("password") String password,
// optional for Resource Owner Password and Client Credentials Grants
@FormParam("scope") String scope) {
try {
- oauth2Service.requestAccessToken(authorization, grantType,
- authorizationCode, redirectURI, client_id, username,
- password, scope);
+ OAuthResponse oauth2Response = oauth2Service.requestAccessToken(request,
+ authorization, grantType, authorizationCode, redirectURI,
+ client_id, username, password, scope);
- return Response.ok().build();
+ ResponseBuilder builder =
+ Response.status(oauth2Response.getResponseStatus());
+ builder.entity(oauth2Response.getBody());
+ builder.header(HttpHeaders.CACHE_CONTROL, "no-store");
+ builder.header(HttpHeaders.PRAGMA, "no-store");
+ return builder.build();
}
catch (KustvaktException e) {
throw responseHandler.throwit(e);
}
+ catch (OAuthProblemException e) {
+ throw responseHandler.throwit(e);
+ }
}
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
index eba6325..d7eb33e 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
@@ -12,6 +12,7 @@
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
+import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@@ -27,6 +28,12 @@
import de.ids_mannheim.korap.web.input.OAuth2ClientJson;
+/** Defines controllers for OAuth2 clients, namely applications attempting
+ * to access users' resources.
+ *
+ * @author margaretha
+ *
+ */
@Controller
@Path("/oauth2/client")
public class OAuthClientController {
@@ -38,7 +45,10 @@
/** Registers a client application. Before starting an OAuth process,
* client applications have to be registered first. Only registered
- * users are allowed to register client applications.
+ * users are allowed to register client applications. After registration,
+ * the client will receive a client_id and a client_secret, if the client
+ * is confidential (capable of storing the client_secret), that are needed
+ * in the authorization process.
*
* From RFC 6749:
* The authorization server SHOULD document the size of any identifier
@@ -46,7 +56,9 @@
*
* @param context
* @param clientJson a JSON object describing the client
- * @return client id and secret if the client type is confidential
+ * @return client_id and client_secret if the client type is confidential
+ *
+ * @see OAuth2ClientJson
*/
@POST
@Path("register")
@@ -92,6 +104,9 @@
catch (KustvaktException e) {
throw responseHandler.throwit(e);
}
+ catch (OAuthProblemException e) {
+ throw responseHandler.throwit(e);
+ }
}
@@ -109,5 +124,8 @@
catch (KustvaktException e) {
throw responseHandler.throwit(e);
}
+ catch (OAuthProblemException e) {
+ throw responseHandler.throwit(e);
+ }
}
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/input/OAuth2ClientJson.java b/full/src/main/java/de/ids_mannheim/korap/web/input/OAuth2ClientJson.java
index 80c5fba..2dc4034 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/input/OAuth2ClientJson.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/input/OAuth2ClientJson.java
@@ -4,13 +4,20 @@
import lombok.Getter;
import lombok.Setter;
+/** Defines required attributes to register an OAuth2 client.
+ *
+ * @author margaretha
+ *
+ */
@Setter
@Getter
public class OAuth2ClientJson {
-
+
// all required for registration
private String name;
private OAuth2ClientType type;
private String url;
+ // redirect URI determines where the OAuth 2.0 service will return the user to
+ // after they have authorized a client. It must be https.
private String redirectURI;
}
diff --git a/full/src/main/resources/default-config.xml b/full/src/main/resources/default-config.xml
index 76917d4..95b2365 100644
--- a/full/src/main/resources/default-config.xml
+++ b/full/src/main/resources/default-config.xml
@@ -169,7 +169,9 @@
<bean id="urlValidator" class="org.apache.commons.validator.routines.UrlValidator">
<constructor-arg value="http,https" />
</bean>
-
+ <bean id="httpsValidator" class="org.apache.commons.validator.routines.UrlValidator">
+ <constructor-arg value="https"/>
+ </bean>
<bean id="kustvakt_rewrite" class="de.ids_mannheim.korap.rewrite.FullRewriteHandler">
<constructor-arg ref="kustvakt_config" />
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
index e54f354..6251147 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
@@ -7,6 +7,7 @@
import javax.ws.rs.core.MultivaluedMap;
import org.apache.http.entity.ContentType;
+import org.apache.oltu.oauth2.common.error.OAuthError;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -27,6 +28,10 @@
import de.ids_mannheim.korap.utils.JsonUtils;
import de.ids_mannheim.korap.web.input.OAuth2ClientJson;
+/**
+ * @author margaretha
+ *
+ */
public class OAuth2ClientControllerTest extends SpringJerseyTest {
@Autowired
@@ -40,7 +45,7 @@
json.setName("OAuth2ClientTest");
json.setType(OAuth2ClientType.CONFIDENTIAL);
json.setUrl("http://example.client.com");
- json.setRedirectURI("http://example.client.com/redirect");
+ json.setRedirectURI("https://example.client.com/redirect");
return resource().path("oauth2").path("client").path("register")
.header(Attributes.AUTHORIZATION,
@@ -79,7 +84,7 @@
json.setName("OAuth2PublicClient");
json.setType(OAuth2ClientType.PUBLIC);
json.setUrl("http://public.client.com");
- json.setRedirectURI("http://public.client.com/redirect");
+ json.setRedirectURI("https://public.client.com/redirect");
ClientResponse response = resource().path("oauth2").path("client")
.path("register")
@@ -155,13 +160,14 @@
ContentType.APPLICATION_FORM_URLENCODED)
.entity(form).delete(ClientResponse.class);
- assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
-
String entity = response.getEntity(String.class);
+// System.out.println(entity);
+ assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+
JsonNode node = JsonUtils.readTree(entity);
- assertEquals(StatusCodes.AUTHENTICATION_FAILED,
- node.at("/errors/0/0").asInt());
- assertEquals("Client credentials are incorrect.",
- node.at("/errors/0/1").asText());
+ assertEquals(OAuthError.TokenResponse.INVALID_CLIENT,
+ node.at("/error").asText());
+ assertEquals("Invalid client credentials.",
+ node.at("/error_description").asText());
}
}
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
new file mode 100644
index 0000000..f52c883
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
@@ -0,0 +1,53 @@
+package de.ids_mannheim.korap.web.controller;
+
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.apache.http.entity.ContentType;
+import org.apache.oltu.oauth2.common.message.types.GrantType;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.google.common.net.HttpHeaders;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.core.util.MultivaluedMapImpl;
+
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.SpringJerseyTest;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+
+/**
+ * @author margaretha
+ *
+ */
+public class OAuth2ControllerTest extends SpringJerseyTest {
+
+ @Autowired
+ private HttpAuthorizationHandler handler;
+ private String username = "OAuth2ControllerTest";
+
+ @Test
+ public void testRequestTokenUnsupportedGrant ()
+ throws UniformInterfaceException, ClientHandlerException,
+ KustvaktException {
+
+ MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+// form.add("grant_type", "blahblah");
+ form.add("grant_type", GrantType.REFRESH_TOKEN.name());
+
+ ClientResponse response = resource().path("oauth2").path("token")
+ .header(Attributes.AUTHORIZATION,
+ handler.createBasicAuthorizationHeaderValue(username,
+ "pass"))
+ .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+ .header(HttpHeaders.CONTENT_TYPE,
+ ContentType.APPLICATION_FORM_URLENCODED)
+ .entity(form).post(ClientResponse.class);
+
+ System.out.println(response.getStatus());
+ System.out.println(response.getEntity(String.class));
+ }
+
+}
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java
index 0653e69..af9f4ff 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java
@@ -26,6 +26,10 @@
import de.ids_mannheim.korap.utils.JsonUtils;
import de.ids_mannheim.korap.web.input.UserGroupJson;
+/**
+ * @author margaretha
+ *
+ */
public class UserGroupControllerAdminTest extends SpringJerseyTest {
@Autowired
private HttpAuthorizationHandler handler;
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
index 373d194..5da4c40 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
@@ -26,6 +26,10 @@
import de.ids_mannheim.korap.utils.JsonUtils;
import de.ids_mannheim.korap.web.input.UserGroupJson;
+/**
+ * @author margaretha
+ *
+ */
public class UserGroupControllerTest extends SpringJerseyTest {
@Autowired
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java
index 2b98f0d..28ed689 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java
@@ -24,6 +24,10 @@
import de.ids_mannheim.korap.exceptions.KustvaktException;
import de.ids_mannheim.korap.utils.JsonUtils;
+/**
+ * @author margaretha
+ *
+ */
public class VirtualCorpusControllerAdminTest extends SpringJerseyTest {
@Autowired
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
index b01a164..71b1162 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
@@ -37,6 +37,10 @@
import de.ids_mannheim.korap.service.VirtualCorpusServiceTest;
import de.ids_mannheim.korap.utils.JsonUtils;
+/**
+ * @author margaretha
+ *
+ */
public class VirtualCorpusControllerTest extends SpringJerseyTest {
@Autowired
diff --git a/full/src/test/resources/test-config.xml b/full/src/test/resources/test-config.xml
index 562c3bb..1757481 100644
--- a/full/src/test/resources/test-config.xml
+++ b/full/src/test/resources/test-config.xml
@@ -169,6 +169,9 @@
<bean id="urlValidator" class="org.apache.commons.validator.routines.UrlValidator">
<constructor-arg value="http,https"/>
</bean>
+ <bean id="httpsValidator" class="org.apache.commons.validator.routines.UrlValidator">
+ <constructor-arg value="https"/>
+ </bean>
<bean id="kustvakt_rewrite" class="de.ids_mannheim.korap.rewrite.FullRewriteHandler">
<constructor-arg ref="kustvakt_config" />