Implemented OAuth2 request token with resource owner password grant.

Change-Id: I516d5adf0091d711ff183470b3f0de8a6e502270
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 6e1904b..66d610f 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
@@ -53,7 +53,7 @@
             }
         }
     }
-    
+
     private ClientResponse testRegisterConfidentialClient ()
             throws KustvaktException {
 
@@ -121,6 +121,29 @@
         testDeregisterPublicClient(clientId);
     }
 
+    @Test
+    public void testRegisterNativeClient () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        OAuth2ClientJson json = new OAuth2ClientJson();
+        json.setName("NativeClient");
+        json.setType(OAuth2ClientType.PUBLIC);
+        json.setUrl("http://korap.ids-mannheim.de/native");
+        json.setRedirectURI("https://korap.ids-mannheim.de/native/redirect");
+
+        ClientResponse response = resource().path("oauth2").path("client")
+                .path("register")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(username,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
+                .entity(json).post(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        
+        //EM: need to check native
+    }
+
     private void testDeregisterPublicClient (String clientId)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
@@ -178,13 +201,13 @@
 
         String entity = response.getEntity(String.class);
         assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
-        
+
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(OAuthError.TokenResponse.INVALID_CLIENT,
                 node.at("/error").asText());
         assertEquals("Invalid client credentials.",
                 node.at("/error_description").asText());
-        
+
         checkWWWAuthenticateHeader(response);
     }
 }
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
index 46671ff..956e3ed 100644
--- 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
@@ -48,6 +48,109 @@
                 .entity(form).post(ClientResponse.class);
     }
 
+    private ClientResponse testRequestTokenPublicClient (
+            MultivaluedMap<String, String> form)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        return resource().path("oauth2").path("token")
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .entity(form).post(ClientResponse.class);
+    }
+
+    @Test
+    public void testRequestTokenPasswordGrantConfidential ()
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("grant_type", "password");
+
+        ClientResponse response = testRequestTokenConfidentialClient(form);
+        String entity = response.getEntity(String.class);
+
+        JsonNode node = JsonUtils.readTree(entity);
+        assertNotNull(node.at("/access_token").asText());
+        assertNotNull(node.at("/refresh_token").asText());
+        assertEquals(TokenType.BEARER.toString(),
+                node.at("/token_type").asText());
+        assertNotNull(node.at("/expires_in").asText());
+    }
+
+    @Test
+    public void testRequestTokenConfidentialMissingSecret ()
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("grant_type", "password");
+        form.add("client_id", "fCBbQkAyYzI4NzUxMg==");
+
+        ClientResponse response = testRequestTokenPublicClient(form);
+        assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+
+        String entity = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(OAuthError.TokenResponse.INVALID_CLIENT,
+                node.at("/error").asText());
+    }
+
+    @Test
+    public void testRequestTokenPasswordGrantPublic ()
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("grant_type", "password");
+        form.add("client_id", "iBr3LsTCxOj7D2o0A5m");
+
+        ClientResponse response = testRequestTokenPublicClient(form);
+        String entity = response.getEntity(String.class);
+
+        JsonNode node = JsonUtils.readTree(entity);
+        assertNotNull(node.at("/access_token").asText());
+        assertNotNull(node.at("/refresh_token").asText());
+        assertEquals(TokenType.BEARER.toString(),
+                node.at("/token_type").asText());
+        assertNotNull(node.at("/expires_in").asText());
+    }
+
+    @Test
+    public void testRequestTokenPasswordGrantMissingClientId ()
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("grant_type", "password");
+
+        ClientResponse response = testRequestTokenPublicClient(form);
+        String entity = response.getEntity(String.class);
+        assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(OAuthError.TokenResponse.INVALID_REQUEST,
+                node.at("/error").asText());
+        assertEquals("client_id is missing",
+                node.at("/error_description").asText());
+    }
+
+    @Test
+    public void testRequestTokenPasswordGrantNonNative ()
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("grant_type", "password");
+        form.add("client_id", "8bIDtZnH6NvRkW2Fq==");
+
+        ClientResponse response = testRequestTokenPublicClient(form);
+        String entity = response.getEntity(String.class);
+        assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT,
+                node.at("/error").asText());
+        assertEquals("Password grant is not allowed for third party clients",
+                node.at("/error_description").asText());
+    }
+
     @Test
     public void testRequestTokenClientCredentialsGrant ()
             throws UniformInterfaceException, ClientHandlerException,
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 5da4c40..17b2b51 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
@@ -654,7 +654,7 @@
         JsonNode node = JsonUtils.readTree(entity);
 
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
-        assertEquals(StatusCodes.MISSING_ARGUMENT,
+        assertEquals(StatusCodes.MISSING_PARAMETER,
                 node.at("/errors/0/0").asInt());
         assertEquals("groupId", node.at("/errors/0/1").asText());
         assertEquals("0", node.at("/errors/0/2").asText());
@@ -844,7 +844,7 @@
         String entity = response.getEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
 
-        assertEquals(StatusCodes.MISSING_ARGUMENT,
+        assertEquals(StatusCodes.MISSING_PARAMETER,
                 node.at("/errors/0/0").asInt());
         assertEquals("groupId", node.at("/errors/0/1").asText());
         assertEquals("0", node.at("/errors/0/2").asText());
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 ad2874a..42f36ae 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
@@ -689,7 +689,7 @@
         String entity = response.getEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
-        assertEquals(StatusCodes.MISSING_ARGUMENT,
+        assertEquals(StatusCodes.MISSING_PARAMETER,
                 node.at("/errors/0/0").asInt());
         assertEquals("vcId", node.at("/errors/0/1").asText());
     }
diff --git a/full/src/test/resources/kustvakt-test.conf b/full/src/test/resources/kustvakt-test.conf
index c99ee5e..b26384b 100644
--- a/full/src/test/resources/kustvakt-test.conf
+++ b/full/src/test/resources/kustvakt-test.conf
@@ -43,13 +43,20 @@
 
 ## options referring to the security module!
 
-## token expiration time in minutes!
+## OAuth 
+### (see de.ids_mannheim.korap.constant.AuthenticationMethod for possible 
+### oauth.password.authentication values)
+oauth.password.authentication = TEST
+oauth.native.client.host=korap.ids-mannheim.de
+
+# JWT
+security.jwt.issuer=korap.ids-mannheim.de
+
+## token expiration
 security.longTokenTTL = 1D
 security.tokenTTL = 9S
 security.shortTokenTTL = 5S
 
-security.jwt.issuer=korap.ids-mannheim.de
-
 ## specifies the user data field that is used to salt user passwords
 security.passcode.salt=salt