Skip to content

Commit

Permalink
stream #22 - oauth (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
dtuchs authored Nov 20, 2024
1 parent 5e20bd7 commit 777a389
Show file tree
Hide file tree
Showing 13 changed files with 336 additions and 35 deletions.
1 change: 1 addition & 0 deletions niffler-e-2-e-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ dependencies {
// REST
testImplementation "com.squareup.retrofit2:retrofit:${retrofitVersion}"
testImplementation "com.squareup.retrofit2:converter-jackson:${retrofitVersion}"
testImplementation "com.squareup.retrofit2:converter-scalars:${retrofitVersion}"
testImplementation "com.squareup.okhttp3:logging-interceptor:${okhttp3Version}"
testImplementation "com.squareup.okhttp3:okhttp-urlconnection:${okhttp3Version}"
testImplementation "org.springframework.data:spring-data-commons:${springDataCommonsVersion}"
Expand Down
44 changes: 22 additions & 22 deletions niffler-e-2-e-tests/src/test/java/guru/qa/niffler/api/AuthApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,33 @@

public interface AuthApi {

@GET("oauth2/authorize")
Call<Void> authorize(
@Query("response_type") String responseType,
@Query("client_id") String clientId,
@Query("scope") String scope,
@Query(value = "redirect_uri", encoded = true) String redirectUri,
@Query("code_challenge") String codeChallenge,
@Query("code_challenge_method") String codeChallengeMethod);
@GET("register")
Call<Void> requestRegisterForm();

@POST("oauth2/token")
@FormUrlEncoded
Call<JsonNode> token(
@Field("client_id") String clientId,
@Field(value = "redirect_uri", encoded = true) String redirectUri,
@Field("grant_type") String grantType,
@Field("code") String code,
@Field("code_verifier") String codeChallenge);
@GET("oauth2/authorize")
Call<Void> authorize(@Query("response_type") String responseType,
@Query("client_id") String clientId,
@Query("scope") String scope,
@Query(value = "redirect_uri", encoded = true) String redirectUri,
@Query("code_challenge") String codeChallenge,
@Query("code_challenge_method") String codeChallengeMethod
);

@POST("login")
@FormUrlEncoded
Call<Void> login(
@Field("username") String username,
@Field("password") String password,
@Field("_csrf") String csrf);
Call<Void> login(@Field("username") String username,
@Field("password") String password,
@Field("_csrf") String csrf
);

@GET("register")
Call<Void> requestRegisterForm();
@POST("oauth2/token")
@FormUrlEncoded
Call<JsonNode> token(@Field("code") String code,
@Field(value = "redirect_uri", encoded = true) String redirectUri,
@Field("client_id") String clientId,
@Field("code_verifier") String codeVerifier,
@Field("grant_type") String grantType
);

@POST("register")
@FormUrlEncoded
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package guru.qa.niffler.api.core;

import guru.qa.niffler.jupiter.extension.ApiLoginExtension;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.Objects;

public class CodeInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
final Response response = chain.proceed(chain.request());
if (response.isRedirect()) {
String location = Objects.requireNonNull(
response.header("Location")
);
if (location.contains("code=")) {
ApiLoginExtension.setCode(
StringUtils.substringAfter(
location, "code="
)
);
}
}
return response;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public RestClient(String baseUrl, boolean followRedirect) {
this(baseUrl, followRedirect, JacksonConverterFactory.create(), HEADERS, new Interceptor[0]);
}

public RestClient(String baseUrl, boolean followRedirect, @Nullable Interceptor... interceptors) {
this(baseUrl, followRedirect, JacksonConverterFactory.create(), HEADERS, interceptors);
}

public RestClient(String baseUrl, HttpLoggingInterceptor.Level loggingLevel) {
this(baseUrl, false, JacksonConverterFactory.create(), loggingLevel, new Interceptor[0]);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package guru.qa.niffler.jupiter.annotation;

import guru.qa.niffler.jupiter.extension.ApiLoginExtension;
import guru.qa.niffler.jupiter.extension.UserExtension;
import org.junit.jupiter.api.extension.ExtendWith;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ExtendWith({
UserExtension.class,
ApiLoginExtension.class
})
public @interface ApiLogin {
String username() default "";
String password() default "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package guru.qa.niffler.jupiter.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Token {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package guru.qa.niffler.jupiter.extension;

import com.codeborne.selenide.Selenide;
import com.codeborne.selenide.WebDriverRunner;
import guru.qa.niffler.api.core.ThreadSafeCookieStore;
import guru.qa.niffler.config.Config;
import guru.qa.niffler.jupiter.annotation.ApiLogin;
import guru.qa.niffler.jupiter.annotation.Token;
import guru.qa.niffler.model.rest.TestData;
import guru.qa.niffler.model.rest.UserJson;
import guru.qa.niffler.page.MainPage;
import guru.qa.niffler.service.impl.AuthApiClient;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.platform.commons.support.AnnotationSupport;
import org.openqa.selenium.Cookie;


public class ApiLoginExtension implements BeforeEachCallback, ParameterResolver {

private static final Config CFG = Config.getInstance();
public static final ExtensionContext.Namespace NAMESPACE = ExtensionContext.Namespace.create(ApiLoginExtension.class);

private final AuthApiClient authApiClient = new AuthApiClient();
private final boolean setupBrowser;

private ApiLoginExtension(boolean setupBrowser) {
this.setupBrowser = setupBrowser;
}

public ApiLoginExtension() {
this.setupBrowser = true;
}

public static ApiLoginExtension restApiLoginExtension() {
return new ApiLoginExtension(false);
}

@Override
public void beforeEach(ExtensionContext context) throws Exception {
AnnotationSupport.findAnnotation(context.getRequiredTestMethod(), ApiLogin.class)
.ifPresent(apiLogin -> {

final UserJson userToLogin;
final UserJson userFromUserExtension = UserExtension.getUserJson();
if ("".equals(apiLogin.username()) || "".equals(apiLogin.password())) {
if (userFromUserExtension == null) {
throw new IllegalStateException("@User must be present in case that @ApiLogin is empty!");
}
userToLogin = userFromUserExtension;
} else {
UserJson fakeUser = new UserJson(
apiLogin.username(),
new TestData(
apiLogin.password()
)
);
if (userFromUserExtension != null) {
throw new IllegalStateException("@User must not be present in case that @ApiLogin contains username or password!");
}
UserExtension.setUser(fakeUser);
userToLogin = fakeUser;
}

final String token = authApiClient.login(
userToLogin.username(),
userToLogin.testData().password()
);
setToken(token);
if (setupBrowser) {
Selenide.open(CFG.frontUrl());
Selenide.localStorage().setItem("id_token", getToken());
WebDriverRunner.getWebDriver().manage().addCookie(
new Cookie(
"JSESSIONID",
ThreadSafeCookieStore.INSTANCE.cookieValue("JSESSIONID")
)
);
Selenide.open(MainPage.URL, MainPage.class).checkThatPageLoaded();
}
});
}

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return parameterContext.getParameter().getType().isAssignableFrom(String.class)
&& AnnotationSupport.isAnnotated(parameterContext.getParameter(), Token.class);
}

@Override
public String resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return getToken();
}

public static void setToken(String token) {
TestMethodContextExtension.context().getStore(NAMESPACE).put("token", token);
}

public static String getToken() {
return TestMethodContextExtension.context().getStore(NAMESPACE).get("token", String.class);
}

public static void setCode(String code) {
TestMethodContextExtension.context().getStore(NAMESPACE).put("code", code);
}

public static String getCode() {
return TestMethodContextExtension.context().getStore(NAMESPACE).get("code", String.class);
}

public static Cookie getJsessionIdCookie() {
return new Cookie(
"JSESSIONID",
ThreadSafeCookieStore.INSTANCE.cookieValue("JSESSIONID")
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ public void beforeEach(ExtensionContext context) throws Exception {
usersClient.addIncomeInvitation(testUser, userAnno.incomeInvitations());
usersClient.addOutcomeInvitation(testUser, userAnno.outcomeInvitations());
usersClient.addFriend(testUser, userAnno.friends());
context.getStore(NAMESPACE).put(
context.getUniqueId(),
testUser
);
setUser(testUser);
}
});
}
Expand All @@ -45,6 +42,19 @@ public boolean supportsParameter(ParameterContext parameterContext, ExtensionCon

@Override
public UserJson resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return extensionContext.getStore(NAMESPACE).get(extensionContext.getUniqueId(), UserJson.class);
return getUserJson();
}

public static void setUser(UserJson testUser) {
final ExtensionContext context = TestMethodContextExtension.context();
context.getStore(NAMESPACE).put(
context.getUniqueId(),
testUser
);
}

public static UserJson getUserJson() {
final ExtensionContext context = TestMethodContextExtension.context();
return context.getStore(NAMESPACE).get(context.getUniqueId(), UserJson.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package guru.qa.niffler.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import guru.qa.niffler.api.AuthApi;
import guru.qa.niffler.api.core.CodeInterceptor;
import guru.qa.niffler.api.core.RestClient;
import guru.qa.niffler.api.core.ThreadSafeCookieStore;
import guru.qa.niffler.config.Config;
import guru.qa.niffler.jupiter.extension.ApiLoginExtension;
import guru.qa.niffler.utils.OAuthUtils;
import lombok.SneakyThrows;
import retrofit2.Response;


public class AuthApiClient extends RestClient {

private static final Config CFG = Config.getInstance();
private final AuthApi authApi;

public AuthApiClient() {
super(CFG.authUrl(), true, new CodeInterceptor());
this.authApi = create(AuthApi.class);
}

@SneakyThrows
public String login(String username, String password) {
final String codeVerifier = OAuthUtils.generateCodeVerifier();
final String codeChallenge = OAuthUtils.generateCodeChallange(codeVerifier);
final String redirectUri = CFG.frontUrl() + "authorized";
final String clientId = "client";

authApi.authorize(
"code",
clientId,
"openid",
redirectUri,
codeChallenge,
"S256"
).execute();

authApi.login(
username,
password,
ThreadSafeCookieStore.INSTANCE.cookieValue("XSRF-TOKEN")
).execute();

Response<JsonNode> tokenResponse = authApi.token(
ApiLoginExtension.getCode(),
redirectUri,
clientId,
codeVerifier,
"authorization_code"
).execute();

return tokenResponse.body().get("id_token").asText();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package guru.qa.niffler.test.web;
package guru.qa.niffler.test.fake;

import guru.qa.niffler.jupiter.extension.UsersClientExtension;
import guru.qa.niffler.model.rest.CategoryJson;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package guru.qa.niffler.test.fake;

import guru.qa.niffler.config.Config;
import guru.qa.niffler.jupiter.annotation.ApiLogin;
import guru.qa.niffler.jupiter.annotation.Token;
import guru.qa.niffler.model.rest.UserJson;
import guru.qa.niffler.service.impl.AuthApiClient;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class OAuthTest {

private static final Config CFG = Config.getInstance();
private final AuthApiClient authApiClient = new AuthApiClient();

@Test
@ApiLogin(username = "duck", password = "12345")
void oauthTest(@Token String token, UserJson user) {
System.out.println(user);
Assertions.assertNotNull(token);
}
}
Loading

0 comments on commit 777a389

Please sign in to comment.