Skip to content
This repository has been archived by the owner on Nov 11, 2024. It is now read-only.

Added user service methods and tests #661

Merged
merged 3 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dependencies {
implementation project(':dev.galasa.framework')
implementation project(':dev.galasa.framework.api.beans')
implementation project(':dev.galasa.framework.api.common')
implementation project(':dev.galasa.framework.api.users')

implementation 'org.apache.commons:commons-lang3:3.14.0'
implementation 'dev.galasa:com.auth0.jwt:4.4.1'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@
import dev.galasa.framework.api.common.ServletError;
import dev.galasa.framework.spi.FrameworkException;
import dev.galasa.framework.spi.auth.IAuthStoreService;
import dev.galasa.framework.spi.auth.IFrontEndClient;
import dev.galasa.framework.spi.auth.IInternalAuthToken;
import dev.galasa.framework.spi.auth.IInternalUser;
import dev.galasa.framework.spi.auth.IUser;
import dev.galasa.framework.spi.utils.ITimeService;
import dev.galasa.framework.spi.utils.SystemTimeService;
import dev.galasa.framework.spi.auth.AuthStoreException;

public class AuthTokensRoute extends BaseRoute {
Expand All @@ -58,8 +62,14 @@ public class AuthTokensRoute extends BaseRoute {
// Regex to match /auth/tokens and /auth/tokens/ only
private static final String PATH_PATTERN = "\\/tokens\\/?";

private static final String REST_API_CLIENT = "rest-api";
private static final String WEB_UI_CLIENT = "web-ui";

private static final IBeanValidator<TokenPayload> validator = new TokenPayloadValidator();

private ITimeService timeService ;

// TODO: Need to be passed a framework so we can get a timer service from it.
eamansour marked this conversation as resolved.
Show resolved Hide resolved
public AuthTokensRoute(
ResponseBuilder responseBuilder,
IOidcProvider oidcProvider,
Expand All @@ -71,6 +81,8 @@ public AuthTokensRoute(
this.dexGrpcClient = dexGrpcClient;
this.authStoreService = authStoreService;
this.env = env;

this.timeService = new SystemTimeService();
}

/**
Expand Down Expand Up @@ -102,7 +114,7 @@ public HttpServletResponse handleGetRequest(String pathInfo, QueryParameters que

// Convert the token received from the auth store into the token bean that will
// be returned as JSON
List<AuthToken>tokensToReturn = convertAuthStoreTokenIntoTokenBeans(authTokensFromAuthStore);
List<AuthToken> tokensToReturn = convertAuthStoreTokenIntoTokenBeans(authTokensFromAuthStore);

return getResponseBuilder().buildResponse(request, response, "application/json",
getTokensAsJsonString(tokensToReturn), HttpServletResponse.SC_OK);
Expand Down Expand Up @@ -182,6 +194,9 @@ public HttpServletResponse handlePostRequest(String pathInfo, QueryParameters qu
addTokenToAuthStore(requestPayload.getClientId(), jwt, tokenDescription);
}

boolean isWebUiLogin = isLoggingIntoWebUI(requestPayload.getRefreshToken(), tokenDescription);
recordUserJustLoggedIn(isWebUiLogin , jwt , this.timeService, this.env);

} else {
logger.info("Unable to get new bearer and refresh tokens from issuer.");

Expand Down Expand Up @@ -295,6 +310,37 @@ private List<AuthToken> convertAuthStoreTokenIntoTokenBeans(List<IInternalAuthTo

}

// This method is protected so we can unit test it easily.
protected void recordUserJustLoggedIn(boolean isWebUI, String jwt, ITimeService timeService , Environment env)
throws InternalServletException, AuthStoreException {

JwtWrapper jwtWrapper = new JwtWrapper(jwt, env);
String loginId = jwtWrapper.getUsername();
IUser user;

String clientName = REST_API_CLIENT;
if (isWebUI) {
clientName = WEB_UI_CLIENT;
}

user = authStoreService.getUserByLoginId(loginId);

if (user == null) {
authStoreService.createUser(loginId, clientName);
} else {

IFrontEndClient client = user.getClient(clientName);
if (client == null) {
client = authStoreService.createClient(clientName);
user.addClient(client);
}

client.setLastLogin(timeService.now());

authStoreService.updateUser(user);
}
}

private void validateLoginId(String loginId, String servletPath) throws InternalServletException {

if (loginId == null || loginId.trim().length() == 0) {
Expand All @@ -303,4 +349,11 @@ private void validateLoginId(String loginId, String servletPath) throws Internal
}

}

private boolean isLoggingIntoWebUI(String refreshToken, String tokenDescription) {

return (refreshToken == null && tokenDescription == null);

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package dev.galasa.framework.api.authentication.routes;
package dev.galasa.framework.api.authentication.internal.routes;

import static org.assertj.core.api.Assertions.*;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package dev.galasa.framework.api.authentication.routes;
package dev.galasa.framework.api.authentication.internal.routes;

import static org.assertj.core.api.Assertions.*;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package dev.galasa.framework.api.authentication.routes;
package dev.galasa.framework.api.authentication.internal.routes;

import static org.assertj.core.api.Assertions.*;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package dev.galasa.framework.api.authentication.routes;
package dev.galasa.framework.api.authentication.internal.routes;

import static org.assertj.core.api.Assertions.*;

Expand All @@ -16,7 +16,6 @@

import org.junit.Test;

import dev.galasa.framework.api.authentication.internal.routes.AuthTokensDetailsRoute;
import dev.galasa.framework.api.authentication.mocks.MockAuthenticationServlet;
import dev.galasa.framework.api.authentication.mocks.MockDexGrpcClient;
import dev.galasa.framework.api.common.BaseServletTest;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package dev.galasa.framework.api.authentication.routes;
package dev.galasa.framework.api.authentication.internal.routes;

import static org.assertj.core.api.Assertions.*;

Expand All @@ -25,21 +25,20 @@
import com.google.gson.JsonObject;

import dev.galasa.framework.api.authentication.internal.DexGrpcClient;
import dev.galasa.framework.api.authentication.internal.routes.AuthTokensRoute;
import dev.galasa.framework.api.authentication.mocks.MockAuthenticationServlet;
import dev.galasa.framework.api.authentication.mocks.MockDexGrpcClient;
import dev.galasa.framework.api.authentication.mocks.MockOidcProvider;
import dev.galasa.framework.api.beans.AuthToken;
import dev.galasa.framework.api.beans.User;
import dev.galasa.framework.api.common.BaseServletTest;
import dev.galasa.framework.api.common.EnvironmentVariables;
import dev.galasa.framework.api.common.InternalUser;
import dev.galasa.framework.api.common.mocks.MockHttpServletRequest;
import dev.galasa.framework.api.common.mocks.MockHttpServletResponse;
import dev.galasa.framework.api.common.mocks.MockInternalAuthToken;
import dev.galasa.framework.api.common.mocks.MockTimeService;
import dev.galasa.framework.api.common.mocks.MockAuthStoreService;
import dev.galasa.framework.api.common.ResponseBuilder;
import dev.galasa.framework.api.common.mocks.*;
import dev.galasa.framework.spi.auth.IFrontEndClient;
import dev.galasa.framework.spi.auth.IInternalAuthToken;
import dev.galasa.framework.spi.auth.IInternalUser;
import dev.galasa.framework.spi.auth.IUser;
import dev.galasa.framework.spi.utils.GalasaGson;
import dev.galasa.framework.api.common.mocks.MockFramework;
import dev.galasa.framework.api.common.mocks.MockHttpResponse;
Expand Down Expand Up @@ -609,7 +608,7 @@ public void testAuthPostRequestWithInvalidRequestPayloadReturnsBadRequestError()
@Test
public void testAuthPostRequestWithValidRefreshTokenRequestPayloadReturnsJWT() throws Exception {
// Given...
String dummyJwt = "this-is-a-jwt";
String dummyJwt = DUMMY_JWT;
String dummyRefreshToken = "this-is-a-refresh-token";
String mockResponseJson = buildTokenResponse(dummyJwt, dummyRefreshToken);

Expand Down Expand Up @@ -723,7 +722,7 @@ public void testAuthPostRequestWithDescriptionInRequestReturnsJWTAndStoresTokenI
@Test
public void testAuthPostRequestWithValidAuthCodeRequestPayloadReturnsJWT() throws Exception {
// Given...
String dummyJwt = "this-is-a-jwt";
String dummyJwt = DUMMY_JWT;
String dummyRefreshToken = "this-is-a-refresh-token";
String mockResponseJson = buildTokenResponse(dummyJwt, dummyRefreshToken);

Expand Down Expand Up @@ -889,4 +888,176 @@ public void testAuthPostRequestWithTokenErrorResponseReturnsServerError() throws
checkErrorStructure(outStream.toString(), 5055, "GAL5055E", "Failed to get a JWT and a refresh token from the Galasa Dex server", "The Dex server did not respond with a JWT and refresh token");
}



@Test
public void testRecordingUserJustLoggedInEventWhenUserAlreadyExistButClientDoesNotShouldUpdateExistingUserRecordClientLoginTime() throws Exception {

// Given...
JsonObject dummyErrorJson = new JsonObject();
dummyErrorJson.addProperty("error", "oh no, something went wrong!");
aashir21 marked this conversation as resolved.
Show resolved Hide resolved
HttpResponse<String> mockResponse = new MockHttpResponse<String>(gson.toJson(dummyErrorJson));

ResponseBuilder responseBuilder = new ResponseBuilder();
MockOidcProvider mockOidcProvider = new MockOidcProvider(mockResponse);

String clientId = "myclient";
MockDexGrpcClient mockDexGrpcClient = new MockDexGrpcClient("http://issuer", clientId, "secret", "http://callback");


Instant now = Instant.MIN.plusMillis(3) ;
MockTimeService mockTimeService = new MockTimeService(now);

String userNumberInput = "567890";
String versionInput = "98767898yhj";

MockUser mockUser = new MockUser();
mockUser.loginId = "requestorId";
mockUser.userNumber = userNumberInput;
mockUser.version = versionInput;

MockAuthStoreService authStoreService = new MockAuthStoreService(mockTimeService);
authStoreService.addUser(mockUser);

MockEnvironment mockEnv = new MockEnvironment();
mockEnv.setenv(EnvironmentVariables.GALASA_USERNAME_CLAIMS,"sub");
String dummyJwt = DUMMY_JWT;

AuthTokensRoute route = new AuthTokensRoute(
responseBuilder,
mockOidcProvider,
mockDexGrpcClient,
authStoreService,
mockEnv);

boolean isWebUiJustLoggedIn = true ;

// When...
route.recordUserJustLoggedIn(isWebUiJustLoggedIn,dummyJwt, mockTimeService, mockEnv);

// Then...
IUser userGotBack = authStoreService.getUserByLoginId("requestorId");
assertThat(userGotBack).isNotNull();
assertThat(userGotBack.getUserNumber()).isEqualTo(userNumberInput);
assertThat(userGotBack.getVersion()).isEqualTo(versionInput);

IFrontEndClient clientGotBack = userGotBack.getClient("web-ui");
assertThat(clientGotBack.getLastLogin()).isEqualTo(now);
}


@Test
public void testRecordingUserJustLoggedInUsingTokenEventWhenUserAlreadyExistsAndClientDoesExistTooShouldUpdateExistingClientLoginTime() throws Exception {
testRecordingUserJustLoggedInUEventWhenUserAlreadyExistsAndClientDoesExistTooShouldUpdateExistingClientLoginTime(false, "rest-api");
}

@Test
public void testRecordingUserJustLoggedInUsingWebUiEventWhenUserAlreadyExistsAndClientDoesExistTooShouldUpdateExistingClientLoginTime() throws Exception {
testRecordingUserJustLoggedInUEventWhenUserAlreadyExistsAndClientDoesExistTooShouldUpdateExistingClientLoginTime(true, "web-ui");
}

public void testRecordingUserJustLoggedInUEventWhenUserAlreadyExistsAndClientDoesExistTooShouldUpdateExistingClientLoginTime(boolean isWebUiJustLoggedIn, String expectedClientName) throws Exception {

// Given...
JsonObject dummyErrorJson = new JsonObject();
dummyErrorJson.addProperty("error", "oh no, something went wrong!");
HttpResponse<String> mockResponse = new MockHttpResponse<String>(gson.toJson(dummyErrorJson));

ResponseBuilder responseBuilder = new ResponseBuilder();
MockOidcProvider mockOidcProvider = new MockOidcProvider(mockResponse);

String clientId = "myclient";
MockDexGrpcClient mockDexGrpcClient = new MockDexGrpcClient("http://issuer", clientId, "secret", "http://callback");


Instant now = Instant.MIN.plusMillis(3) ;
MockTimeService mockTimeService = new MockTimeService(now);

String userNumberInput = "567890";
String versionInput = "98767898yhj";

MockUser mockUser = new MockUser();
mockUser.loginId = "requestorId";
mockUser.userNumber = userNumberInput;
mockUser.version = versionInput;

MockAuthStoreService authStoreService = new MockAuthStoreService(mockTimeService);
authStoreService.addUser(mockUser);

MockFrontEndClient existingClient = new MockFrontEndClient(expectedClientName);
existingClient.lastLoginTime = Instant.MIN; // This client has an old time on it to start with. We expect this to be updated to now.
mockUser.addClient(existingClient);

MockEnvironment mockEnv = new MockEnvironment();
mockEnv.setenv(EnvironmentVariables.GALASA_USERNAME_CLAIMS,"sub");
String dummyJwt = DUMMY_JWT;

AuthTokensRoute route = new AuthTokensRoute(
responseBuilder,
mockOidcProvider,
mockDexGrpcClient,
authStoreService,
mockEnv);

// When...
route.recordUserJustLoggedIn(isWebUiJustLoggedIn,dummyJwt, mockTimeService, mockEnv);

// Then...
IUser userGotBack = authStoreService.getUserByLoginId("requestorId");
assertThat(userGotBack).isNotNull();
assertThat(userGotBack.getUserNumber()).isEqualTo(userNumberInput);
assertThat(userGotBack.getVersion()).isEqualTo(versionInput);

IFrontEndClient clientGotBack = userGotBack.getClient(expectedClientName);
assertThat(clientGotBack.getLastLogin()).isEqualTo(now);
}


@Test
public void testRecordingUserJustLoggedInEventWhenUserDoesntExistCreatesNewUserRecord() throws Exception {

// Given...

JsonObject dummyErrorJson = new JsonObject();
dummyErrorJson.addProperty("error", "oh no, something went wrong!");
HttpResponse<String> mockResponse = new MockHttpResponse<String>(gson.toJson(dummyErrorJson));

ResponseBuilder responseBuilder = new ResponseBuilder();
MockOidcProvider mockOidcProvider = new MockOidcProvider(mockResponse);

String clientId = "myclient";
MockDexGrpcClient mockDexGrpcClient = new MockDexGrpcClient("http://issuer", clientId, "secret", "http://callback");


Instant now = Instant.MIN.plusMillis(3) ;
MockTimeService mockTimeService = new MockTimeService(now);


MockAuthStoreService authStoreService = new MockAuthStoreService(mockTimeService);

MockEnvironment mockEnv = new MockEnvironment();
mockEnv.setenv(EnvironmentVariables.GALASA_USERNAME_CLAIMS,"sub");
String dummyJwt = DUMMY_JWT;

AuthTokensRoute route = new AuthTokensRoute(
responseBuilder,
mockOidcProvider,
mockDexGrpcClient,
authStoreService,
mockEnv);

boolean isWebUiJustLoggedIn = true ;

// When...
route.recordUserJustLoggedIn(isWebUiJustLoggedIn,dummyJwt, mockTimeService, mockEnv);

// Then...
IUser userGotBack = authStoreService.getUserByLoginId("requestorId");
assertThat(userGotBack).isNotNull();
assertThat(userGotBack.getUserNumber()).isEqualTo(MockAuthStoreService.DEFAULT_USER_NUMBER);
assertThat(userGotBack.getVersion()).isEqualTo(MockAuthStoreService.DEFAULT_USER_VERSION_NUMBER);

IFrontEndClient clientGotBack = userGotBack.getClient("web-ui");
assertThat(clientGotBack.getLastLogin()).isEqualTo(now);
}
}
Loading