From 3e4250acb7a65688cca2041b688f185876601942 Mon Sep 17 00:00:00 2001 From: open-schnick Date: Tue, 3 Nov 2020 12:52:49 +0100 Subject: [PATCH 1/8] Added Steps, and fixed feature --- .run/Run Cucumber Tests.run.xml | 16 ++++++ .../rest/cucumber/CommonCucumberSteps.java | 20 +++---- .../rest/cucumber/UserAuthorizationSteps.java | 56 +++++++++++++++++-- src/test/resources/UserAuthorization.feature | 15 +++-- 4 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 .run/Run Cucumber Tests.run.xml diff --git a/.run/Run Cucumber Tests.run.xml b/.run/Run Cucumber Tests.run.xml new file mode 100644 index 00000000..c7ced12e --- /dev/null +++ b/.run/Run Cucumber Tests.run.xml @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java b/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java index 68b3d581..4e6f1c7e 100644 --- a/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java +++ b/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java @@ -6,8 +6,10 @@ import de.filefighter.rest.RestApplicationIntegrationTest; import de.filefighter.rest.domain.filesystem.data.persistance.FileSystemEntity; import de.filefighter.rest.domain.filesystem.data.persistance.FileSystemRepository; +import de.filefighter.rest.domain.token.business.AccessTokenBusinessService; import de.filefighter.rest.domain.token.data.persistance.AccessTokenEntity; import de.filefighter.rest.domain.token.data.persistance.AccessTokenRepository; +import de.filefighter.rest.domain.user.business.UserBusinessService; import de.filefighter.rest.domain.user.data.persistance.UserEntity; import de.filefighter.rest.domain.user.data.persistance.UserRepository; import io.cucumber.java.en.And; @@ -46,21 +48,19 @@ public void databaseIsEmpty() { fileSystemRepository.deleteAll(); } - @And("user {long} exists") - public void userExists(long userId) { - userRepository.save(UserEntity - .builder() + @Given("accessToken with value {string} exists for user {long}") + public void accessTokenWithValueExistsForUser(String tokenValue, long userId) { + accessTokenRepository.save(AccessTokenEntity.builder() .userId(userId) - .build()); + .value(tokenValue) + .validUntil(Instant.now().getEpochSecond()+ ACCESS_TOKEN_DURATION_IN_SECONDS).build()); } - @And("user {long} has access token {string}") - public void userHasAccessToken(long userId, String accessTokenValue) { - accessTokenRepository.save(AccessTokenEntity + @And("user {long} exists") + public void userExists(long userId) { + userRepository.save(UserEntity .builder() .userId(userId) - .value(accessTokenValue) - .validUntil(Instant.now().getEpochSecond() + ACCESS_TOKEN_DURATION_IN_SECONDS) .build()); } diff --git a/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java b/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java index fce5a740..d2cfcf71 100644 --- a/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java +++ b/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java @@ -1,27 +1,73 @@ package de.filefighter.rest.cucumber; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import de.filefighter.rest.RestApplicationIntegrationTest; import io.cucumber.java.en.And; -import io.cucumber.java.en.Then; import io.cucumber.java.en.When; +import org.bson.internal.Base64; +import org.springframework.http.HttpMethod; -import static org.junit.jupiter.api.Assertions.assertTrue; +import java.time.Instant; +import java.util.HashMap; +import java.util.UUID; + +import static com.mongodb.internal.connection.tlschannel.util.Util.assertTrue; +import static de.filefighter.rest.configuration.RestConfiguration.*; +import static org.junit.jupiter.api.Assertions.assertEquals; public class UserAuthorizationSteps extends RestApplicationIntegrationTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + @When("user requests login with username {string} and password {string}") public void userRequestsLoginWithUsernameAndPassword(String username, String password) { + String authString = AUTHORIZATION_BASIC_PREFIX + username + ":" + password; + String base64encoded = Base64.encode(authString.getBytes()); + + HashMap authHeader = new HashMap<>(); + authHeader.put("Authorization", base64encoded); + + String url = BASE_API_URI + USER_BASE_URI + "login"; + + executeRestApiCall(HttpMethod.GET, url, authHeader); } @When("user requests accessToken with refreshToken {string} and userId {long}") public void userRequestsAccessTokenWithRefreshTokenAndUserId(String refreshTokenValue, long userId) { - } + String authHeaderString = AUTHORIZATION_BEARER_PREFIX + refreshTokenValue; + String url = BASE_API_URI + USER_BASE_URI + userId + "/login"; + + HashMap authHeader = new HashMap<>(); + authHeader.put("Authorization", authHeaderString); - @And("response contains valid accessToken") - public void responseContainsValidAccessToken() { + executeRestApiCall(HttpMethod.GET, url, authHeader); } @When("user requests userInfo with accessToken {string} and userId {long}") public void userRequestsUserInfoWithAccessTokenAndUserId(String accessTokenValue, long userId) { + String authHeaderString = AUTHORIZATION_BEARER_PREFIX + accessTokenValue; + String url = BASE_API_URI + USER_BASE_URI + userId + "/info"; + + HashMap authHeader = new HashMap<>(); + authHeader.put("Authorization", authHeaderString); + + executeRestApiCall(HttpMethod.GET, url, authHeader); + } + + @And("response contains valid accessToken for user {long}") + public void responseContainsValidAccessTokenForUser(long userId) throws JsonProcessingException { + JsonNode rootNode = objectMapper.readTree(latestResponse.getBody()); + String tokenValue = rootNode.get("token").asText(); + long actualUserId = rootNode.get("userId").asLong(); + long validUntil = rootNode.get("validUntil").asLong(); + + int expectedTokenLength = UUID.randomUUID().toString().length(); + long expectedValidUntil = Instant.now().getEpochSecond(); + + assertEquals(expectedTokenLength, tokenValue.length()); + assertTrue(validUntil > expectedValidUntil); + assertEquals(userId, actualUserId); } } diff --git a/src/test/resources/UserAuthorization.feature b/src/test/resources/UserAuthorization.feature index 7bc88839..d055fc97 100644 --- a/src/test/resources/UserAuthorization.feature +++ b/src/test/resources/UserAuthorization.feature @@ -19,20 +19,27 @@ # And response contains key "status" and value "denied" # And response status code is 401 # -#Scenario: Successful retrieval of accessToken with refreshToken. +#Scenario: Successful creation of new accessToken with refreshToken. # When user requests accessToken with refreshToken "token" and userId 1234 # Then response contains key "userId" and value "1234" -# And response contains valid accessToken +# And response contains valid accessToken for user 1234 # And response status code is 200 # -#Scenario: Failed retrieval of accessToken with wrong refreshToken. +#Scenario: Successful request of existing accessToken with refreshToken. +# Given accessToken with value "token" exists for user 1234 +# When user requests accessToken with refreshToken "token" and userId 1234 +# Then response contains key "userId" and value "1234" +# And response contains valid accessToken for user 1234 +# And response status code is 200 +# +# Scenario: Failed retrieval of accessToken with wrong refreshToken. # When user requests accessToken with refreshToken "not_the_token" and userId 1234 # Then response contains key "message" and value "User not authenticated." # And response contains key "status" and value "denied" # And response status code is 401 # #Scenario: Successful UserInfo request with valid accessToken. -# Given user 1234 has access token "accessToken" +# Given accessToken with value "accessToken" exists for user 1234 # When user requests userInfo with accessToken "accessToken" and userId 1234 # Then response contains the user with id 1234 # And response status code is 200 From 3af3e2f632963ba31039da37b07f8d5efe54ee95 Mon Sep 17 00:00:00 2001 From: open-schnick Date: Tue, 3 Nov 2020 14:02:37 +0100 Subject: [PATCH 2/8] Added default users for dev, fixed wrong param names "roles" --- .../rest/configuration/PrepareDataBase.java | 32 +++++++++++++++++-- .../domain/user/business/UserDtoService.java | 20 ++++++++++++ .../user/data/persistance/UserEntity.java | 3 +- .../domain/user/role/GroupRepository.java | 2 +- .../rest/domain/user/role/Groups.java | 8 ++--- 5 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 src/main/java/de/filefighter/rest/domain/user/business/UserDtoService.java diff --git a/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java b/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java index f68d0571..3fb81280 100644 --- a/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java +++ b/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java @@ -32,7 +32,7 @@ CommandLineRunner cleanDataBase(UserRepository userRepository, FileSystemReposit @Bean @Profile("prod") - CommandLineRunner initUserDataBase(UserRepository repository) { + CommandLineRunner initUserDataBaseProd(UserRepository repository) { //Note: when the admin user changes his/her password, a new refreshToken will be created. return args -> { @@ -42,9 +42,35 @@ CommandLineRunner initUserDataBase(UserRepository repository) { .username("admin") .password("admin") .refreshToken("refreshToken1234") - .roleIds(new long[]{0, 1}) + .groupIds(new long[]{0, 1}) .build())); - LOG.info("Loading Users" + (repository.findAll().size() == 1 ? " was successful." : " failed.")); + LOG.info("Inserting Users" + (repository.findAll().size() == 1 ? " was successful." : " failed.")); + }; + } + + @Bean + @Profile("dev") + CommandLineRunner initUserDataBaseDev(UserRepository repository) { + + return args -> { + LOG.info("Preloading default users: " + + repository.save(UserEntity + .builder() + .userId(0L) + .username("user") + .password("1234") + .refreshToken("refreshToken1234") + .groupIds(new long[]{0}) + .build()) + + repository.save(UserEntity + .builder() + .userId(0L) + .username("user1") + .password("12345") + .refreshToken("refreshToken1234") + .groupIds(new long[]{0}) + .build())); + LOG.info("Inserting Users" + (repository.findAll().size() == 2 ? " was successful." : " failed.")); }; } } \ No newline at end of file diff --git a/src/main/java/de/filefighter/rest/domain/user/business/UserDtoService.java b/src/main/java/de/filefighter/rest/domain/user/business/UserDtoService.java new file mode 100644 index 00000000..2448706d --- /dev/null +++ b/src/main/java/de/filefighter/rest/domain/user/business/UserDtoService.java @@ -0,0 +1,20 @@ +package de.filefighter.rest.domain.user.business; + +import de.filefighter.rest.domain.common.DtoServiceInterface; +import de.filefighter.rest.domain.user.data.dto.User; +import de.filefighter.rest.domain.user.data.persistance.UserEntity; +import org.springframework.stereotype.Service; + +@Service +public class UserDtoService implements DtoServiceInterface { + + @Override + public User createDto(UserEntity entity) { + return null; + } + + @Override + public UserEntity createEntity(User dto) { + return null; + } +} diff --git a/src/main/java/de/filefighter/rest/domain/user/data/persistance/UserEntity.java b/src/main/java/de/filefighter/rest/domain/user/data/persistance/UserEntity.java index db28695c..a4604180 100644 --- a/src/main/java/de/filefighter/rest/domain/user/data/persistance/UserEntity.java +++ b/src/main/java/de/filefighter/rest/domain/user/data/persistance/UserEntity.java @@ -16,7 +16,6 @@ public class UserEntity { private String username; private String password; private String refreshToken; //TODO: add valid_until for refreshToken - private long[] roleIds; - + private long[] groupIds; } diff --git a/src/main/java/de/filefighter/rest/domain/user/role/GroupRepository.java b/src/main/java/de/filefighter/rest/domain/user/role/GroupRepository.java index 61d87549..484ed9ec 100644 --- a/src/main/java/de/filefighter/rest/domain/user/role/GroupRepository.java +++ b/src/main/java/de/filefighter/rest/domain/user/role/GroupRepository.java @@ -9,7 +9,7 @@ public class GroupRepository { //TODO: test this. public Groups getRoleById(long id) { for (Groups role : roles) { - if (role.getRoleId() == id) { + if (role.getGroupId() == id) { return role; } } diff --git a/src/main/java/de/filefighter/rest/domain/user/role/Groups.java b/src/main/java/de/filefighter/rest/domain/user/role/Groups.java index a6ca7f18..616a6dff 100644 --- a/src/main/java/de/filefighter/rest/domain/user/role/Groups.java +++ b/src/main/java/de/filefighter/rest/domain/user/role/Groups.java @@ -5,16 +5,16 @@ public enum Groups { FAMILY(0, "Family"), ADMIN(1, "Admin"); - private final long roleId; + private final long groupId; private final String displayName; Groups(long roleId, String displayName) { - this.roleId = roleId; + this.groupId = roleId; this.displayName = displayName; } - public long getRoleId() { - return roleId; + public long getGroupId() { + return groupId; } public String getDisplayName() { From 1d0b1a772e970ed71cb20982c7e920a2ede22280 Mon Sep 17 00:00:00 2001 From: open-schnick Date: Tue, 3 Nov 2020 18:50:10 +0100 Subject: [PATCH 3/8] FF-89 implemented logic, fixed cucumber tests. --- .../rest/configuration/PrepareDataBase.java | 63 ++++++++++- .../domain/common/DtoServiceInterface.java | 6 ++ .../filefighter/rest/domain/common/Utils.java | 8 ++ .../business/AccessTokenBusinessService.java | 72 +++++++++++++ .../token/business/AccessTokenDtoService.java | 37 +++++++ .../data/persistance/AccessTokenEntity.java | 2 +- .../persistance/AccessTokenRepository.java | 2 +- ...se.java => AccessTokenNotFoundAdvise.java} | 8 +- .../AccessTokenNotFoundException.java | 8 ++ .../exceptions/TokenNotFoundException.java | 8 -- .../user/business/UserBusinessService.java | 95 +++++++++++++++- .../domain/user/business/UserDtoService.java | 26 ++++- .../rest/domain/user/data/dto/User.java | 4 +- .../user/data/persistance/UserRepository.java | 5 +- .../exceptions/UserAlreadyExistsAdvise.java | 2 +- .../UserNotAuthenticatedAdvise.java | 2 +- .../UserNotAuthenticatedException.java | 6 +- .../user/exceptions/UserNotFoundAdvice.java | 2 +- .../exceptions/UserNotFoundException.java | 4 + .../domain/user/rest/UserRestController.java | 2 +- .../domain/user/rest/UserRestService.java | 27 ++++- .../domain/user/role/GroupRepository.java | 6 +- .../rest/cucumber/CommonCucumberSteps.java | 2 +- .../rest/cucumber/SystemHealthSteps.java | 9 ++ .../rest/cucumber/UserAuthorizationSteps.java | 21 +++- .../rest/SystemHealthRestIntegrationTest.java | 2 +- .../AccessTokenBusinessServiceUnitTest.java | 25 +++++ .../AccessTokenDtoServiceUnitTest.java | 21 ++++ .../business/UserBusinessServiceUnitTest.java | 25 ++++- .../user/business/UserDtoServiceUnitTest.java | 21 ++++ .../user/rest/UserRestControllerUnitTest.java | 10 +- .../user/rest/UserRestServiceUnitTest.java | 37 +++++++ src/test/resources/SystemHealth.feature | 1 + src/test/resources/UserAuthorization.feature | 101 +++++++++--------- 34 files changed, 566 insertions(+), 104 deletions(-) create mode 100644 src/main/java/de/filefighter/rest/domain/common/DtoServiceInterface.java create mode 100644 src/main/java/de/filefighter/rest/domain/common/Utils.java create mode 100644 src/main/java/de/filefighter/rest/domain/token/business/AccessTokenDtoService.java rename src/main/java/de/filefighter/rest/domain/token/exceptions/{TokenNotFoundAdvise.java => AccessTokenNotFoundAdvise.java} (75%) create mode 100644 src/main/java/de/filefighter/rest/domain/token/exceptions/AccessTokenNotFoundException.java delete mode 100644 src/main/java/de/filefighter/rest/domain/token/exceptions/TokenNotFoundException.java create mode 100644 src/test/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessServiceUnitTest.java create mode 100644 src/test/java/de/filefighter/rest/domain/token/business/AccessTokenDtoServiceUnitTest.java create mode 100644 src/test/java/de/filefighter/rest/domain/user/business/UserDtoServiceUnitTest.java create mode 100644 src/test/java/de/filefighter/rest/domain/user/rest/UserRestServiceUnitTest.java diff --git a/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java b/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java index 3fb81280..01b93576 100644 --- a/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java +++ b/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java @@ -1,21 +1,52 @@ package de.filefighter.rest.configuration; import de.filefighter.rest.domain.filesystem.data.persistance.FileSystemRepository; +import de.filefighter.rest.domain.token.business.AccessTokenBusinessService; +import de.filefighter.rest.domain.token.data.persistance.AccessTokenEntity; import de.filefighter.rest.domain.token.data.persistance.AccessTokenRepository; import de.filefighter.rest.domain.user.data.persistance.UserEntity; import de.filefighter.rest.domain.user.data.persistance.UserRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; +import java.time.Instant; + @Configuration public class PrepareDataBase { + @Value("${server.port}") + int serverPort; + private static final Logger LOG = LoggerFactory.getLogger(PrepareDataBase.class); + @Bean + CommandLineRunner veryImportantFileFighterStartScript() { + return args -> { + System.out.println(); + System.out.println("-------------------------------< REST API >-------------------------------"); + System.out.println(); + System.out.println(" _____ _ _ _____ _ _ _ "); + System.out.println(" | ___| (_) | | ___ | ___| (_) __ _ | |__ | |_ ___ _ __ "); + System.out.println(" | |_ | | | | / _ \\ | |_ | | / _ | | '_ \\ | __| / _ \\ | '__|"); + System.out.println(" | _| | | | | | __/ | _| | | | (_| | | | | | | |_ | __/ | | "); + System.out.println(" |_| |_| |_| \\___| |_| |_| \\__, | |_| |_| \\__| \\___| |_| "); + System.out.println(" |___/ "); + System.out.println(" Version 0.2 Last updated at 03.11.20 "); + System.out.println(" Developed by Gimleux, Valentin, Open-Schnick. "); + System.out.println(" Development Blog: https://filefighter.github.io "); + System.out.println(" The code can be found at: https://www.github.com/filefighter "); + System.out.println(" Running on http://localhost:"+serverPort); + System.out.println(); + System.out.println("-------------------------------< REST API >-------------------------------"); + System.out.println(); + }; + } + @Bean CommandLineRunner cleanDataBase(UserRepository userRepository, FileSystemRepository fileSystemRepository, AccessTokenRepository accessTokenRepository) { @@ -56,21 +87,43 @@ CommandLineRunner initUserDataBaseDev(UserRepository repository) { LOG.info("Preloading default users: " + repository.save(UserEntity .builder() - .userId(0L) + .userId(0) .username("user") .password("1234") - .refreshToken("refreshToken1234") + .refreshToken("rft1234") .groupIds(new long[]{0}) .build()) + repository.save(UserEntity .builder() - .userId(0L) + .userId(1) .username("user1") .password("12345") - .refreshToken("refreshToken1234") - .groupIds(new long[]{0}) + .refreshToken("rft") + .groupIds(new long[]{-1}) .build())); LOG.info("Inserting Users" + (repository.findAll().size() == 2 ? " was successful." : " failed.")); }; } + + @Bean + @Profile("dev") + CommandLineRunner initAccessTokenDataBaseDev(AccessTokenRepository repository) { + + return args -> { + LOG.info("Preloading default tokens: " + + repository.save(AccessTokenEntity + .builder() + .userId(0) + .value("token") + .validUntil(Instant.now().getEpochSecond() + AccessTokenBusinessService.ACCESS_TOKEN_DURATION_IN_SECONDS) + .build()) + + repository.save(AccessTokenEntity + .builder() + .userId(1) + .value("token1234") + .validUntil(Instant.now().getEpochSecond() + AccessTokenBusinessService.ACCESS_TOKEN_DURATION_IN_SECONDS) + .build())); + LOG.info("Inserting token" + (repository.findAll().size() == 2 ? " was successful." : " failed.")); + }; + } } \ No newline at end of file diff --git a/src/main/java/de/filefighter/rest/domain/common/DtoServiceInterface.java b/src/main/java/de/filefighter/rest/domain/common/DtoServiceInterface.java new file mode 100644 index 00000000..06605dbb --- /dev/null +++ b/src/main/java/de/filefighter/rest/domain/common/DtoServiceInterface.java @@ -0,0 +1,6 @@ +package de.filefighter.rest.domain.common; + +public interface DtoServiceInterface { + D createDto(E entity); + E findEntity(D dto); +} diff --git a/src/main/java/de/filefighter/rest/domain/common/Utils.java b/src/main/java/de/filefighter/rest/domain/common/Utils.java new file mode 100644 index 00000000..4e81e622 --- /dev/null +++ b/src/main/java/de/filefighter/rest/domain/common/Utils.java @@ -0,0 +1,8 @@ +package de.filefighter.rest.domain.common; + +public class Utils { + + public static boolean stringIsValid(String s){ + return !(null == s || s.isEmpty() || s.isBlank()); + } +} diff --git a/src/main/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessService.java b/src/main/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessService.java index 18b9fc61..60682f72 100644 --- a/src/main/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessService.java +++ b/src/main/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessService.java @@ -1,8 +1,80 @@ package de.filefighter.rest.domain.token.business; +import de.filefighter.rest.domain.token.data.dto.AccessToken; +import de.filefighter.rest.domain.token.data.persistance.AccessTokenEntity; +import de.filefighter.rest.domain.token.data.persistance.AccessTokenRepository; +import de.filefighter.rest.domain.token.exceptions.AccessTokenNotFoundException; +import de.filefighter.rest.domain.user.data.dto.User; +import de.filefighter.rest.domain.user.exceptions.UserNotAuthenticatedException; import org.springframework.stereotype.Service; +import java.time.Instant; +import java.util.UUID; + +import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BASIC_PREFIX; +import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BEARER_PREFIX; +import static de.filefighter.rest.domain.common.Utils.stringIsValid; + @Service public class AccessTokenBusinessService { + + private final AccessTokenRepository accessTokenRepository; + private final AccessTokenDtoService accessTokenDtoService; + public static final long ACCESS_TOKEN_DURATION_IN_SECONDS = 3600L; + public static final long ACCESS_TOKEN_SAFETY_MARGIN = 5L; + + public AccessTokenBusinessService(AccessTokenRepository accessTokenRepository, AccessTokenDtoService accessTokenDtoService) { + this.accessTokenRepository = accessTokenRepository; + this.accessTokenDtoService = accessTokenDtoService; + } + + public AccessToken getValidAccessTokenForUser(User user) { + AccessTokenEntity accessTokenEntity = accessTokenRepository.findByUserId(user.getId()); + long currentTimeSeconds = Instant.now().getEpochSecond(); + + if (null == accessTokenEntity) { + accessTokenEntity = AccessTokenEntity + .builder() + .validUntil(currentTimeSeconds + ACCESS_TOKEN_DURATION_IN_SECONDS) + .value(this.generateRandomTokenValue()) + .userId(user.getId()) + .build(); + accessTokenEntity = accessTokenRepository.save(accessTokenEntity); + } else { + if (currentTimeSeconds + ACCESS_TOKEN_SAFETY_MARGIN > accessTokenEntity.getValidUntil()) { + accessTokenRepository.delete(accessTokenEntity); + accessTokenEntity = AccessTokenEntity + .builder() + .validUntil(currentTimeSeconds + ACCESS_TOKEN_DURATION_IN_SECONDS) + .value(this.generateRandomTokenValue()) + .userId(user.getId()) + .build(); + accessTokenEntity = accessTokenRepository.save(accessTokenEntity); + } + } + + return accessTokenDtoService.createDto(accessTokenEntity); + } + + public AccessToken findAccessTokenByValueAndUserId(String accessTokenValue, long userId) { + if (!stringIsValid(accessTokenValue)) + throw new IllegalArgumentException("AccessTokenValue was not valid."); + + AccessTokenEntity accessTokenEntity = accessTokenRepository.findByUserIdAndValue(userId, accessTokenValue); + if (null == accessTokenEntity) + throw new UserNotAuthenticatedException(userId); + + return accessTokenDtoService.createDto(accessTokenEntity); + } + + public String generateRandomTokenValue() { + return UUID.randomUUID().toString(); + } + + public String checkBearerHeader(String accessTokenValue) { + if (!accessTokenValue.matches("^" + AUTHORIZATION_BEARER_PREFIX + "(.*)$")) + throw new UserNotAuthenticatedException("Header does not contain '" + AUTHORIZATION_BEARER_PREFIX + "', or format is invalid."); + return accessTokenValue.split(AUTHORIZATION_BEARER_PREFIX)[1]; + } } diff --git a/src/main/java/de/filefighter/rest/domain/token/business/AccessTokenDtoService.java b/src/main/java/de/filefighter/rest/domain/token/business/AccessTokenDtoService.java new file mode 100644 index 00000000..c7e2e4a5 --- /dev/null +++ b/src/main/java/de/filefighter/rest/domain/token/business/AccessTokenDtoService.java @@ -0,0 +1,37 @@ +package de.filefighter.rest.domain.token.business; + +import de.filefighter.rest.domain.common.DtoServiceInterface; +import de.filefighter.rest.domain.token.data.dto.AccessToken; +import de.filefighter.rest.domain.token.data.persistance.AccessTokenEntity; +import de.filefighter.rest.domain.token.data.persistance.AccessTokenRepository; +import de.filefighter.rest.domain.token.exceptions.AccessTokenNotFoundException; +import org.springframework.stereotype.Service; + +@Service +public class AccessTokenDtoService implements DtoServiceInterface { + + private final AccessTokenRepository accessTokenRepository; + + public AccessTokenDtoService(AccessTokenRepository accessTokenRepository) { + this.accessTokenRepository = accessTokenRepository; + } + + @Override + public AccessToken createDto(AccessTokenEntity entity) { + return AccessToken + .builder() + .token(entity.getValue()) + .userId(entity.getUserId()) + .validUntil(entity.getValidUntil()) + .build(); + } + + @Override + public AccessTokenEntity findEntity(AccessToken dto) { + AccessTokenEntity accessTokenEntity = accessTokenRepository.findByValue(dto.getToken()); + if (null == accessTokenEntity) + throw new AccessTokenNotFoundException("AccessTokenEntity does not exist for AccessToken: "+ dto); + + return accessTokenEntity; + } +} diff --git a/src/main/java/de/filefighter/rest/domain/token/data/persistance/AccessTokenEntity.java b/src/main/java/de/filefighter/rest/domain/token/data/persistance/AccessTokenEntity.java index 172ca570..3c7dea5a 100644 --- a/src/main/java/de/filefighter/rest/domain/token/data/persistance/AccessTokenEntity.java +++ b/src/main/java/de/filefighter/rest/domain/token/data/persistance/AccessTokenEntity.java @@ -15,5 +15,5 @@ public class AccessTokenEntity { private String value; private long userId; private long validUntil; - + } \ No newline at end of file diff --git a/src/main/java/de/filefighter/rest/domain/token/data/persistance/AccessTokenRepository.java b/src/main/java/de/filefighter/rest/domain/token/data/persistance/AccessTokenRepository.java index 4bf99bb7..92f49f4f 100644 --- a/src/main/java/de/filefighter/rest/domain/token/data/persistance/AccessTokenRepository.java +++ b/src/main/java/de/filefighter/rest/domain/token/data/persistance/AccessTokenRepository.java @@ -7,5 +7,5 @@ public interface AccessTokenRepository extends MongoRepository { AccessTokenEntity findByUserId(long userId); AccessTokenEntity findByValue(String value); - void deleteByUserId(long userId); + AccessTokenEntity findByUserIdAndValue(long userId, String value); } diff --git a/src/main/java/de/filefighter/rest/domain/token/exceptions/TokenNotFoundAdvise.java b/src/main/java/de/filefighter/rest/domain/token/exceptions/AccessTokenNotFoundAdvise.java similarity index 75% rename from src/main/java/de/filefighter/rest/domain/token/exceptions/TokenNotFoundAdvise.java rename to src/main/java/de/filefighter/rest/domain/token/exceptions/AccessTokenNotFoundAdvise.java index 443253fd..c5e3c583 100644 --- a/src/main/java/de/filefighter/rest/domain/token/exceptions/TokenNotFoundAdvise.java +++ b/src/main/java/de/filefighter/rest/domain/token/exceptions/AccessTokenNotFoundAdvise.java @@ -11,13 +11,13 @@ import org.springframework.web.bind.annotation.ResponseStatus; @ControllerAdvice -public class TokenNotFoundAdvise { +public class AccessTokenNotFoundAdvise { @ResponseBody - @ExceptionHandler(TokenNotFoundException.class) + @ExceptionHandler(AccessTokenNotFoundException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) - ResponseEntity tokenNotFoundAdvise(TokenNotFoundException ex) { + ResponseEntity tokenNotFoundAdvise(AccessTokenNotFoundException ex) { LoggerFactory.getLogger(UserAlreadyExistsAdvise.class).warn(ex.getMessage()); - return new ResponseEntity<>(new ServerResponse("Denied", ex.getMessage()), HttpStatus.BAD_REQUEST); + return new ResponseEntity<>(new ServerResponse("denied", ex.getMessage()), HttpStatus.BAD_REQUEST); } } diff --git a/src/main/java/de/filefighter/rest/domain/token/exceptions/AccessTokenNotFoundException.java b/src/main/java/de/filefighter/rest/domain/token/exceptions/AccessTokenNotFoundException.java new file mode 100644 index 00000000..adecc922 --- /dev/null +++ b/src/main/java/de/filefighter/rest/domain/token/exceptions/AccessTokenNotFoundException.java @@ -0,0 +1,8 @@ +package de.filefighter.rest.domain.token.exceptions; + +public class AccessTokenNotFoundException extends RuntimeException { + + public AccessTokenNotFoundException(String reason) { + super(reason); + } +} diff --git a/src/main/java/de/filefighter/rest/domain/token/exceptions/TokenNotFoundException.java b/src/main/java/de/filefighter/rest/domain/token/exceptions/TokenNotFoundException.java deleted file mode 100644 index 3437b430..00000000 --- a/src/main/java/de/filefighter/rest/domain/token/exceptions/TokenNotFoundException.java +++ /dev/null @@ -1,8 +0,0 @@ -package de.filefighter.rest.domain.token.exceptions; - -public class TokenNotFoundException extends RuntimeException { - - public TokenNotFoundException(String reason) { - super(reason); - } -} diff --git a/src/main/java/de/filefighter/rest/domain/user/business/UserBusinessService.java b/src/main/java/de/filefighter/rest/domain/user/business/UserBusinessService.java index 7d2851c0..37f5a9e3 100644 --- a/src/main/java/de/filefighter/rest/domain/user/business/UserBusinessService.java +++ b/src/main/java/de/filefighter/rest/domain/user/business/UserBusinessService.java @@ -1,17 +1,108 @@ package de.filefighter.rest.domain.user.business; +import de.filefighter.rest.domain.token.data.dto.AccessToken; +import de.filefighter.rest.domain.token.data.dto.RefreshToken; +import de.filefighter.rest.domain.user.data.dto.User; +import de.filefighter.rest.domain.user.data.persistance.UserEntity; import de.filefighter.rest.domain.user.data.persistance.UserRepository; +import de.filefighter.rest.domain.user.exceptions.UserNotAuthenticatedException; +import de.filefighter.rest.domain.user.exceptions.UserNotFoundException; import org.springframework.stereotype.Service; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.sql.Ref; +import java.util.Base64; +import java.util.UUID; + +import static de.filefighter.rest.configuration.RestConfiguration.*; +import static de.filefighter.rest.domain.common.Utils.stringIsValid; + @Service public class UserBusinessService { + private final UserRepository userRepository; + private final UserDtoService userDtoService; - public UserBusinessService(UserRepository userRepository) { + public UserBusinessService(UserRepository userRepository, UserDtoService userDtoService) { this.userRepository = userRepository; + this.userDtoService = userDtoService; } - public long getUserCount(){ + public long getUserCount() { return userRepository.count(); } + + public User getUserByUsernameAndPassword(String base64encodedUserAndPassword) { + if (!stringIsValid(base64encodedUserAndPassword)) + throw new UserNotAuthenticatedException("Header was empty."); + + //TODO: maybe filter unsupported characters? + if (!base64encodedUserAndPassword.matches("^"+AUTHORIZATION_BASIC_PREFIX+"(.*)$")) + throw new UserNotAuthenticatedException("Header does not contain '"+AUTHORIZATION_BASIC_PREFIX+"', or format is invalid."); + + String[] split = base64encodedUserAndPassword.split(AUTHORIZATION_BASIC_PREFIX); + + base64encodedUserAndPassword = split[1]; + String decodedUsernameUndPassword; + try { + byte[] decodedValue = Base64.getDecoder().decode(base64encodedUserAndPassword); + decodedUsernameUndPassword = new String(decodedValue, StandardCharsets.UTF_8.toString()); + } catch (UnsupportedEncodingException ex) { + throw new RuntimeException(ex); + } + + split = decodedUsernameUndPassword.strip().split(":"); + + if (split.length != 2) + throw new UserNotAuthenticatedException("Credentials didnt meet formal requirements."); + + String username = split[0]; + String password = split[1]; + + UserEntity userEntity = userRepository.findByUsernameAndPassword(username, password); + if (null == userEntity) + throw new UserNotAuthenticatedException("No User found for username or password."); + + return userDtoService.createDto(userEntity); + } + + public RefreshToken getRefreshTokenForUser(User user) { + UserEntity userEntity = userRepository.findByUserIdAndUsername(user.getId(), user.getUsername()); + if (null == userEntity) + throw new UserNotFoundException(); + + String refreshTokenValue = userEntity.getRefreshToken(); + + if(!stringIsValid(refreshTokenValue)) + throw new IllegalStateException("RefreshToken was empty in db."); + + return RefreshToken + .builder() + .refreshToken(refreshTokenValue) + .user(user) + .build(); + } + + public User getUserByRefreshTokenAndUserId(String refreshToken, long userId) { + if (!stringIsValid(refreshToken)) + throw new UserNotAuthenticatedException("RefreshToken was not valid."); + + UserEntity userEntity = userRepository.findByRefreshTokenAndUserId(refreshToken, userId); + if (null == userEntity) + throw new UserNotAuthenticatedException(userId); + + return userDtoService.createDto(userEntity); + } + + public User getUserByAccessTokenAndUserId(AccessToken accessToken, long userId) { + if(accessToken.getUserId() != userId) + throw new UserNotAuthenticatedException(userId); + + UserEntity userEntity = userRepository.findByUserId(userId); + if (null == userEntity) + throw new UserNotFoundException(); + + return userDtoService.createDto(userEntity); + } } diff --git a/src/main/java/de/filefighter/rest/domain/user/business/UserDtoService.java b/src/main/java/de/filefighter/rest/domain/user/business/UserDtoService.java index 2448706d..d6d2631d 100644 --- a/src/main/java/de/filefighter/rest/domain/user/business/UserDtoService.java +++ b/src/main/java/de/filefighter/rest/domain/user/business/UserDtoService.java @@ -3,18 +3,38 @@ import de.filefighter.rest.domain.common.DtoServiceInterface; import de.filefighter.rest.domain.user.data.dto.User; import de.filefighter.rest.domain.user.data.persistance.UserEntity; +import de.filefighter.rest.domain.user.data.persistance.UserRepository; +import de.filefighter.rest.domain.user.exceptions.UserNotFoundException; +import de.filefighter.rest.domain.user.role.GroupRepository; import org.springframework.stereotype.Service; @Service public class UserDtoService implements DtoServiceInterface { + private final GroupRepository groupRepository; + private final UserRepository userRepository; + + public UserDtoService(GroupRepository groupRepository, UserRepository userRepository) { + this.groupRepository = groupRepository; + this.userRepository = userRepository; + } + @Override public User createDto(UserEntity entity) { - return null; + return User + .builder() + .id(entity.getUserId()) + .username(entity.getUsername()) + .groups(groupRepository.getRolesByIds(entity.getGroupIds())) + .create(); } @Override - public UserEntity createEntity(User dto) { - return null; + public UserEntity findEntity(User dto) { + UserEntity userEntity = userRepository.findByUserIdAndUsername(dto.getId(), dto.getUsername()); + if (null == userEntity) + throw new UserNotFoundException(dto.getId()); + + return userEntity; } } diff --git a/src/main/java/de/filefighter/rest/domain/user/data/dto/User.java b/src/main/java/de/filefighter/rest/domain/user/data/dto/User.java index b1c5bb80..6d5a726a 100644 --- a/src/main/java/de/filefighter/rest/domain/user/data/dto/User.java +++ b/src/main/java/de/filefighter/rest/domain/user/data/dto/User.java @@ -9,11 +9,11 @@ public class User { private long id; private String username; - private Groups[] roles; + private Groups[] groups; public User(long id, String username, Groups... roles) { this.id = id; this.username = username; - this.roles = roles; + this.groups = roles; } } \ No newline at end of file diff --git a/src/main/java/de/filefighter/rest/domain/user/data/persistance/UserRepository.java b/src/main/java/de/filefighter/rest/domain/user/data/persistance/UserRepository.java index aafc05be..27d51680 100644 --- a/src/main/java/de/filefighter/rest/domain/user/data/persistance/UserRepository.java +++ b/src/main/java/de/filefighter/rest/domain/user/data/persistance/UserRepository.java @@ -5,7 +5,8 @@ @Service public interface UserRepository extends MongoRepository { - UserEntity findByUserId(long userId); + UserEntity findByUserIdAndUsername(long userId, String username); UserEntity findByUsernameAndPassword(String username, String password); - UserEntity findByRefreshToken(String refreshToken); + UserEntity findByRefreshTokenAndUserId(String refreshToken, long userId); + UserEntity findByUserId(long userId); } diff --git a/src/main/java/de/filefighter/rest/domain/user/exceptions/UserAlreadyExistsAdvise.java b/src/main/java/de/filefighter/rest/domain/user/exceptions/UserAlreadyExistsAdvise.java index e5d89cb6..bd9cfbde 100644 --- a/src/main/java/de/filefighter/rest/domain/user/exceptions/UserAlreadyExistsAdvise.java +++ b/src/main/java/de/filefighter/rest/domain/user/exceptions/UserAlreadyExistsAdvise.java @@ -17,6 +17,6 @@ public class UserAlreadyExistsAdvise { ResponseEntity userAlreadyExistsAdvise(UserAlreadyExistsException ex) { LoggerFactory.getLogger(UserAlreadyExistsAdvise.class).warn(ex.getMessage()); - return new ResponseEntity<>(new ServerResponse("Denied", ex.getMessage()), HttpStatus.BAD_REQUEST); + return new ResponseEntity<>(new ServerResponse("denied", ex.getMessage()), HttpStatus.BAD_REQUEST); } } diff --git a/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotAuthenticatedAdvise.java b/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotAuthenticatedAdvise.java index 8f65bb53..3fb11dbb 100644 --- a/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotAuthenticatedAdvise.java +++ b/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotAuthenticatedAdvise.java @@ -17,6 +17,6 @@ public class UserNotAuthenticatedAdvise { @ResponseStatus(HttpStatus.UNAUTHORIZED) ResponseEntity userNotAuthenticatedHandler(UserNotAuthenticatedException ex) { LoggerFactory.getLogger(UserAlreadyExistsAdvise.class).warn(ex.getMessage()); - return new ResponseEntity<>(new ServerResponse("Denied", ex.getMessage()), HttpStatus.UNAUTHORIZED); + return new ResponseEntity<>(new ServerResponse("denied", ex.getMessage()), HttpStatus.UNAUTHORIZED); } } \ No newline at end of file diff --git a/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotAuthenticatedException.java b/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotAuthenticatedException.java index 68cc6f85..7b31ae1e 100644 --- a/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotAuthenticatedException.java +++ b/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotAuthenticatedException.java @@ -5,9 +5,9 @@ public UserNotAuthenticatedException(String reason){ super("User could not be authenticated. "+reason); } - public UserNotAuthenticatedException() { - super("User could not be authenticated."); - } +// public UserNotAuthenticatedException() { +// super("User could not be authenticated."); +// } public UserNotAuthenticatedException(long id){ super("User with the id "+id+" could not be authenticated."); diff --git a/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotFoundAdvice.java b/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotFoundAdvice.java index bf255f31..3df30506 100644 --- a/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotFoundAdvice.java +++ b/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotFoundAdvice.java @@ -17,6 +17,6 @@ class UserNotFoundAdvice { @ResponseStatus(HttpStatus.NOT_FOUND) ResponseEntity userNotFoundHandler(UserNotFoundException ex) { LoggerFactory.getLogger(UserAlreadyExistsAdvise.class).warn(ex.getMessage()); - return new ResponseEntity<>(new ServerResponse("Denied", ex.getMessage()), HttpStatus.NOT_FOUND); + return new ResponseEntity<>(new ServerResponse("denied", ex.getMessage()), HttpStatus.NOT_FOUND); } } diff --git a/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotFoundException.java b/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotFoundException.java index 463b00d8..9c89d4d8 100644 --- a/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotFoundException.java +++ b/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotFoundException.java @@ -2,6 +2,10 @@ public class UserNotFoundException extends RuntimeException { + public UserNotFoundException(){ + super("User not found."); + } + public UserNotFoundException(long id) { super("Could not find user " + id); } diff --git a/src/main/java/de/filefighter/rest/domain/user/rest/UserRestController.java b/src/main/java/de/filefighter/rest/domain/user/rest/UserRestController.java index 4b4b1b38..1dc9a1a1 100644 --- a/src/main/java/de/filefighter/rest/domain/user/rest/UserRestController.java +++ b/src/main/java/de/filefighter/rest/domain/user/rest/UserRestController.java @@ -49,7 +49,7 @@ public ResponseEntity getAccessTokenAndUserInfoByRefreshTokenAndUse @PathVariable long userId, @RequestHeader(value = "Authorization", defaultValue = AUTHORIZATION_BEARER_PREFIX + "token") String refreshToken) { - LOG.info("Requested refreshing for user {} with token {}.", userId, refreshToken); + LOG.info("Requested login for user {} with token {}.", userId, refreshToken); return userRestService.getAccessTokenByRefreshTokenAndUserId(refreshToken, userId); } diff --git a/src/main/java/de/filefighter/rest/domain/user/rest/UserRestService.java b/src/main/java/de/filefighter/rest/domain/user/rest/UserRestService.java index 8771d8e0..d5e2048c 100644 --- a/src/main/java/de/filefighter/rest/domain/user/rest/UserRestService.java +++ b/src/main/java/de/filefighter/rest/domain/user/rest/UserRestService.java @@ -1,9 +1,12 @@ package de.filefighter.rest.domain.user.rest; +import de.filefighter.rest.domain.token.business.AccessTokenBusinessService; import de.filefighter.rest.domain.token.data.dto.AccessToken; import de.filefighter.rest.domain.token.data.dto.RefreshToken; +import de.filefighter.rest.domain.user.business.UserBusinessService; import de.filefighter.rest.domain.user.data.dto.User; import de.filefighter.rest.domain.user.data.dto.UserRegisterForm; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; @@ -11,19 +14,35 @@ @Service public class UserRestService implements UserRestServiceInterface { + private final UserBusinessService userBusinessService; + private final AccessTokenBusinessService accessTokenBusinessService; + + public UserRestService(UserBusinessService userBusinessService, AccessTokenBusinessService accessTokenBusinessService) { + this.userBusinessService = userBusinessService; + this.accessTokenBusinessService = accessTokenBusinessService; + } + @Override - public ResponseEntity getUserByAccessTokenAndUserId(String accessToken, long userId) { - return null; + public ResponseEntity getUserByAccessTokenAndUserId(String accessTokenValue, long userId) { + String cleanValue = accessTokenBusinessService.checkBearerHeader(accessTokenValue); + AccessToken accessToken = accessTokenBusinessService.findAccessTokenByValueAndUserId(cleanValue, userId); + User user = userBusinessService.getUserByAccessTokenAndUserId(accessToken, userId); + return new ResponseEntity<>(user, HttpStatus.OK); } @Override public ResponseEntity getRefreshTokenWithUsernameAndPassword(String base64encodedUserAndPassword) { - return null; + User user = userBusinessService.getUserByUsernameAndPassword(base64encodedUserAndPassword); + RefreshToken refreshToken = userBusinessService.getRefreshTokenForUser(user); + return new ResponseEntity<>(refreshToken, HttpStatus.OK); } @Override public ResponseEntity getAccessTokenByRefreshTokenAndUserId(String refreshToken, long userId) { - return null; + String cleanValue = accessTokenBusinessService.checkBearerHeader(refreshToken); + User user = userBusinessService.getUserByRefreshTokenAndUserId(cleanValue, userId); + AccessToken accessToken = accessTokenBusinessService.getValidAccessTokenForUser(user); + return new ResponseEntity<>(accessToken, HttpStatus.OK); } @Override diff --git a/src/main/java/de/filefighter/rest/domain/user/role/GroupRepository.java b/src/main/java/de/filefighter/rest/domain/user/role/GroupRepository.java index 484ed9ec..557a2e10 100644 --- a/src/main/java/de/filefighter/rest/domain/user/role/GroupRepository.java +++ b/src/main/java/de/filefighter/rest/domain/user/role/GroupRepository.java @@ -17,7 +17,11 @@ public Groups getRoleById(long id) { } public Groups[] getRolesByIds(long... ids){ - Groups[] roles = new Groups[ids.length]; //TODO: check this again. + Groups[] roles; + if(null == ids){ + return new Groups[0]; + } + roles = new Groups[ids.length]; for (int i = 0; i < ids.length; i++) { roles[i] = this.getRoleById(ids[i]); diff --git a/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java b/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java index 4e6f1c7e..bf9666cf 100644 --- a/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java +++ b/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java @@ -148,7 +148,7 @@ public void responseContainsKeyAndValue(String key, String value) throws JsonPro @And("response contains the user with id {long}") public void responseContainsTheUserWithId(long userId) throws JsonProcessingException { JsonNode rootNode = objectMapper.readTree(latestResponse.getBody()); - long actualValue = rootNode.get("userId").asLong(); + long actualValue = rootNode.get("id").asLong(); assertEquals(userId, actualValue); } diff --git a/src/test/java/de/filefighter/rest/cucumber/SystemHealthSteps.java b/src/test/java/de/filefighter/rest/cucumber/SystemHealthSteps.java index 813926d6..57498ad2 100644 --- a/src/test/java/de/filefighter/rest/cucumber/SystemHealthSteps.java +++ b/src/test/java/de/filefighter/rest/cucumber/SystemHealthSteps.java @@ -1,12 +1,21 @@ package de.filefighter.rest.cucumber; import de.filefighter.rest.RestApplicationIntegrationTest; +import io.cucumber.java.en.And; import io.cucumber.java.en.When; import org.springframework.http.HttpMethod; +import java.util.Timer; +import java.util.concurrent.TimeUnit; + public class SystemHealthSteps extends RestApplicationIntegrationTest { @When("the systemHealth endpoint is requested") public void theSystemHealthEndpointIsRequested() { executeRestApiCall(HttpMethod.GET, "health/"); } + + @And("the user waits for {int} second\\(s)") + public void theUserWaitsForSecondS(int seconds) throws InterruptedException { + TimeUnit.SECONDS.sleep(seconds); + } } diff --git a/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java b/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java index d2cfcf71..ff8ac65c 100644 --- a/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java +++ b/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java @@ -23,8 +23,9 @@ public class UserAuthorizationSteps extends RestApplicationIntegrationTest { @When("user requests login with username {string} and password {string}") public void userRequestsLoginWithUsernameAndPassword(String username, String password) { - String authString = AUTHORIZATION_BASIC_PREFIX + username + ":" + password; + String authString = username + ":" + password; String base64encoded = Base64.encode(authString.getBytes()); + base64encoded = AUTHORIZATION_BASIC_PREFIX+ base64encoded; HashMap authHeader = new HashMap<>(); authHeader.put("Authorization", base64encoded); @@ -64,10 +65,22 @@ public void responseContainsValidAccessTokenForUser(long userId) throws JsonProc long validUntil = rootNode.get("validUntil").asLong(); int expectedTokenLength = UUID.randomUUID().toString().length(); - long expectedValidUntil = Instant.now().getEpochSecond(); + int actualTokenLength = tokenValue.length(); + boolean isStillViable = validUntil > Instant.now().getEpochSecond(); - assertEquals(expectedTokenLength, tokenValue.length()); - assertTrue(validUntil > expectedValidUntil); + assertEquals(expectedTokenLength, actualTokenLength); + assertTrue(isStillViable); assertEquals(userId, actualUserId); } + + @And("response contains refreshToken {string} and the user with id {long}") + public void responseContainsRefreshTokenAndTheUserWithId(String refreshToken, long userId) throws JsonProcessingException { + JsonNode rootNode = objectMapper.readTree(latestResponse.getBody()); + String actualRefreshToken = rootNode.get("refreshToken").asText(); + JsonNode userNode = rootNode.get("user"); + long actualUserId = userNode.get("id").asLong(); + + assertEquals(userId, actualUserId); + assertEquals(refreshToken, actualRefreshToken); + } } diff --git a/src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestIntegrationTest.java b/src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestIntegrationTest.java index fd13c08f..0b13019c 100644 --- a/src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestIntegrationTest.java +++ b/src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestIntegrationTest.java @@ -58,7 +58,7 @@ public void healthCheckShouldContainVariablesAndCorrectValues() throws JsonProce .username("admin") .password("admin") .refreshToken("refreshToken1234") - .roleIds(new long[]{0, 1}) + .groupIds(new long[]{0, 1}) .build())); String jsonString = this.restTemplate.getForObject("http://localhost:" + port + "/health", String.class); diff --git a/src/test/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessServiceUnitTest.java b/src/test/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessServiceUnitTest.java new file mode 100644 index 00000000..36cbc258 --- /dev/null +++ b/src/test/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessServiceUnitTest.java @@ -0,0 +1,25 @@ +package de.filefighter.rest.domain.token.business; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class AccessTokenBusinessServiceUnitTest { + + @BeforeEach + void setUp() { + } + + @Test + void getValidAccessTokenForUser() { + } + + @Test + void getAccessTokenByValueAndId() { + } + + @Test + void generateRandomTokenValue() { + } +} \ No newline at end of file diff --git a/src/test/java/de/filefighter/rest/domain/token/business/AccessTokenDtoServiceUnitTest.java b/src/test/java/de/filefighter/rest/domain/token/business/AccessTokenDtoServiceUnitTest.java new file mode 100644 index 00000000..12e5aef9 --- /dev/null +++ b/src/test/java/de/filefighter/rest/domain/token/business/AccessTokenDtoServiceUnitTest.java @@ -0,0 +1,21 @@ +package de.filefighter.rest.domain.token.business; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class AccessTokenDtoServiceUnitTest { + + @BeforeEach + void setUp() { + } + + @Test + void createDto() { + } + + @Test + void findEntity() { + } +} \ No newline at end of file diff --git a/src/test/java/de/filefighter/rest/domain/user/business/UserBusinessServiceUnitTest.java b/src/test/java/de/filefighter/rest/domain/user/business/UserBusinessServiceUnitTest.java index 919ba089..778382e4 100644 --- a/src/test/java/de/filefighter/rest/domain/user/business/UserBusinessServiceUnitTest.java +++ b/src/test/java/de/filefighter/rest/domain/user/business/UserBusinessServiceUnitTest.java @@ -4,18 +4,19 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class UserBusinessServiceUnitTest { private final UserRepository userRepositoryMock = mock(UserRepository.class); + private final UserDtoService userDtoServiceMock = mock(UserDtoService.class); private UserBusinessService userBusinessService; @BeforeEach void setUp() { - userBusinessService = new UserBusinessService(userRepositoryMock); + userBusinessService = new UserBusinessService(userRepositoryMock, userDtoServiceMock); } @Test @@ -28,4 +29,24 @@ void getUserCount() { assertEquals(expectedCount, actualCount); } + + @Test + void getUserByUsernameAndPassword() { + } + + @Test + void getRefreshTokenForUser() { + } + + @Test + void getUserByRefreshTokenAndUserId() { + } + + @Test + void getUserByAccessTokenAndUserId() { + } + + @Test + void getUserEntityByUserIdAndUsername() { + } } \ No newline at end of file diff --git a/src/test/java/de/filefighter/rest/domain/user/business/UserDtoServiceUnitTest.java b/src/test/java/de/filefighter/rest/domain/user/business/UserDtoServiceUnitTest.java new file mode 100644 index 00000000..8275936a --- /dev/null +++ b/src/test/java/de/filefighter/rest/domain/user/business/UserDtoServiceUnitTest.java @@ -0,0 +1,21 @@ +package de.filefighter.rest.domain.user.business; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class UserDtoServiceUnitTest { + + @BeforeEach + void setUp() { + } + + @Test + void createDto() { + } + + @Test + void findEntity() { + } +} \ No newline at end of file diff --git a/src/test/java/de/filefighter/rest/domain/user/rest/UserRestControllerUnitTest.java b/src/test/java/de/filefighter/rest/domain/user/rest/UserRestControllerUnitTest.java index 6d146269..fe3692a2 100644 --- a/src/test/java/de/filefighter/rest/domain/user/rest/UserRestControllerUnitTest.java +++ b/src/test/java/de/filefighter/rest/domain/user/rest/UserRestControllerUnitTest.java @@ -26,7 +26,7 @@ void setUp() { @Test void registerNewUser() { - User user = User.builder().id(420).roles(null).username("kevin").create(); + User user = User.builder().id(420).groups(null).username("kevin").create(); ResponseEntity expectedUser = new ResponseEntity<>(user, OK); when(userRestServiceMock.registerNewUserWithAccessToken(any(), any())).thenReturn(expectedUser); @@ -38,7 +38,7 @@ void registerNewUser() { @Test void loginUserWithUsernameAndPassword() { - User user = User.builder().id(420).roles(null).username("kevin").create(); + User user = User.builder().id(420).groups(null).username("kevin").create(); RefreshToken refreshToken = RefreshToken.builder().refreshToken("token").user(user).build(); ResponseEntity expectedRefreshToken = new ResponseEntity<>(refreshToken, OK); @@ -62,7 +62,7 @@ void getAccessTokenAndUserInfoByRefreshTokenAndUserId() { @Test void getUserInfoWithAccessToken() { - User user = User.builder().id(420).roles(null).username("kevin").create(); + User user = User.builder().id(420).groups(null).username("kevin").create(); ResponseEntity expectedUser = new ResponseEntity<>(user, OK); when(userRestServiceMock.getUserByAccessTokenAndUserId("token", 420)).thenReturn(expectedUser); @@ -73,7 +73,7 @@ void getUserInfoWithAccessToken() { @Test void updateUserWithAccessToken() { - User user = User.builder().id(420).roles(null).username("kevin").create(); + User user = User.builder().id(420).groups(null).username("kevin").create(); ResponseEntity expectedUser = new ResponseEntity<>(user, OK); UserRegisterForm userRegisterForm = UserRegisterForm.builder().create(); @@ -85,7 +85,7 @@ void updateUserWithAccessToken() { @Test void findUserByUsername(){ - User user = User.builder().id(420).roles(null).username("kevin").create(); + User user = User.builder().id(420).groups(null).username("kevin").create(); ResponseEntity expectedUser = new ResponseEntity<>(user, OK); String username="kevin"; diff --git a/src/test/java/de/filefighter/rest/domain/user/rest/UserRestServiceUnitTest.java b/src/test/java/de/filefighter/rest/domain/user/rest/UserRestServiceUnitTest.java new file mode 100644 index 00000000..0c0b99cb --- /dev/null +++ b/src/test/java/de/filefighter/rest/domain/user/rest/UserRestServiceUnitTest.java @@ -0,0 +1,37 @@ +package de.filefighter.rest.domain.user.rest; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class UserRestServiceUnitTest { + + @BeforeEach + void setUp() { + } + + @Test + void getUserByAccessTokenAndUserId() { + } + + @Test + void getRefreshTokenWithUsernameAndPassword() { + } + + @Test + void getAccessTokenByRefreshTokenAndUserId() { + } + + @Test + void updateUserByAccessTokenAndUserId() { + } + + @Test + void registerNewUserWithAccessToken() { + } + + @Test + void findUserByUsernameAndAccessToken() { + } +} \ No newline at end of file diff --git a/src/test/resources/SystemHealth.feature b/src/test/resources/SystemHealth.feature index 40433196..9a0107b2 100644 --- a/src/test/resources/SystemHealth.feature +++ b/src/test/resources/SystemHealth.feature @@ -13,6 +13,7 @@ Scenario: SystemHealth is requested without users in db Scenario: SystemHealth is requested with users in db Given user 1234 exists And user 3214 exists + And the user waits for 1 second(s) When the systemHealth endpoint is requested Then response contains key "userCount" and value "2" And response contains key "uptimeInSeconds" and value of at least 1 diff --git a/src/test/resources/UserAuthorization.feature b/src/test/resources/UserAuthorization.feature index d055fc97..509b3e8b 100644 --- a/src/test/resources/UserAuthorization.feature +++ b/src/test/resources/UserAuthorization.feature @@ -1,51 +1,50 @@ -#Feature: User Authorization -# As a user -# I want to be able to log in with username and password, as well as verify my identity -# when using the endpoints. -# -#Background: -# Given database is empty -# And user with id 1234 exists and has username "user", password "secure_password" and refreshToken "token" -# -#Scenario: Successful login with username and password. -# When user requests login with username "user" and password "secure_password" -# Then response contains key "refreshToken" and value "token" -# And response status code is 200 -# And response contains the user with id 1234 -# -#Scenario: Failed login with username and password. -# When user requests login with username "user" and password "wrong_password" -# Then response contains key "message" and value "User not authenticated." -# And response contains key "status" and value "denied" -# And response status code is 401 -# -#Scenario: Successful creation of new accessToken with refreshToken. -# When user requests accessToken with refreshToken "token" and userId 1234 -# Then response contains key "userId" and value "1234" -# And response contains valid accessToken for user 1234 -# And response status code is 200 -# -#Scenario: Successful request of existing accessToken with refreshToken. -# Given accessToken with value "token" exists for user 1234 -# When user requests accessToken with refreshToken "token" and userId 1234 -# Then response contains key "userId" and value "1234" -# And response contains valid accessToken for user 1234 -# And response status code is 200 -# -# Scenario: Failed retrieval of accessToken with wrong refreshToken. -# When user requests accessToken with refreshToken "not_the_token" and userId 1234 -# Then response contains key "message" and value "User not authenticated." -# And response contains key "status" and value "denied" -# And response status code is 401 -# -#Scenario: Successful UserInfo request with valid accessToken. -# Given accessToken with value "accessToken" exists for user 1234 -# When user requests userInfo with accessToken "accessToken" and userId 1234 -# Then response contains the user with id 1234 -# And response status code is 200 -# -#Scenario: Failed UserInfo request with invalid accessToken. -# When user requests userInfo with accessToken "notTheAccessToken" and userId 1234 -# Then response contains key "message" and value "User not authenticated." -# And response contains key "status" and value "denied" -# And response status code is 401 \ No newline at end of file +Feature: User Authorization + As a user + I want to be able to log in with username and password, as well as verify my identity + when using the endpoints. + +Background: + Given database is empty + And user with id 1234 exists and has username "user", password "secure_password" and refreshToken "token" + +Scenario: Successful login with username and password. + When user requests login with username "user" and password "secure_password" + And response status code is 200 + And response contains refreshToken "token" and the user with id 1234 + +Scenario: Failed login with username and password. + When user requests login with username "user" and password "wrong_password" + Then response contains key "message" and value "User could not be authenticated. No User found for username or password." + And response contains key "status" and value "denied" + And response status code is 401 + +Scenario: Successful creation of new accessToken with refreshToken. + When user requests accessToken with refreshToken "token" and userId 1234 + Then response contains key "userId" and value "1234" + And response contains valid accessToken for user 1234 + And response status code is 200 + +Scenario: Successful request of existing accessToken with refreshToken. + Given accessToken with value "6bb9cb4f-7b51-4c0a-8013-ed7a34e56282" exists for user 1234 + When user requests accessToken with refreshToken "token" and userId 1234 + Then response contains key "userId" and value "1234" + And response contains valid accessToken for user 1234 + And response status code is 200 + + Scenario: Failed retrieval of accessToken with wrong refreshToken. + When user requests accessToken with refreshToken "not_the_token" and userId 1234 + Then response contains key "message" and value "User with the id 1234 could not be authenticated." + And response contains key "status" and value "denied" + And response status code is 401 + +Scenario: Successful UserInfo request with valid accessToken. + Given accessToken with value "6bb9cb4f-7b51-4c0a-8013-ed7a34e56282" exists for user 1234 + When user requests userInfo with accessToken "6bb9cb4f-7b51-4c0a-8013-ed7a34e56282" and userId 1234 + Then response contains the user with id 1234 + And response status code is 200 + +Scenario: Failed UserInfo request with invalid accessToken. + When user requests userInfo with accessToken "tokenInWrongFormat" and userId 1234 + Then response contains key "message" and value "User with the id 1234 could not be authenticated." + And response contains key "status" and value "denied" + And response status code is 401 \ No newline at end of file From eaa33ec76f99db5f21549ea3e3ac721a81c4dfaf Mon Sep 17 00:00:00 2001 From: open-schnick Date: Wed, 4 Nov 2020 00:31:01 +0100 Subject: [PATCH 4/8] Implemented requested changes by @qvalentin (#9) --- .../rest/cucumber/CommonCucumberSteps.java | 14 ------ .../rest/cucumber/UserAuthorizationSteps.java | 45 ++++++++++++++++++- src/test/resources/UserAuthorization.feature | 14 ++++-- 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java b/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java index bf9666cf..872291b6 100644 --- a/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java +++ b/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java @@ -6,10 +6,7 @@ import de.filefighter.rest.RestApplicationIntegrationTest; import de.filefighter.rest.domain.filesystem.data.persistance.FileSystemEntity; import de.filefighter.rest.domain.filesystem.data.persistance.FileSystemRepository; -import de.filefighter.rest.domain.token.business.AccessTokenBusinessService; -import de.filefighter.rest.domain.token.data.persistance.AccessTokenEntity; import de.filefighter.rest.domain.token.data.persistance.AccessTokenRepository; -import de.filefighter.rest.domain.user.business.UserBusinessService; import de.filefighter.rest.domain.user.data.persistance.UserEntity; import de.filefighter.rest.domain.user.data.persistance.UserRepository; import io.cucumber.java.en.And; @@ -18,11 +15,8 @@ import org.springframework.beans.factory.annotation.Autowired; import java.io.IOException; -import java.time.Instant; import java.util.Arrays; -import java.util.UUID; -import static de.filefighter.rest.domain.token.business.AccessTokenBusinessService.ACCESS_TOKEN_DURATION_IN_SECONDS; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -48,14 +42,6 @@ public void databaseIsEmpty() { fileSystemRepository.deleteAll(); } - @Given("accessToken with value {string} exists for user {long}") - public void accessTokenWithValueExistsForUser(String tokenValue, long userId) { - accessTokenRepository.save(AccessTokenEntity.builder() - .userId(userId) - .value(tokenValue) - .validUntil(Instant.now().getEpochSecond()+ ACCESS_TOKEN_DURATION_IN_SECONDS).build()); - } - @And("user {long} exists") public void userExists(long userId) { userRepository.save(UserEntity diff --git a/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java b/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java index ff8ac65c..b8a1f607 100644 --- a/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java +++ b/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java @@ -4,9 +4,13 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import de.filefighter.rest.RestApplicationIntegrationTest; +import de.filefighter.rest.domain.token.data.persistance.AccessTokenEntity; +import de.filefighter.rest.domain.token.data.persistance.AccessTokenRepository; import io.cucumber.java.en.And; +import io.cucumber.java.en.Given; import io.cucumber.java.en.When; import org.bson.internal.Base64; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpMethod; import java.time.Instant; @@ -15,17 +19,44 @@ import static com.mongodb.internal.connection.tlschannel.util.Util.assertTrue; import static de.filefighter.rest.configuration.RestConfiguration.*; +import static de.filefighter.rest.domain.token.business.AccessTokenBusinessService.ACCESS_TOKEN_DURATION_IN_SECONDS; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; public class UserAuthorizationSteps extends RestApplicationIntegrationTest { - private final ObjectMapper objectMapper = new ObjectMapper(); + private final ObjectMapper objectMapper; + private final AccessTokenRepository accessTokenRepository; + + @Autowired + public UserAuthorizationSteps(AccessTokenRepository accessTokenRepository) { + this.objectMapper = new ObjectMapper(); + this.accessTokenRepository = accessTokenRepository; + } + + + @Given("accessToken with value {string} exists for user {long} and is valid until {long}") + public void accessTokenWithValueExistsForUserAndIsValidUntil(String tokenValue, long userId, long validUntil) { + accessTokenRepository.save(AccessTokenEntity.builder() + .userId(userId) + .value(tokenValue) + .validUntil(validUntil) + .validUntil(Instant.now().getEpochSecond()+ ACCESS_TOKEN_DURATION_IN_SECONDS).build()); + } + + @Given("accessToken with value {string} exists for user {long}") + public void accessTokenWithValueExistsForUser(String tokenValue, long userId) { + accessTokenRepository.save(AccessTokenEntity.builder() + .userId(userId) + .value(tokenValue) + .validUntil(Instant.now().getEpochSecond()+ ACCESS_TOKEN_DURATION_IN_SECONDS).build()); + } @When("user requests login with username {string} and password {string}") public void userRequestsLoginWithUsernameAndPassword(String username, String password) { String authString = username + ":" + password; String base64encoded = Base64.encode(authString.getBytes()); - base64encoded = AUTHORIZATION_BASIC_PREFIX+ base64encoded; + base64encoded = AUTHORIZATION_BASIC_PREFIX + base64encoded; HashMap authHeader = new HashMap<>(); authHeader.put("Authorization", base64encoded); @@ -83,4 +114,14 @@ public void responseContainsRefreshTokenAndTheUserWithId(String refreshToken, lo assertEquals(userId, actualUserId); assertEquals(refreshToken, actualRefreshToken); } + + @And("response contains valid accessToken for user {long} with a different value than {string}") + public void responseContainsValidAccessTokenForUserWithADifferentValueThan(int userId, String differentTokenValue) throws JsonProcessingException { + JsonNode rootNode = objectMapper.readTree(latestResponse.getBody()); + String actualTokenValue = rootNode.get("token").asText(); + long actualUserId = rootNode.get("userId").asLong(); + + assertEquals(userId, actualUserId); + assertNotEquals(differentTokenValue, actualTokenValue); + } } diff --git a/src/test/resources/UserAuthorization.feature b/src/test/resources/UserAuthorization.feature index 509b3e8b..1e5178cf 100644 --- a/src/test/resources/UserAuthorization.feature +++ b/src/test/resources/UserAuthorization.feature @@ -9,10 +9,10 @@ Background: Scenario: Successful login with username and password. When user requests login with username "user" and password "secure_password" - And response status code is 200 + Then response status code is 200 And response contains refreshToken "token" and the user with id 1234 -Scenario: Failed login with username and password. +Scenario: Failed login with wrong username or password. When user requests login with username "user" and password "wrong_password" Then response contains key "message" and value "User could not be authenticated. No User found for username or password." And response contains key "status" and value "denied" @@ -24,13 +24,21 @@ Scenario: Successful creation of new accessToken with refreshToken. And response contains valid accessToken for user 1234 And response status code is 200 -Scenario: Successful request of existing accessToken with refreshToken. +Scenario: Successful retrieval of existing accessToken with refreshToken. Given accessToken with value "6bb9cb4f-7b51-4c0a-8013-ed7a34e56282" exists for user 1234 When user requests accessToken with refreshToken "token" and userId 1234 Then response contains key "userId" and value "1234" And response contains valid accessToken for user 1234 And response status code is 200 +Scenario: Successful retrieval of freshly created accessToken with refreshToken + Given accessToken with value "6bb9cb4f-7b51-4c0a-8013-ed7a34e56282" exists for user 1234 and is valid until 0 + When user requests accessToken with refreshToken "token" and userId 1234 + Then response contains key "userId" and value "1234" + And response contains valid accessToken for user 1234 with a different value than "6bb9cb4f-7b51-4c0a-8013-ed7a34e56282" + And response status code is 200 + + Scenario: Failed retrieval of accessToken with wrong refreshToken. When user requests accessToken with refreshToken "not_the_token" and userId 1234 Then response contains key "message" and value "User with the id 1234 could not be authenticated." From c5ea7171f95151017cc07b79eb66b1d0261bf029 Mon Sep 17 00:00:00 2001 From: open-schnick Date: Wed, 4 Nov 2020 09:26:44 +0100 Subject: [PATCH 5/8] Fixed test. --- .../filefighter/rest/cucumber/UserAuthorizationSteps.java | 6 +++--- src/test/resources/SystemHealth.feature | 2 +- src/test/resources/UserAuthorization.feature | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java b/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java index b8a1f607..872b63a3 100644 --- a/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java +++ b/src/test/java/de/filefighter/rest/cucumber/UserAuthorizationSteps.java @@ -41,7 +41,7 @@ public void accessTokenWithValueExistsForUserAndIsValidUntil(String tokenValue, .userId(userId) .value(tokenValue) .validUntil(validUntil) - .validUntil(Instant.now().getEpochSecond()+ ACCESS_TOKEN_DURATION_IN_SECONDS).build()); + .build()); } @Given("accessToken with value {string} exists for user {long}") @@ -49,7 +49,7 @@ public void accessTokenWithValueExistsForUser(String tokenValue, long userId) { accessTokenRepository.save(AccessTokenEntity.builder() .userId(userId) .value(tokenValue) - .validUntil(Instant.now().getEpochSecond()+ ACCESS_TOKEN_DURATION_IN_SECONDS).build()); + .validUntil(Instant.now().getEpochSecond() + ACCESS_TOKEN_DURATION_IN_SECONDS).build()); } @When("user requests login with username {string} and password {string}") @@ -116,7 +116,7 @@ public void responseContainsRefreshTokenAndTheUserWithId(String refreshToken, lo } @And("response contains valid accessToken for user {long} with a different value than {string}") - public void responseContainsValidAccessTokenForUserWithADifferentValueThan(int userId, String differentTokenValue) throws JsonProcessingException { + public void responseContainsValidAccessTokenForUserWithADifferentValueThan(long userId, String differentTokenValue) throws JsonProcessingException { JsonNode rootNode = objectMapper.readTree(latestResponse.getBody()); String actualTokenValue = rootNode.get("token").asText(); long actualUserId = rootNode.get("userId").asLong(); diff --git a/src/test/resources/SystemHealth.feature b/src/test/resources/SystemHealth.feature index 9a0107b2..5018b16d 100644 --- a/src/test/resources/SystemHealth.feature +++ b/src/test/resources/SystemHealth.feature @@ -13,7 +13,7 @@ Scenario: SystemHealth is requested without users in db Scenario: SystemHealth is requested with users in db Given user 1234 exists And user 3214 exists - And the user waits for 1 second(s) + And the user waits for 2 second(s) When the systemHealth endpoint is requested Then response contains key "userCount" and value "2" And response contains key "uptimeInSeconds" and value of at least 1 diff --git a/src/test/resources/UserAuthorization.feature b/src/test/resources/UserAuthorization.feature index 1e5178cf..5afa3eaf 100644 --- a/src/test/resources/UserAuthorization.feature +++ b/src/test/resources/UserAuthorization.feature @@ -31,7 +31,8 @@ Scenario: Successful retrieval of existing accessToken with refreshToken. And response contains valid accessToken for user 1234 And response status code is 200 -Scenario: Successful retrieval of freshly created accessToken with refreshToken + # Better scenario description? +Scenario: Successful retrieval of overwritten accessToken with refreshToken Given accessToken with value "6bb9cb4f-7b51-4c0a-8013-ed7a34e56282" exists for user 1234 and is valid until 0 When user requests accessToken with refreshToken "token" and userId 1234 Then response contains key "userId" and value "1234" From 681f589493bc1284846c4696293a9cce20d5ddce Mon Sep 17 00:00:00 2001 From: open-schnick Date: Wed, 4 Nov 2020 11:30:20 +0100 Subject: [PATCH 6/8] Added UnitTests (1/2) - Some Cleanup --- .../rest/configuration/PrepareDataBase.java | 1 + .../data/dto/FileSystemItemUpdate.java | 2 +- .../filesystem/data/dto/FolderContents.java | 2 +- .../data/persistance/FileSystemEntity.java | 2 +- .../business/SystemHealthBusinessService.java | 2 +- .../rest/domain/health/data/SystemHealth.java | 2 +- .../business/AccessTokenBusinessService.java | 4 +- .../token/business/AccessTokenDtoService.java | 2 +- .../domain/user/business/UserDtoService.java | 2 +- .../rest/domain/user/data/dto/User.java | 2 +- .../user/data/dto/UserRegisterForm.java | 2 +- .../rest/cucumber/CommonCucumberSteps.java | 12 +- .../rest/domain/common/UtilsUnitTest.java | 37 +++++ .../FileSystemRestControllerUnitTest.java | 6 +- .../SystemHealthRestControllerUnitTest.java | 31 ----- .../rest/SystemHealthRestIntegrationTest.java | 73 ---------- .../rest/SystemHealthRestServiceUnitTest.java | 2 +- .../PermissionRestControllerUnitTest.java | 2 +- .../AccessTokenBusinessServiceUnitTest.java | 130 +++++++++++++++++- .../AccessTokenDtoServiceUnitTest.java | 46 ++++++- .../user/rest/UserRestControllerUnitTest.java | 12 +- 21 files changed, 239 insertions(+), 135 deletions(-) create mode 100644 src/test/java/de/filefighter/rest/domain/common/UtilsUnitTest.java delete mode 100644 src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestControllerUnitTest.java delete mode 100644 src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestIntegrationTest.java diff --git a/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java b/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java index 01b93576..d13eed14 100644 --- a/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java +++ b/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java @@ -25,6 +25,7 @@ public class PrepareDataBase { private static final Logger LOG = LoggerFactory.getLogger(PrepareDataBase.class); @Bean + @Profile("dev,prod") CommandLineRunner veryImportantFileFighterStartScript() { return args -> { System.out.println(); diff --git a/src/main/java/de/filefighter/rest/domain/filesystem/data/dto/FileSystemItemUpdate.java b/src/main/java/de/filefighter/rest/domain/filesystem/data/dto/FileSystemItemUpdate.java index 77bf6390..1e7a6d24 100644 --- a/src/main/java/de/filefighter/rest/domain/filesystem/data/dto/FileSystemItemUpdate.java +++ b/src/main/java/de/filefighter/rest/domain/filesystem/data/dto/FileSystemItemUpdate.java @@ -5,7 +5,7 @@ import lombok.Data; @Data -@Builder(builderMethodName = "create") +@Builder public class FileSystemItemUpdate { private String name; private FileSystemType type; diff --git a/src/main/java/de/filefighter/rest/domain/filesystem/data/dto/FolderContents.java b/src/main/java/de/filefighter/rest/domain/filesystem/data/dto/FolderContents.java index f144c5c2..153821da 100644 --- a/src/main/java/de/filefighter/rest/domain/filesystem/data/dto/FolderContents.java +++ b/src/main/java/de/filefighter/rest/domain/filesystem/data/dto/FolderContents.java @@ -4,7 +4,7 @@ import lombok.Getter; @Getter -@Builder(buildMethodName = "create") +@Builder public class FolderContents { private final Folder[] folders; private final File[] files; diff --git a/src/main/java/de/filefighter/rest/domain/filesystem/data/persistance/FileSystemEntity.java b/src/main/java/de/filefighter/rest/domain/filesystem/data/persistance/FileSystemEntity.java index 925edc29..febd46a2 100644 --- a/src/main/java/de/filefighter/rest/domain/filesystem/data/persistance/FileSystemEntity.java +++ b/src/main/java/de/filefighter/rest/domain/filesystem/data/persistance/FileSystemEntity.java @@ -7,7 +7,7 @@ @Data @Document(collection = "file") -@Builder(buildMethodName = "create") +@Builder public class FileSystemEntity { @MongoId private String _id; private long id; diff --git a/src/main/java/de/filefighter/rest/domain/health/business/SystemHealthBusinessService.java b/src/main/java/de/filefighter/rest/domain/health/business/SystemHealthBusinessService.java index 86721846..314bc04e 100644 --- a/src/main/java/de/filefighter/rest/domain/health/business/SystemHealthBusinessService.java +++ b/src/main/java/de/filefighter/rest/domain/health/business/SystemHealthBusinessService.java @@ -22,7 +22,7 @@ public SystemHealth getCurrentSystemHealthInfo(){ return SystemHealth.builder() .uptimeInSeconds(currentEpoch - serverStartedAt) .userCount(userBusinessService.getUserCount()) - .create(); + .build(); } public long getCurrentEpochSeconds(){ diff --git a/src/main/java/de/filefighter/rest/domain/health/data/SystemHealth.java b/src/main/java/de/filefighter/rest/domain/health/data/SystemHealth.java index ceff106c..e98ada97 100644 --- a/src/main/java/de/filefighter/rest/domain/health/data/SystemHealth.java +++ b/src/main/java/de/filefighter/rest/domain/health/data/SystemHealth.java @@ -8,7 +8,7 @@ */ @Getter -@Builder(buildMethodName = "create") +@Builder public class SystemHealth { private final long uptimeInSeconds; private final long userCount; diff --git a/src/main/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessService.java b/src/main/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessService.java index 60682f72..88135d5e 100644 --- a/src/main/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessService.java +++ b/src/main/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessService.java @@ -59,7 +59,7 @@ public AccessToken getValidAccessTokenForUser(User user) { public AccessToken findAccessTokenByValueAndUserId(String accessTokenValue, long userId) { if (!stringIsValid(accessTokenValue)) - throw new IllegalArgumentException("AccessTokenValue was not valid."); + throw new IllegalArgumentException("Value of AccessToken was not valid."); AccessTokenEntity accessTokenEntity = accessTokenRepository.findByUserIdAndValue(userId, accessTokenValue); if (null == accessTokenEntity) @@ -73,7 +73,7 @@ public String generateRandomTokenValue() { } public String checkBearerHeader(String accessTokenValue) { - if (!accessTokenValue.matches("^" + AUTHORIZATION_BEARER_PREFIX + "(.*)$")) + if (!accessTokenValue.matches("^" + AUTHORIZATION_BEARER_PREFIX + "[^\\s](.*)$")) throw new UserNotAuthenticatedException("Header does not contain '" + AUTHORIZATION_BEARER_PREFIX + "', or format is invalid."); return accessTokenValue.split(AUTHORIZATION_BEARER_PREFIX)[1]; } diff --git a/src/main/java/de/filefighter/rest/domain/token/business/AccessTokenDtoService.java b/src/main/java/de/filefighter/rest/domain/token/business/AccessTokenDtoService.java index c7e2e4a5..f889297d 100644 --- a/src/main/java/de/filefighter/rest/domain/token/business/AccessTokenDtoService.java +++ b/src/main/java/de/filefighter/rest/domain/token/business/AccessTokenDtoService.java @@ -28,7 +28,7 @@ public AccessToken createDto(AccessTokenEntity entity) { @Override public AccessTokenEntity findEntity(AccessToken dto) { - AccessTokenEntity accessTokenEntity = accessTokenRepository.findByValue(dto.getToken()); + AccessTokenEntity accessTokenEntity = accessTokenRepository.findByUserIdAndValue(dto.getUserId(), dto.getToken()); if (null == accessTokenEntity) throw new AccessTokenNotFoundException("AccessTokenEntity does not exist for AccessToken: "+ dto); diff --git a/src/main/java/de/filefighter/rest/domain/user/business/UserDtoService.java b/src/main/java/de/filefighter/rest/domain/user/business/UserDtoService.java index d6d2631d..ccbebf39 100644 --- a/src/main/java/de/filefighter/rest/domain/user/business/UserDtoService.java +++ b/src/main/java/de/filefighter/rest/domain/user/business/UserDtoService.java @@ -26,7 +26,7 @@ public User createDto(UserEntity entity) { .id(entity.getUserId()) .username(entity.getUsername()) .groups(groupRepository.getRolesByIds(entity.getGroupIds())) - .create(); + .build(); } @Override diff --git a/src/main/java/de/filefighter/rest/domain/user/data/dto/User.java b/src/main/java/de/filefighter/rest/domain/user/data/dto/User.java index 6d5a726a..d70e6bb0 100644 --- a/src/main/java/de/filefighter/rest/domain/user/data/dto/User.java +++ b/src/main/java/de/filefighter/rest/domain/user/data/dto/User.java @@ -4,8 +4,8 @@ import lombok.Data; -@Builder(builderClassName = "UserBuilder", buildMethodName = "create") @Data +@Builder public class User { private long id; private String username; diff --git a/src/main/java/de/filefighter/rest/domain/user/data/dto/UserRegisterForm.java b/src/main/java/de/filefighter/rest/domain/user/data/dto/UserRegisterForm.java index e36029c6..af076c41 100644 --- a/src/main/java/de/filefighter/rest/domain/user/data/dto/UserRegisterForm.java +++ b/src/main/java/de/filefighter/rest/domain/user/data/dto/UserRegisterForm.java @@ -4,7 +4,7 @@ import lombok.Data; @Data -@Builder(buildMethodName = "create", builderClassName = "UserRegistrationFormBuilder") +@Builder public class UserRegisterForm { private String username; private String password; diff --git a/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java b/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java index 872291b6..689e799b 100644 --- a/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java +++ b/src/test/java/de/filefighter/rest/cucumber/CommonCucumberSteps.java @@ -69,15 +69,15 @@ public void fileOrFolderExistsWithIdAndPath(String fileOrFolder, long fsItemId, System.out.println(Arrays.toString(names)); - // create root dir. + // build root dir. fileSystemRepository.save(FileSystemEntity .builder() .isFile(false) .path(completeFilePath.toString()) - .create()); + .build()); - // create all files and folders. + // build all files and folders. for (int i = 0; i < names.length; i++) { if (!names[i].isEmpty() && !names[i].isBlank()) { boolean isLastOne = i == names.length - 1; @@ -88,7 +88,7 @@ public void fileOrFolderExistsWithIdAndPath(String fileOrFolder, long fsItemId, .builder() .isFile(false) .path(completeFilePath.toString()) - .create()); + .build()); System.out.println("folder: "+completeFilePath.toString()); }else{ System.out.println("last one: "+names[i]); @@ -97,7 +97,7 @@ public void fileOrFolderExistsWithIdAndPath(String fileOrFolder, long fsItemId, .builder() .isFile(true) .id(fsItemId) - .create()); + .build()); } else if (fileOrFolder.equals("folder")) { completeFilePath.append(names[i]).append("/"); fileSystemRepository.save(FileSystemEntity @@ -105,7 +105,7 @@ public void fileOrFolderExistsWithIdAndPath(String fileOrFolder, long fsItemId, .isFile(false) .id(fsItemId) .path(completeFilePath.toString()) - .create()); + .build()); } else { throw new IllegalArgumentException("Found not valid string for FileOrFolder in Steps file."); } diff --git a/src/test/java/de/filefighter/rest/domain/common/UtilsUnitTest.java b/src/test/java/de/filefighter/rest/domain/common/UtilsUnitTest.java new file mode 100644 index 00000000..0f192d85 --- /dev/null +++ b/src/test/java/de/filefighter/rest/domain/common/UtilsUnitTest.java @@ -0,0 +1,37 @@ +package de.filefighter.rest.domain.common; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +@SuppressWarnings(value = "ConstantConditions") +class UtilsUnitTest { + + @Test + void stringIsValid() { + String string = "string"; + boolean actual = Utils.stringIsValid(string); + assertTrue(actual); + } + + @Test + void stringIsNull() { + String string = null; + boolean actual = Utils.stringIsValid(string); + assertFalse(actual); + } + + @Test + void stringIsEmpty() { + String string = ""; + boolean actual = Utils.stringIsValid(string); + assertFalse(actual); + } + + @Test + void stringIsBlank() { + String string = ""; + boolean actual = Utils.stringIsValid(string); + assertFalse(actual); + } +} \ No newline at end of file diff --git a/src/test/java/de/filefighter/rest/domain/filesystem/rest/FileSystemRestControllerUnitTest.java b/src/test/java/de/filefighter/rest/domain/filesystem/rest/FileSystemRestControllerUnitTest.java index 954b9a27..28452505 100644 --- a/src/test/java/de/filefighter/rest/domain/filesystem/rest/FileSystemRestControllerUnitTest.java +++ b/src/test/java/de/filefighter/rest/domain/filesystem/rest/FileSystemRestControllerUnitTest.java @@ -28,7 +28,7 @@ void getContentsOfFolder() { File dummyFile = new File(); ResponseEntity expectedModel = new ResponseEntity<>(FolderContents.builder() .files(new File[]{dummyFile}) - .folders(new Folder[]{dummyFolder}).create(), OK); + .folders(new Folder[]{dummyFolder}).build(), OK); String path= "/root/data.txt"; String token = "token"; @@ -72,7 +72,7 @@ void uploadFileOrFolder() { File file = new File(); ResponseEntity expectedModel = new ResponseEntity<>(file, OK); - FileSystemItemUpdate fileSystemItemUpdate = FileSystemItemUpdate.create().name("ugabuga").build(); + FileSystemItemUpdate fileSystemItemUpdate = FileSystemItemUpdate.builder().name("ugabuga").build(); String token = "token"; when(fileSystemRestServiceMock.uploadFileSystemItemWithAccessToken(fileSystemItemUpdate, token)).thenReturn(expectedModel); @@ -87,7 +87,7 @@ void updateExistingFileOrFolder() { ResponseEntity expectedModel = new ResponseEntity<>(file, OK); long id = 420L; - FileSystemItemUpdate fileSystemItemUpdate = FileSystemItemUpdate.create().name("ugabuga").build(); + FileSystemItemUpdate fileSystemItemUpdate = FileSystemItemUpdate.builder().name("ugabuga").build(); String token = "token"; when(fileSystemRestServiceMock.updatedFileSystemItemWithIdAndAccessToken(id, fileSystemItemUpdate, token)).thenReturn(expectedModel); diff --git a/src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestControllerUnitTest.java b/src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestControllerUnitTest.java deleted file mode 100644 index 187a9086..00000000 --- a/src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestControllerUnitTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package de.filefighter.rest.domain.health.rest; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; - -import static org.mockito.Mockito.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -class SystemHealthRestControllerUnitTest { - - private final SystemHealthRestService systemHealthRestServiceMock = mock(SystemHealthRestService.class); - private MockMvc mockMvc; - - @BeforeEach - public void setUp() { - SystemHealthRestController systemHealthRestController = new SystemHealthRestController(systemHealthRestServiceMock); - mockMvc = MockMvcBuilders.standaloneSetup(systemHealthRestController).build(); - } - - @Test - void getSystemHealthInfo() throws Exception { - mockMvc.perform(get("/health")) - .andExpect(status().isOk()) - .andReturn(); - - verify(systemHealthRestServiceMock, times(1)).getSystemHealth(); - } -} \ No newline at end of file diff --git a/src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestIntegrationTest.java b/src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestIntegrationTest.java deleted file mode 100644 index 0b13019c..00000000 --- a/src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestIntegrationTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package de.filefighter.rest.domain.health.rest; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import de.filefighter.rest.domain.filesystem.data.persistance.FileSystemRepository; -import de.filefighter.rest.domain.token.data.persistance.AccessTokenRepository; -import de.filefighter.rest.domain.user.data.persistance.UserEntity; -import de.filefighter.rest.domain.user.data.persistance.UserRepository; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -public class SystemHealthRestIntegrationTest { - - @LocalServerPort - private int port; - - private final Logger LOG = LoggerFactory.getLogger(SystemHealthRestIntegrationTest.class); - private final ObjectMapper objectMapper; - private final TestRestTemplate restTemplate; - private final UserRepository userRepository; - private final FileSystemRepository fileSystemRepository; - private final AccessTokenRepository accessTokenRepository; - - @Autowired - public SystemHealthRestIntegrationTest(TestRestTemplate restTemplate, UserRepository userRepository, FileSystemRepository fileSystemRepository, AccessTokenRepository accessTokenRepository) { - this.objectMapper = new ObjectMapper(); - this.restTemplate = restTemplate; - this.userRepository = userRepository; - this.fileSystemRepository = fileSystemRepository; - this.accessTokenRepository = accessTokenRepository; - } - - @BeforeEach - public void cleanDbs() { - LOG.info("Cleaning Databases."); - userRepository.deleteAll(); - fileSystemRepository.deleteAll(); - accessTokenRepository.deleteAll(); - } - - @Test - public void healthCheckShouldContainVariablesAndCorrectValues() throws JsonProcessingException { - LOG.info("Preloading default admin user: " + userRepository.save(UserEntity - .builder() - .userId(0L) - .username("admin") - .password("admin") - .refreshToken("refreshToken1234") - .groupIds(new long[]{0, 1}) - .build())); - String jsonString = this.restTemplate.getForObject("http://localhost:" + port + "/health", String.class); - - // Note when a key does not exist, a NullPointerException will be thrown. - JsonNode root = objectMapper.readTree(jsonString); - String uptime = root.get("uptimeInSeconds").asText(); - String userCount = root.get("userCount").asText(); - - assertTrue(Integer.parseInt(uptime) > 0); - assertEquals(1, Integer.parseInt(userCount)); - } -} \ No newline at end of file diff --git a/src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestServiceUnitTest.java b/src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestServiceUnitTest.java index ca18b206..8b8cc210 100644 --- a/src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestServiceUnitTest.java +++ b/src/test/java/de/filefighter/rest/domain/health/rest/SystemHealthRestServiceUnitTest.java @@ -23,7 +23,7 @@ public void setUp() { @Test void getSystemHealth() { - SystemHealth dummyHealth = SystemHealth.builder().uptimeInSeconds(420).create(); + SystemHealth dummyHealth = SystemHealth.builder().uptimeInSeconds(420).build(); ResponseEntity expectedModel = new ResponseEntity<>(dummyHealth, HttpStatus.OK); when(systemHealthBusinessServiceMock.getCurrentSystemHealthInfo()).thenReturn(dummyHealth); diff --git a/src/test/java/de/filefighter/rest/domain/permission/rest/PermissionRestControllerUnitTest.java b/src/test/java/de/filefighter/rest/domain/permission/rest/PermissionRestControllerUnitTest.java index d35d0fea..54474ebf 100644 --- a/src/test/java/de/filefighter/rest/domain/permission/rest/PermissionRestControllerUnitTest.java +++ b/src/test/java/de/filefighter/rest/domain/permission/rest/PermissionRestControllerUnitTest.java @@ -30,7 +30,7 @@ void setUp() { void getPermissionSetForFileOrFolder() { String token = "token"; long id = 420; - User dummyUser = User.builder().create(); + User dummyUser = User.builder().build(); ResponseEntity expected = new ResponseEntity<>(new PermissionSet(null, null, new User[]{dummyUser}, null), OK); when(permissionRestService.getPermissionSetByIdAndToken(id, token)).thenReturn(expected); diff --git a/src/test/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessServiceUnitTest.java b/src/test/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessServiceUnitTest.java index 36cbc258..997b9b0b 100644 --- a/src/test/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessServiceUnitTest.java +++ b/src/test/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessServiceUnitTest.java @@ -1,25 +1,151 @@ package de.filefighter.rest.domain.token.business; +import de.filefighter.rest.domain.token.data.dto.AccessToken; +import de.filefighter.rest.domain.token.data.persistance.AccessTokenEntity; +import de.filefighter.rest.domain.token.data.persistance.AccessTokenRepository; +import de.filefighter.rest.domain.user.data.dto.User; +import de.filefighter.rest.domain.user.exceptions.UserNotAuthenticatedException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.time.Instant; + import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; class AccessTokenBusinessServiceUnitTest { + private final AccessTokenRepository accessTokenRepositoryMock = mock(AccessTokenRepository.class); + private final AccessTokenDtoService accessTokenDtoService = mock(AccessTokenDtoService.class); + private AccessTokenBusinessService accessTokenBusinessService; + @BeforeEach void setUp() { + accessTokenBusinessService = new AccessTokenBusinessService(accessTokenRepositoryMock, accessTokenDtoService); + } + + @Test + void getValidAccessTokenForUserWhenNoTokenExists() { + long dummyId = 1234; + User dummyUser = User.builder().id(dummyId).build(); + AccessToken dummyAccessToken = AccessToken.builder().userId(dummyId).build(); + AccessTokenEntity dummyAccessTokenEntity = AccessTokenEntity.builder().userId(dummyId).build(); + + when(accessTokenRepositoryMock.findByUserId(dummyId)).thenReturn(null); + when(accessTokenRepositoryMock.save(any())).thenReturn(dummyAccessTokenEntity); + when(accessTokenDtoService.createDto(dummyAccessTokenEntity)).thenReturn(dummyAccessToken); + + AccessToken accessToken = accessTokenBusinessService.getValidAccessTokenForUser(dummyUser); + assertEquals(dummyAccessToken, accessToken); + } + + @Test + void getValidAccessTokenForUserWhenTokenExists() { + long dummyId = 1234; + User dummyUser = User.builder().id(dummyId).build(); + AccessToken dummyAccessToken = AccessToken.builder().userId(dummyId).build(); + AccessTokenEntity dummyAccessTokenEntity = AccessTokenEntity + .builder() + .userId(dummyId) + .validUntil(Instant.now().getEpochSecond() + AccessTokenBusinessService.ACCESS_TOKEN_DURATION_IN_SECONDS + 100) + .build(); + + when(accessTokenRepositoryMock.findByUserId(dummyId)).thenReturn(dummyAccessTokenEntity); + when(accessTokenDtoService.createDto(dummyAccessTokenEntity)).thenReturn(dummyAccessToken); + + AccessToken accessToken = accessTokenBusinessService.getValidAccessTokenForUser(dummyUser); + assertEquals(dummyAccessToken, accessToken); + } + + @Test + void getValidAccessTokenForUserWhenTokenExistsButIsInvalid() { + long dummyId = 1234; + User dummyUser = User.builder().id(dummyId).build(); + AccessToken dummyAccessToken = AccessToken.builder().userId(dummyId).build(); + AccessTokenEntity dummyAccessTokenEntity = AccessTokenEntity + .builder() + .userId(dummyId) + .validUntil(Instant.now().getEpochSecond()) + .build(); + + when(accessTokenRepositoryMock.findByUserId(dummyId)).thenReturn(dummyAccessTokenEntity); + when(accessTokenRepositoryMock.save(any())).thenReturn(dummyAccessTokenEntity); + when(accessTokenDtoService.createDto(dummyAccessTokenEntity)).thenReturn(dummyAccessToken); + + AccessToken accessToken = accessTokenBusinessService.getValidAccessTokenForUser(dummyUser); + + assertEquals(dummyAccessToken, accessToken); + verify(accessTokenRepositoryMock, times(0)).save(dummyAccessTokenEntity); // the newly saved token is different. } @Test - void getValidAccessTokenForUser() { + void findAccessTokenByValueAndUserIdWithInvalidToken() { + String tokenValue = ""; + long userId = 1234; + + assertThrows(IllegalArgumentException.class, () -> + accessTokenBusinessService.findAccessTokenByValueAndUserId(tokenValue, userId) + ); } @Test - void getAccessTokenByValueAndId() { + void findAccessTokenByValueAndUserIdWithTokenNotFound() { + String tokenValue = "value"; + long userId = 1234; + + when(accessTokenRepositoryMock.findByUserIdAndValue(userId, tokenValue)).thenReturn(null); + + assertThrows(UserNotAuthenticatedException.class, () -> + accessTokenBusinessService.findAccessTokenByValueAndUserId(tokenValue, userId) + ); + } + + @Test + void findAccessTokenByValueAndUserIdWithFoundToken() { + String tokenValue = "validToken"; + long userId = 1234; + AccessToken dummyAccessToken = AccessToken.builder().build(); + AccessTokenEntity dummyAccessTokenEntity = AccessTokenEntity.builder().build(); + + when(accessTokenRepositoryMock.findByUserIdAndValue(userId, tokenValue)).thenReturn(dummyAccessTokenEntity); + when(accessTokenDtoService.createDto(dummyAccessTokenEntity)).thenReturn(dummyAccessToken); + + AccessToken actual = accessTokenBusinessService.findAccessTokenByValueAndUserId(tokenValue, userId); + + assertEquals(dummyAccessToken, actual); } @Test void generateRandomTokenValue() { + String generatedToken = accessTokenBusinessService.generateRandomTokenValue(); + assertEquals(36, generatedToken.length()); + } + + @Test + void checkBearerHeaderWithWrongHeader(){ + String header0 = "wrongHeader"; + String header1 = ""; + String header2 = "Bearer: "; + + assertThrows(UserNotAuthenticatedException.class, () -> + accessTokenBusinessService.checkBearerHeader(header0) + ); + assertThrows(UserNotAuthenticatedException.class, () -> + accessTokenBusinessService.checkBearerHeader(header1) + ); + assertThrows(UserNotAuthenticatedException.class, () -> + accessTokenBusinessService.checkBearerHeader(header2) + ); + } + + @Test + void checkBearerHeaderWithCorrectHeader(){ + String header = "Bearer: something"; + String expected = "something"; + + String actual = accessTokenBusinessService.checkBearerHeader(header); + + assertEquals(expected, actual); } } \ No newline at end of file diff --git a/src/test/java/de/filefighter/rest/domain/token/business/AccessTokenDtoServiceUnitTest.java b/src/test/java/de/filefighter/rest/domain/token/business/AccessTokenDtoServiceUnitTest.java index 12e5aef9..705d0e76 100644 --- a/src/test/java/de/filefighter/rest/domain/token/business/AccessTokenDtoServiceUnitTest.java +++ b/src/test/java/de/filefighter/rest/domain/token/business/AccessTokenDtoServiceUnitTest.java @@ -1,21 +1,65 @@ package de.filefighter.rest.domain.token.business; +import de.filefighter.rest.domain.token.data.dto.AccessToken; +import de.filefighter.rest.domain.token.data.persistance.AccessTokenEntity; +import de.filefighter.rest.domain.token.data.persistance.AccessTokenRepository; +import de.filefighter.rest.domain.token.exceptions.AccessTokenNotFoundException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; class AccessTokenDtoServiceUnitTest { + private final AccessTokenRepository accessTokenRepository = mock(AccessTokenRepository.class); + private AccessTokenDtoService accessTokenDtoService; + @BeforeEach void setUp() { + accessTokenDtoService = new AccessTokenDtoService(accessTokenRepository); } @Test void createDto() { + AccessTokenEntity dummyEntity = AccessTokenEntity.builder() + .validUntil(420) + .userId(240) + .value("token") + .build(); + + AccessToken actual = accessTokenDtoService.createDto(dummyEntity); + + assertEquals(dummyEntity.getUserId(), actual.getUserId()); + assertEquals(dummyEntity.getValidUntil(), actual.getValidUntil()); + assertEquals(dummyEntity.getValue(), actual.getToken()); } @Test - void findEntity() { + void findEntityNotSuccessfully() { + long userId = 420; + String token = "token"; + AccessToken dummyToken = AccessToken.builder().userId(userId).token(token).build(); + + when(accessTokenRepository.findByUserIdAndValue(userId, token)).thenReturn(null); + + assertThrows(AccessTokenNotFoundException.class, () -> + accessTokenDtoService.findEntity(dummyToken) + ); + } + + @Test + void findEntitySuccessfully() { + long userId = 420; + String token = "token"; + AccessToken dummyToken = AccessToken.builder().userId(userId).token(token).build(); + AccessTokenEntity expected = AccessTokenEntity.builder().userId(userId).value(token).build(); + + when(accessTokenRepository.findByUserIdAndValue(userId, token)).thenReturn(expected); + + AccessTokenEntity actual = accessTokenDtoService.findEntity(dummyToken); + + assertEquals(expected, actual); } } \ No newline at end of file diff --git a/src/test/java/de/filefighter/rest/domain/user/rest/UserRestControllerUnitTest.java b/src/test/java/de/filefighter/rest/domain/user/rest/UserRestControllerUnitTest.java index fe3692a2..42a06bd2 100644 --- a/src/test/java/de/filefighter/rest/domain/user/rest/UserRestControllerUnitTest.java +++ b/src/test/java/de/filefighter/rest/domain/user/rest/UserRestControllerUnitTest.java @@ -26,7 +26,7 @@ void setUp() { @Test void registerNewUser() { - User user = User.builder().id(420).groups(null).username("kevin").create(); + User user = User.builder().id(420).groups(null).username("kevin").build(); ResponseEntity expectedUser = new ResponseEntity<>(user, OK); when(userRestServiceMock.registerNewUserWithAccessToken(any(), any())).thenReturn(expectedUser); @@ -38,7 +38,7 @@ void registerNewUser() { @Test void loginUserWithUsernameAndPassword() { - User user = User.builder().id(420).groups(null).username("kevin").create(); + User user = User.builder().id(420).groups(null).username("kevin").build(); RefreshToken refreshToken = RefreshToken.builder().refreshToken("token").user(user).build(); ResponseEntity expectedRefreshToken = new ResponseEntity<>(refreshToken, OK); @@ -62,7 +62,7 @@ void getAccessTokenAndUserInfoByRefreshTokenAndUserId() { @Test void getUserInfoWithAccessToken() { - User user = User.builder().id(420).groups(null).username("kevin").create(); + User user = User.builder().id(420).groups(null).username("kevin").build(); ResponseEntity expectedUser = new ResponseEntity<>(user, OK); when(userRestServiceMock.getUserByAccessTokenAndUserId("token", 420)).thenReturn(expectedUser); @@ -73,9 +73,9 @@ void getUserInfoWithAccessToken() { @Test void updateUserWithAccessToken() { - User user = User.builder().id(420).groups(null).username("kevin").create(); + User user = User.builder().id(420).groups(null).username("kevin").build(); ResponseEntity expectedUser = new ResponseEntity<>(user, OK); - UserRegisterForm userRegisterForm = UserRegisterForm.builder().create(); + UserRegisterForm userRegisterForm = UserRegisterForm.builder().build(); when(userRestServiceMock.updateUserByAccessTokenAndUserId(userRegisterForm, "token", 420)).thenReturn(expectedUser); ResponseEntity actualUser = userRestController.updateUserWithAccessToken(420,"token", userRegisterForm); @@ -85,7 +85,7 @@ void updateUserWithAccessToken() { @Test void findUserByUsername(){ - User user = User.builder().id(420).groups(null).username("kevin").create(); + User user = User.builder().id(420).groups(null).username("kevin").build(); ResponseEntity expectedUser = new ResponseEntity<>(user, OK); String username="kevin"; From abd9efd45f8af8ac03e73dca8de9f59a2e923c32 Mon Sep 17 00:00:00 2001 From: open-schnick Date: Thu, 5 Nov 2020 09:55:03 +0100 Subject: [PATCH 7/8] Added UnitTests (2/2) - Cleanup, fixes --- .../rest/configuration/PrepareDataBase.java | 10 +- .../user/business/UserBusinessService.java | 31 ++-- .../business/UserBusinessServiceUnitTest.java | 166 +++++++++++++++++- .../user/business/UserDtoServiceUnitTest.java | 48 ++++- .../user/rest/UserRestServiceUnitTest.java | 37 ---- src/test/resources/SystemHealth.feature | 1 + src/test/resources/UserAuthorization.feature | 8 +- 7 files changed, 232 insertions(+), 69 deletions(-) delete mode 100644 src/test/java/de/filefighter/rest/domain/user/rest/UserRestServiceUnitTest.java diff --git a/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java b/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java index d13eed14..44529719 100644 --- a/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java +++ b/src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java @@ -25,7 +25,7 @@ public class PrepareDataBase { private static final Logger LOG = LoggerFactory.getLogger(PrepareDataBase.class); @Bean - @Profile("dev,prod") + @Profile({"dev", "prod"}) CommandLineRunner veryImportantFileFighterStartScript() { return args -> { System.out.println(); @@ -37,11 +37,11 @@ CommandLineRunner veryImportantFileFighterStartScript() { System.out.println(" | _| | | | | | __/ | _| | | | (_| | | | | | | |_ | __/ | | "); System.out.println(" |_| |_| |_| \\___| |_| |_| \\__, | |_| |_| \\__| \\___| |_| "); System.out.println(" |___/ "); - System.out.println(" Version 0.2 Last updated at 03.11.20 "); - System.out.println(" Developed by Gimleux, Valentin, Open-Schnick. "); - System.out.println(" Development Blog: https://filefighter.github.io "); + System.out.println(" Version 0.2 Last updated at 03.11.20 "); + System.out.println(" Developed by Gimleux, Valentin, Open-Schnick. "); + System.out.println(" Development Blog: https://filefighter.github.io "); System.out.println(" The code can be found at: https://www.github.com/filefighter "); - System.out.println(" Running on http://localhost:"+serverPort); + System.out.println(" Running on http://localhost:" + serverPort); System.out.println(); System.out.println("-------------------------------< REST API >-------------------------------"); System.out.println(); diff --git a/src/main/java/de/filefighter/rest/domain/user/business/UserBusinessService.java b/src/main/java/de/filefighter/rest/domain/user/business/UserBusinessService.java index 37f5a9e3..538f84c0 100644 --- a/src/main/java/de/filefighter/rest/domain/user/business/UserBusinessService.java +++ b/src/main/java/de/filefighter/rest/domain/user/business/UserBusinessService.java @@ -7,6 +7,8 @@ import de.filefighter.rest.domain.user.data.persistance.UserRepository; import de.filefighter.rest.domain.user.exceptions.UserNotAuthenticatedException; import de.filefighter.rest.domain.user.exceptions.UserNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.io.UnsupportedEncodingException; @@ -24,6 +26,8 @@ public class UserBusinessService { private final UserRepository userRepository; private final UserDtoService userDtoService; + private static final Logger LOG = LoggerFactory.getLogger(UserBusinessService.class); + public UserBusinessService(UserRepository userRepository, UserDtoService userDtoService) { this.userRepository = userRepository; this.userDtoService = userDtoService; @@ -33,22 +37,23 @@ public long getUserCount() { return userRepository.count(); } - public User getUserByUsernameAndPassword(String base64encodedUserAndPassword) { - if (!stringIsValid(base64encodedUserAndPassword)) + public User getUserByUsernameAndPassword(String base64encodedUserAndPasswordWithHeaderPrefix) { + if (!stringIsValid(base64encodedUserAndPasswordWithHeaderPrefix)) throw new UserNotAuthenticatedException("Header was empty."); //TODO: maybe filter unsupported characters? - if (!base64encodedUserAndPassword.matches("^"+AUTHORIZATION_BASIC_PREFIX+"(.*)$")) - throw new UserNotAuthenticatedException("Header does not contain '"+AUTHORIZATION_BASIC_PREFIX+"', or format is invalid."); + if (!base64encodedUserAndPasswordWithHeaderPrefix.matches("^" + AUTHORIZATION_BASIC_PREFIX + "[^\\s](.*)$")) + throw new UserNotAuthenticatedException("Header does not contain '" + AUTHORIZATION_BASIC_PREFIX + "', or format is invalid."); - String[] split = base64encodedUserAndPassword.split(AUTHORIZATION_BASIC_PREFIX); + String[] split = base64encodedUserAndPasswordWithHeaderPrefix.split(AUTHORIZATION_BASIC_PREFIX); - base64encodedUserAndPassword = split[1]; + base64encodedUserAndPasswordWithHeaderPrefix = split[1]; String decodedUsernameUndPassword; try { - byte[] decodedValue = Base64.getDecoder().decode(base64encodedUserAndPassword); + byte[] decodedValue = Base64.getDecoder().decode(base64encodedUserAndPasswordWithHeaderPrefix); decodedUsernameUndPassword = new String(decodedValue, StandardCharsets.UTF_8.toString()); - } catch (UnsupportedEncodingException ex) { + } catch (UnsupportedEncodingException | IllegalArgumentException ex) { + LOG.warn("Found UnsupportedEncodingException in {}", base64encodedUserAndPasswordWithHeaderPrefix); throw new RuntimeException(ex); } @@ -62,7 +67,7 @@ public User getUserByUsernameAndPassword(String base64encodedUserAndPassword) { UserEntity userEntity = userRepository.findByUsernameAndPassword(username, password); if (null == userEntity) - throw new UserNotAuthenticatedException("No User found for username or password."); + throw new UserNotFoundException("No User found with this username and password."); return userDtoService.createDto(userEntity); } @@ -74,7 +79,7 @@ public RefreshToken getRefreshTokenForUser(User user) { String refreshTokenValue = userEntity.getRefreshToken(); - if(!stringIsValid(refreshTokenValue)) + if (!stringIsValid(refreshTokenValue)) throw new IllegalStateException("RefreshToken was empty in db."); return RefreshToken @@ -90,18 +95,18 @@ public User getUserByRefreshTokenAndUserId(String refreshToken, long userId) { UserEntity userEntity = userRepository.findByRefreshTokenAndUserId(refreshToken, userId); if (null == userEntity) - throw new UserNotAuthenticatedException(userId); + throw new UserNotFoundException(userId); return userDtoService.createDto(userEntity); } public User getUserByAccessTokenAndUserId(AccessToken accessToken, long userId) { - if(accessToken.getUserId() != userId) + if (accessToken.getUserId() != userId) throw new UserNotAuthenticatedException(userId); UserEntity userEntity = userRepository.findByUserId(userId); if (null == userEntity) - throw new UserNotFoundException(); + throw new UserNotFoundException(userId); return userDtoService.createDto(userEntity); } diff --git a/src/test/java/de/filefighter/rest/domain/user/business/UserBusinessServiceUnitTest.java b/src/test/java/de/filefighter/rest/domain/user/business/UserBusinessServiceUnitTest.java index 778382e4..857a99b3 100644 --- a/src/test/java/de/filefighter/rest/domain/user/business/UserBusinessServiceUnitTest.java +++ b/src/test/java/de/filefighter/rest/domain/user/business/UserBusinessServiceUnitTest.java @@ -1,9 +1,16 @@ package de.filefighter.rest.domain.user.business; +import de.filefighter.rest.domain.token.data.dto.AccessToken; +import de.filefighter.rest.domain.token.data.dto.RefreshToken; +import de.filefighter.rest.domain.user.data.dto.User; +import de.filefighter.rest.domain.user.data.persistance.UserEntity; import de.filefighter.rest.domain.user.data.persistance.UserRepository; +import de.filefighter.rest.domain.user.exceptions.UserNotAuthenticatedException; +import de.filefighter.rest.domain.user.exceptions.UserNotFoundException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BASIC_PREFIX; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -21,32 +28,173 @@ void setUp() { @Test void getUserCount() { - long expectedCount = 420; + long count = 20; - when(userRepositoryMock.count()).thenReturn(expectedCount); + when(userRepositoryMock.count()).thenReturn(count); - long actualCount = userBusinessService.getUserCount(); + long actual = userBusinessService.getUserCount(); - assertEquals(expectedCount, actualCount); + assertEquals(count, actual); } @Test - void getUserByUsernameAndPassword() { + void getUserByUsernameAndPasswordThrowsErrors() { + String notValid = ""; + String validButDoesntMatch = "something"; + String matchesButIsNotSupportedEncoding = AUTHORIZATION_BASIC_PREFIX + "���"; + String withoutFormalRequirements = AUTHORIZATION_BASIC_PREFIX + "dWdhYnVnYXBhc3N3b3Jk"; //ugabugapassword + String userNotFound = AUTHORIZATION_BASIC_PREFIX + "dXNlcjpwYXNzd29yZA=="; // user:password + + assertThrows(UserNotAuthenticatedException.class, () -> + userBusinessService.getUserByUsernameAndPassword(notValid) + ); + assertThrows(UserNotAuthenticatedException.class, () -> + userBusinessService.getUserByUsernameAndPassword(validButDoesntMatch) + ); + assertThrows(RuntimeException.class, () -> + userBusinessService.getUserByUsernameAndPassword(matchesButIsNotSupportedEncoding) + ); + assertThrows(UserNotAuthenticatedException.class, () -> + userBusinessService.getUserByUsernameAndPassword(withoutFormalRequirements) + ); + + when(userRepositoryMock.findByUsernameAndPassword("user", "password")).thenReturn(null); + assertThrows(UserNotFoundException.class, () -> + userBusinessService.getUserByUsernameAndPassword(userNotFound) + ); + } + + @Test + void getUserByUsernameAndPasswordWorksCorrectly() { + String header = AUTHORIZATION_BASIC_PREFIX + "dXNlcjpwYXNzd29yZA=="; // user:password + User dummyUser = User.builder().build(); + UserEntity dummyEntity = UserEntity.builder().build(); + + when(userRepositoryMock.findByUsernameAndPassword("user", "password")).thenReturn(dummyEntity); + when(userDtoServiceMock.createDto(dummyEntity)).thenReturn(dummyUser); + + User actual = userBusinessService.getUserByUsernameAndPassword(header); + assertEquals(dummyUser, actual); + } + + @Test + void getRefreshTokenForUserWithoutUser() { + String invalidString = ""; + long userId = 420; + String username = "someString"; + + User dummyUser = User.builder().id(userId).username(username).build(); + + when(userRepositoryMock.findByUserIdAndUsername(userId, username)).thenReturn(null); + + assertThrows(UserNotFoundException.class, () -> + userBusinessService.getRefreshTokenForUser(dummyUser) + ); } @Test - void getRefreshTokenForUser() { + void getRefreshTokenForUserWithInvalidString() { + String invalidString = ""; + long userId = 420; + String username = "someString"; + + User dummyUser = User.builder().id(userId).username(username).build(); + UserEntity dummyEntity = UserEntity.builder().refreshToken(invalidString).build(); + + when(userRepositoryMock.findByUserIdAndUsername(userId, username)).thenReturn(dummyEntity); + + assertThrows(IllegalStateException.class, () -> + userBusinessService.getRefreshTokenForUser(dummyUser) + ); } @Test - void getUserByRefreshTokenAndUserId() { + void getCorrectRefreshTokenForUser() { + long userId = 420; + String username = "someString"; + String refreshToken = "someToken"; + User dummyUser = User.builder().id(userId).username(username).build(); + UserEntity dummyEntity = UserEntity.builder().refreshToken(refreshToken).build(); + RefreshToken expected = RefreshToken.builder().refreshToken(refreshToken).user(dummyUser).build(); + + when(userRepositoryMock.findByUserIdAndUsername(userId, username)).thenReturn(dummyEntity); + + RefreshToken actual = userBusinessService.getRefreshTokenForUser(dummyUser); + assertEquals(expected, actual); } + // -------------------------------------------------------------------------------------------- // + @Test - void getUserByAccessTokenAndUserId() { + void getUserByAccessTokenAndUserIdWithInvalidUserId() { + long userId = 420; + AccessToken dummyAccessToken = AccessToken.builder().userId(300).build(); + + assertThrows(UserNotAuthenticatedException.class, () -> + userBusinessService.getUserByAccessTokenAndUserId(dummyAccessToken, userId) + ); } @Test - void getUserEntityByUserIdAndUsername() { + void getUserByAccessTokenAndUserIdWithoutUser() { + long userId = 420; + AccessToken accessToken = AccessToken.builder().userId(userId).build(); + + when(userRepositoryMock.findByUserId(userId)).thenReturn(null); + + assertThrows(UserNotFoundException.class, () -> + userBusinessService.getUserByAccessTokenAndUserId(accessToken, userId) + ); + } + + @Test + void getUserByAccessTokenAndUserIdCorrectly() { + long userId = 420; + AccessToken accessToken = AccessToken.builder().userId(userId).build(); + User dummyUser = User.builder().id(userId).build(); + UserEntity dummyEntity = UserEntity.builder().build(); + + when(userRepositoryMock.findByUserId(userId)).thenReturn(dummyEntity); + when(userDtoServiceMock.createDto(dummyEntity)).thenReturn(dummyUser); + + User actual = userBusinessService.getUserByAccessTokenAndUserId(accessToken, userId); + + assertEquals(dummyUser, actual); + } + + // -------------------------------------------------------------------------------------------- // + + @Test + void getUserByRefreshTokenAndUserIdWithInvalidToken() { + assertThrows(UserNotAuthenticatedException.class, () -> + userBusinessService.getUserByRefreshTokenAndUserId("", 0) + ); + } + + @Test + void getUserByRefreshTokenAndUserIdWithoutUser() { + String token = "token"; + long userId = 420; + + when(userRepositoryMock.findByRefreshTokenAndUserId(token, userId)).thenReturn(null); + + assertThrows(UserNotFoundException.class, () -> + userBusinessService.getUserByRefreshTokenAndUserId(token, userId) + ); + } + + @Test + void getUserByRefreshTokenAndUserIdCorrectly() { + String token = "token"; + long userId = 420; + User dummyUser = User.builder().id(userId).build(); + UserEntity dummyEntity = UserEntity.builder().refreshToken(token).build(); + + when(userRepositoryMock.findByRefreshTokenAndUserId(token, userId)).thenReturn(dummyEntity); + when(userDtoServiceMock.createDto(dummyEntity)).thenReturn(dummyUser); + + User actual = userBusinessService.getUserByRefreshTokenAndUserId(token, userId); + + assertEquals(dummyUser, actual); } } \ No newline at end of file diff --git a/src/test/java/de/filefighter/rest/domain/user/business/UserDtoServiceUnitTest.java b/src/test/java/de/filefighter/rest/domain/user/business/UserDtoServiceUnitTest.java index 8275936a..3c5b8e3c 100644 --- a/src/test/java/de/filefighter/rest/domain/user/business/UserDtoServiceUnitTest.java +++ b/src/test/java/de/filefighter/rest/domain/user/business/UserDtoServiceUnitTest.java @@ -1,21 +1,67 @@ package de.filefighter.rest.domain.user.business; +import de.filefighter.rest.domain.user.data.dto.User; +import de.filefighter.rest.domain.user.data.persistance.UserEntity; +import de.filefighter.rest.domain.user.data.persistance.UserRepository; +import de.filefighter.rest.domain.user.exceptions.UserNotFoundException; +import de.filefighter.rest.domain.user.role.GroupRepository; +import de.filefighter.rest.domain.user.role.Groups; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; class UserDtoServiceUnitTest { + private final GroupRepository groupRepositoryMock = mock(GroupRepository.class); + private final UserRepository userRepositoryMock = mock(UserRepository.class); + private UserDtoService userDtoService; + @BeforeEach void setUp() { + userDtoService = new UserDtoService(groupRepositoryMock, userRepositoryMock); } @Test void createDto() { + long userId = 0; + String username = "kevin"; + long[] groups = new long[]{0}; + UserEntity dummyEntity = UserEntity.builder().userId(userId).groupIds(groups).username(username).build(); + + when(groupRepositoryMock.getRolesByIds(groups)).thenReturn(new Groups[]{Groups.FAMILY}); + + User actualUser = userDtoService.createDto(dummyEntity); + assertEquals(userId, actualUser.getId()); + assertEquals(username, actualUser.getUsername()); + assertEquals(groups[0], actualUser.getGroups()[0].getGroupId()); + } + + @Test + void findEntityThrowsException() { + long userId = 0; + String username = "kevin"; + User user = User.builder().username(username).id(userId).build(); + + when(userRepositoryMock.findByUserIdAndUsername(userId, username)).thenReturn(null); + + assertThrows(UserNotFoundException.class, () -> + userDtoService.findEntity(user) + ); } @Test - void findEntity() { + void findEntityWorksCorrectly() { + long userId = 0; + String username = "kevin"; + User user = User.builder().username(username).id(userId).build(); + UserEntity expected = UserEntity.builder().username(username).userId(userId).build(); + + when(userRepositoryMock.findByUserIdAndUsername(userId, username)).thenReturn(expected); + UserEntity actual = userDtoService.findEntity(user); + + assertEquals(expected, actual); } } \ No newline at end of file diff --git a/src/test/java/de/filefighter/rest/domain/user/rest/UserRestServiceUnitTest.java b/src/test/java/de/filefighter/rest/domain/user/rest/UserRestServiceUnitTest.java deleted file mode 100644 index 0c0b99cb..00000000 --- a/src/test/java/de/filefighter/rest/domain/user/rest/UserRestServiceUnitTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package de.filefighter.rest.domain.user.rest; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -class UserRestServiceUnitTest { - - @BeforeEach - void setUp() { - } - - @Test - void getUserByAccessTokenAndUserId() { - } - - @Test - void getRefreshTokenWithUsernameAndPassword() { - } - - @Test - void getAccessTokenByRefreshTokenAndUserId() { - } - - @Test - void updateUserByAccessTokenAndUserId() { - } - - @Test - void registerNewUserWithAccessToken() { - } - - @Test - void findUserByUsernameAndAccessToken() { - } -} \ No newline at end of file diff --git a/src/test/resources/SystemHealth.feature b/src/test/resources/SystemHealth.feature index 5018b16d..02245887 100644 --- a/src/test/resources/SystemHealth.feature +++ b/src/test/resources/SystemHealth.feature @@ -7,6 +7,7 @@ Background: Scenario: SystemHealth is requested without users in db When the systemHealth endpoint is requested + And the user waits for 2 second(s) Then response contains key "userCount" and value "0" And response contains key "uptimeInSeconds" and value of at least 1 diff --git a/src/test/resources/UserAuthorization.feature b/src/test/resources/UserAuthorization.feature index 5afa3eaf..d53ec677 100644 --- a/src/test/resources/UserAuthorization.feature +++ b/src/test/resources/UserAuthorization.feature @@ -14,9 +14,9 @@ Scenario: Successful login with username and password. Scenario: Failed login with wrong username or password. When user requests login with username "user" and password "wrong_password" - Then response contains key "message" and value "User could not be authenticated. No User found for username or password." + Then response contains key "message" and value "No User found with this username and password." And response contains key "status" and value "denied" - And response status code is 401 + And response status code is 404 Scenario: Successful creation of new accessToken with refreshToken. When user requests accessToken with refreshToken "token" and userId 1234 @@ -42,9 +42,9 @@ Scenario: Successful retrieval of overwritten accessToken with refreshToken Scenario: Failed retrieval of accessToken with wrong refreshToken. When user requests accessToken with refreshToken "not_the_token" and userId 1234 - Then response contains key "message" and value "User with the id 1234 could not be authenticated." + Then response contains key "message" and value "Could not find user 1234" And response contains key "status" and value "denied" - And response status code is 401 + And response status code is 404 Scenario: Successful UserInfo request with valid accessToken. Given accessToken with value "6bb9cb4f-7b51-4c0a-8013-ed7a34e56282" exists for user 1234 From 6fd055797e039d57351081d5899a7f2643c39ccb Mon Sep 17 00:00:00 2001 From: open-schnick Date: Thu, 5 Nov 2020 13:15:07 +0100 Subject: [PATCH 8/8] implemented changes requested by @qvalentin --- src/test/resources/SystemHealth.feature | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/resources/SystemHealth.feature b/src/test/resources/SystemHealth.feature index 02245887..1a75e9b3 100644 --- a/src/test/resources/SystemHealth.feature +++ b/src/test/resources/SystemHealth.feature @@ -10,6 +10,7 @@ Scenario: SystemHealth is requested without users in db And the user waits for 2 second(s) Then response contains key "userCount" and value "0" And response contains key "uptimeInSeconds" and value of at least 1 + And response status code is 200 Scenario: SystemHealth is requested with users in db Given user 1234 exists @@ -18,4 +19,5 @@ Scenario: SystemHealth is requested with users in db When the systemHealth endpoint is requested Then response contains key "userCount" and value "2" And response contains key "uptimeInSeconds" and value of at least 1 + And response status code is 200