refactor tests, introduce TokenEndpointCall

This commit is contained in:
Dusan Jakub
2023-09-19 21:53:29 +02:00
parent e081da00da
commit 8ec906c0b8
8 changed files with 125 additions and 69 deletions

View File

@@ -123,7 +123,8 @@ public class OAuthResource {
return switch (params.getGrantType()) {
case "authorization_code" -> redeemAuthorizationCode(params);
case "urn:ietf:params:oauth:grant-type:device_code" -> redeemDeviceCode(params);
default -> throw new OAuthApiException(ErrorResponse.Error.invalid_request, "Unsupported grant type");
default ->
throw new OAuthApiException(ErrorResponse.Error.unsupported_grant_type, "Unsupported grant type");
};
}

View File

@@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
public record ErrorResponse(@JsonProperty("error") Error error,
@JsonProperty("error_description") String description) {
public enum Error {
invalid_request, unauthorized_client, unsupported_response_type,
invalid_request, unauthorized_client, unsupported_response_type, unsupported_grant_type,
access_denied, invalid_scope, server_error, temporarily_unavailable, authorization_pending
}
}

View File

@@ -1,5 +1,6 @@
package com.ysoft.geecon;
import com.ysoft.geecon.dto.AccessTokenResponse;
import com.ysoft.geecon.dto.OAuthClient;
import com.ysoft.geecon.dto.User;
import com.ysoft.geecon.error.ErrorResponse;
@@ -53,9 +54,9 @@ public class AuthCodeGrantTest {
assertThat(flow.getCode(), is(notNullValue()));
assertThat(flow.getAccessToken(), is(nullValue()));
flow.exchangeCode();
AccessTokenResponse accessTokenResponse = flow.exchangeCode().expectTokens();
assertThat(flow.getAccessToken(), is(notNullValue()));
assertThat(accessTokenResponse.accessToken(), is(notNullValue()));
}
@Test
@@ -94,9 +95,9 @@ public class AuthCodeGrantTest {
assertThat(flow.getCode(), is(notNullValue()));
assertThat(flow.getAccessToken(), is(nullValue()));
flow.exchangeCode();
AccessTokenResponse accessTokenResponse = flow.exchangeCode().expectTokens();
assertThat(flow.getAccessToken(), is(notNullValue()));
assertThat(accessTokenResponse.accessToken(), is(notNullValue()));
}
}

View File

@@ -1,5 +1,6 @@
package com.ysoft.geecon;
import com.ysoft.geecon.dto.AccessTokenResponse;
import com.ysoft.geecon.dto.DeviceResponse;
import com.ysoft.geecon.dto.OAuthClient;
import com.ysoft.geecon.dto.User;
@@ -21,6 +22,7 @@ import java.io.IOException;
import java.net.URI;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -57,7 +59,8 @@ public class DeviceAuthGrantTest {
ConsentScreen consentScreen = loginScreen.submitCorrect("bob", "password");
consentScreen.submit();
flow.exchangeDeviceCode();
AccessTokenResponse accessTokenResponse = flow.exchangeDeviceCode().expectTokens();
assertThat(accessTokenResponse.accessToken(), is(notNullValue()));
}
@Test
@@ -69,10 +72,10 @@ public class DeviceAuthGrantTest {
}
@Test
public void deviceAuthGrant_authorizationPending() throws IOException {
public void deviceAuthGrant_authorizationPending() {
DeviceAuthorizationGrantFlow flow = new DeviceAuthorizationGrantFlow(deviceUri, CLIENT);
flow.start();
ErrorResponse errorResponse = flow.exchangeDeviceCodeError();
ErrorResponse errorResponse = flow.exchangeDeviceCode().expectError(400);
assertThat(errorResponse.error(), is(ErrorResponse.Error.authorization_pending));
}
}

View File

@@ -0,0 +1,33 @@
package com.ysoft.geecon;
import com.ysoft.geecon.dto.OAuthClient;
import com.ysoft.geecon.error.ErrorResponse;
import com.ysoft.geecon.helpers.TokenEndpointCall;
import com.ysoft.geecon.repo.ClientsRepo;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
@QuarkusTest
public class TokenEndpointTest {
public static final OAuthClient CLIENT = new OAuthClient("deviceclient", "", null, null);
@Inject
ClientsRepo clientsRepo;
@BeforeEach
void beforeAll() {
clientsRepo.register(CLIENT);
}
@Test
public void invalidGrant() {
ErrorResponse errorResponse = new TokenEndpointCall(CLIENT).grantType("invalid").expectError(400);
assertThat(errorResponse.error(), is(ErrorResponse.Error.unsupported_grant_type));
assertThat(errorResponse.description(), is(notNullValue()));
}
}

View File

@@ -1,6 +1,5 @@
package com.ysoft.geecon.helpers;
import com.ysoft.geecon.dto.AccessTokenResponse;
import com.ysoft.geecon.dto.OAuthClient;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
@@ -16,8 +15,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import static io.restassured.RestAssured.given;
import static io.restassured.http.ContentType.JSON;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -104,31 +101,8 @@ public class AuthorizationCodeFlow {
return query;
}
public AccessTokenResponse exchangeCode() {
Map<String, String> tokenForm = new HashMap<>();
tokenForm.put("grant_type", "authorization_code");
tokenForm.put("client_id", client.clientId());
tokenForm.put("redirect_uri", client.redirectUri());
tokenForm.put("code", code);
if (codeVerifier != null) {
tokenForm.put("code_verifier", codeVerifier);
}
AccessTokenResponse accessTokenResponse = given()
.formParams(tokenForm)
.when()
.post("/auth/token")
.then()
.statusCode(200)
.contentType(JSON)
.body("token_type", is("Bearer"))
.body("expires_in", is(notNullValue()))
.body("access_token", is(notNullValue()))
.body("refresh_token", is(notNullValue()))
.extract().body().as(AccessTokenResponse.class);
accessToken = accessTokenResponse.accessToken();
idToken = accessTokenResponse.idToken();
return accessTokenResponse;
public TokenEndpointCall exchangeCode() {
return new TokenEndpointCall(client).authorizationCode(code, codeVerifier);
}
public String getState() {

View File

@@ -1,9 +1,7 @@
package com.ysoft.geecon.helpers;
import com.ysoft.geecon.dto.AccessTokenResponse;
import com.ysoft.geecon.dto.DeviceResponse;
import com.ysoft.geecon.dto.OAuthClient;
import com.ysoft.geecon.error.ErrorResponse;
import static io.restassured.RestAssured.given;
import static io.restassured.http.ContentType.JSON;
@@ -37,35 +35,7 @@ public class DeviceAuthorizationGrantFlow {
return deviceResponse;
}
public AccessTokenResponse exchangeDeviceCode() {
return given()
.formParam("grant_type", "urn:ietf:params:oauth:grant-type:device_code")
.formParam("client_id", client.clientId())
.formParam("device_code", deviceResponse.deviceCode())
.when()
.post("/auth/token")
.then()
.statusCode(200)
.contentType(JSON)
.body("token_type", is(notNullValue()))
.body("expires_in", is(notNullValue()))
.body("access_token", is(notNullValue()))
.body("refresh_token", is(notNullValue()))
.extract().as(AccessTokenResponse.class);
}
public ErrorResponse exchangeDeviceCodeError() {
return given()
.formParam("grant_type", "urn:ietf:params:oauth:grant-type:device_code")
.formParam("client_id", client.clientId())
.formParam("device_code", deviceResponse.deviceCode())
.when()
.post("/auth/token")
.then()
.statusCode(400)
.contentType(JSON)
.body("error", is(notNullValue()))
.body("error_description", is(notNullValue()))
.extract().as(ErrorResponse.class);
public TokenEndpointCall exchangeDeviceCode() {
return new TokenEndpointCall(client).deviceCode(deviceResponse.deviceCode());
}
}

View File

@@ -0,0 +1,74 @@
package com.ysoft.geecon.helpers;
import com.ysoft.geecon.dto.AccessTokenResponse;
import com.ysoft.geecon.dto.OAuthClient;
import com.ysoft.geecon.error.ErrorResponse;
import io.restassured.response.ValidatableResponse;
import java.util.HashMap;
import java.util.Map;
import static io.restassured.RestAssured.given;
import static io.restassured.http.ContentType.JSON;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
public class TokenEndpointCall {
private final OAuthClient client;
private final Map<String, String> tokenForm;
public TokenEndpointCall(OAuthClient client) {
this.client = client;
tokenForm = new HashMap<>();
tokenForm.put("client_id", client.clientId());
}
public TokenEndpointCall authorizationCode(String code, String codeVerifier) {
tokenForm.put("grant_type", "authorization_code");
tokenForm.put("redirect_uri", client.redirectUri());
tokenForm.put("code", code);
if (codeVerifier != null) {
tokenForm.put("code_verifier", codeVerifier);
}
return this;
}
public TokenEndpointCall deviceCode(String deviceCode) {
tokenForm.put("grant_type", "urn:ietf:params:oauth:grant-type:device_code");
tokenForm.put("device_code", deviceCode);
return this;
}
public TokenEndpointCall grantType(String grantType) {
tokenForm.put("grant_type", grantType);
return this;
}
public AccessTokenResponse expectTokens() {
return expect()
.statusCode(200)
.body("token_type", is("Bearer"))
.body("expires_in", is(notNullValue()))
.body("access_token", is(notNullValue()))
.body("refresh_token", is(notNullValue()))
.extract().body().as(AccessTokenResponse.class);
}
public ErrorResponse expectError(int status) {
return expect()
.statusCode(status)
.body("error", is(notNullValue()))
.body("error_description", is(notNullValue()))
.extract().body().as(ErrorResponse.class);
}
private ValidatableResponse expect() {
return given()
.formParams(tokenForm)
.when()
.post("/auth/token")
.then()
.contentType(JSON);
}
}