fix verification url generation, rewrite DAG test

This commit is contained in:
Dusan Jakub
2023-09-18 18:29:42 +02:00
parent f300fdb13f
commit fc039750b2
6 changed files with 128 additions and 59 deletions

View File

@@ -13,6 +13,7 @@ import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriBuilder;
import jakarta.ws.rs.core.UriInfo;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
@@ -46,6 +47,8 @@ public class OAuthResource {
UsersRepo usersRepo;
@Inject
SessionsRepo sessionsRepo;
@Inject
UriInfo uriInfo;
@GET
@Produces(MediaType.TEXT_HTML)
@@ -102,7 +105,9 @@ public class OAuthResource {
return new DeviceResponse(
sessionsRepo.generateAuthorizationCode(sessionId),
sessionsRepo.generateUserCode(sessionId),
"http://verificationuri/device-login",
uriInfo.getBaseUriBuilder()
.path(OAuthResource.class)
.path(OAuthResource.class, "enterDeviceCode").build(),
10,
180
);

View File

@@ -2,10 +2,12 @@ package com.ysoft.geecon.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.net.URI;
public record DeviceResponse(
@JsonProperty("device_code") String deviceCode,
@JsonProperty("user_code") String userCode,
@JsonProperty("verification_uri") String verificationUri,
@JsonProperty("verification_uri") URI verificationUri,
@JsonProperty("interval") long interval,
@JsonProperty("expires_in") long expiresIn
) {

View File

@@ -3,84 +3,67 @@ package com.ysoft.geecon;
import com.ysoft.geecon.dto.DeviceResponse;
import com.ysoft.geecon.dto.OAuthClient;
import com.ysoft.geecon.dto.User;
import com.ysoft.geecon.helpers.ConsentScreen;
import com.ysoft.geecon.helpers.DeviceAuthorizationGrantFlow;
import com.ysoft.geecon.helpers.DeviceCodeScreen;
import com.ysoft.geecon.helpers.LoginScreen;
import com.ysoft.geecon.repo.ClientsRepo;
import com.ysoft.geecon.repo.UsersRepo;
import io.quarkus.test.common.http.TestHTTPResource;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.jsoup.Jsoup;
import org.jsoup.HttpStatusException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static io.restassured.http.ContentType.JSON;
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;
@QuarkusTest
public class DeviceAuthGrantTest {
public static final OAuthClient CLIENT = new OAuthClient("deviceclient", "", null, null);
@Inject
ClientsRepo clientsRepo;
@Inject
UsersRepo usersRepo;
@TestHTTPResource("auth/device")
String deviceUri;
@TestHTTPResource("auth/device-login")
URI deviceLoginUri;
@BeforeEach
void beforeAll() {
clientsRepo.register(CLIENT);
usersRepo.register(new User("bob", "password"));
}
@Test
public void deviceAuthGrant_invalidCode() {
given().formParam("code", "somecode").
when().post("/auth/device-login").
then().statusCode(404);
public void deviceAuthGrant_invalidCode() throws IOException {
DeviceCodeScreen deviceCodeScreen = new DeviceCodeScreen(deviceLoginUri);
HttpStatusException exception = assertThrows(HttpStatusException.class, () -> deviceCodeScreen.enterCode("somecode"));
assertThat(exception.getStatusCode(), is(404));
}
@Test
public void deviceAuthGrant() {
clientsRepo.register(new OAuthClient("myclient", "", null, null));
usersRepo.register(new User("bob", "password"));
public void deviceAuthGrant() throws IOException {
DeviceAuthorizationGrantFlow flow = new DeviceAuthorizationGrantFlow(deviceUri, CLIENT);
DeviceResponse deviceResponse = flow.start();
DeviceResponse deviceResponse = given().
formParam("client_id", "myclient").
when().post("/auth/device")
.then()
.statusCode(200)
.contentType(JSON)
.body("device_code", is(notNullValue()))
.body("user_code", is(notNullValue()))
.body("verification_uri", is(notNullValue()))
.body("interval", is(notNullValue()))
.body("expires_in", is(notNullValue()))
.extract().body().as(DeviceResponse.class);
DeviceCodeScreen deviceCodeScreen = new DeviceCodeScreen(deviceResponse.verificationUri());
LoginScreen loginScreen = deviceCodeScreen.enterCode(deviceResponse.userCode());
String deviceLogin = given().formParam("code", deviceResponse.userCode()).
when().post("/auth/device-login").
then().statusCode(200)
.extract().body().asString();
ConsentScreen consentScreen = loginScreen.submitCorrect("bob", "password");
consentScreen.submit();
String sessionId = Jsoup.parse(deviceLogin).getElementsByAttributeValue("name", "sessionId").first().attr("value");
given().
formParam("sessionId", sessionId).
formParam("username", "bob").
formParam("password", "password").
when().
post("auth")
.then().statusCode(200);
given().
formParam("sessionId", sessionId).
when().
post("auth/consent")
.then().statusCode(200);
given().
formParam("grant_type", "urn:ietf:params:oauth:grant-type:device_code").
formParam("client_id", "myclient").
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()));
flow.exchangeDeviceCode();
}
}

View File

@@ -0,0 +1,57 @@
package com.ysoft.geecon.helpers;
import com.ysoft.geecon.dto.AccessTokenResponse;
import com.ysoft.geecon.dto.DeviceResponse;
import com.ysoft.geecon.dto.OAuthClient;
import java.io.IOException;
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 DeviceAuthorizationGrantFlow {
private final String deviceUrl;
private final OAuthClient client;
private DeviceResponse deviceResponse;
public DeviceAuthorizationGrantFlow(String deviceUrl, OAuthClient client) {
this.deviceUrl = deviceUrl;
this.client = client;
}
public DeviceResponse start() throws IOException {
deviceResponse = given().
formParam("client_id", client.clientId()).
when().post(deviceUrl)
.then()
.statusCode(200)
.contentType(JSON)
.body("device_code", is(notNullValue()))
.body("user_code", is(notNullValue()))
.body("verification_uri", is(notNullValue()))
.body("interval", is(notNullValue()))
.body("expires_in", is(notNullValue()))
.extract().body().as(DeviceResponse.class);
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);
}
}

View File

@@ -0,0 +1,23 @@
package com.ysoft.geecon.helpers;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.FormElement;
import java.io.IOException;
import java.net.URI;
public class DeviceCodeScreen {
private final FormElement form;
public DeviceCodeScreen(URI verificationUrl) throws IOException {
this.form = Jsoup.connect(verificationUrl.toString()).get().expectForm("form");
}
public LoginScreen enterCode(String code) throws IOException {
form.getElementsByAttributeValue("name", "code").val(code);
Document login = form.submit().post();
return new LoginScreen(login);
}
}

View File

@@ -11,7 +11,6 @@ public class LoginScreen {
public LoginScreen(Document doc) {
this.form = doc.expectForm("form");
;
}
public Document submit(String username, String password) throws IOException {