diff --git a/pom.xml b/pom.xml
index ec86f801..a8fdc88e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
de.filefighter
rest
- 0.0.4
+ 0.0.5
RestApi
RestApi for FileFighter
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 e64a07d2..3d53a940 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
@@ -37,7 +37,7 @@ public AccessToken getValidAccessTokenForUser(User user) {
accessTokenEntity = AccessTokenEntity
.builder()
.validUntil(currentTimeSeconds + ACCESS_TOKEN_DURATION_IN_SECONDS)
- .value(this.generateRandomTokenValue())
+ .value(generateRandomTokenValue())
.userId(user.getId())
.build();
accessTokenEntity = accessTokenRepository.save(accessTokenEntity);
@@ -47,7 +47,7 @@ public AccessToken getValidAccessTokenForUser(User user) {
accessTokenEntity = AccessTokenEntity
.builder()
.validUntil(currentTimeSeconds + ACCESS_TOKEN_DURATION_IN_SECONDS)
- .value(this.generateRandomTokenValue())
+ .value(generateRandomTokenValue())
.userId(user.getId())
.build();
accessTokenEntity = accessTokenRepository.save(accessTokenEntity);
@@ -80,7 +80,7 @@ public AccessToken findAccessTokenByValue(String accessTokenValue) {
}
- public AccessToken validateAccessTokenValue(String accessTokenValue) {
+ public AccessToken validateAccessTokenValueWithHeader(String accessTokenValue) {
String cleanValue = validateAuthorizationHeader(AUTHORIZATION_BEARER_PREFIX, accessTokenValue);
AccessTokenEntity accessTokenEntity = accessTokenRepository.findByValue(cleanValue);
if (null == accessTokenEntity)
diff --git a/src/main/java/de/filefighter/rest/domain/user/business/UserAuthorizationService.java b/src/main/java/de/filefighter/rest/domain/user/business/UserAuthorizationService.java
index bf6e20fe..b9d0bee9 100644
--- a/src/main/java/de/filefighter/rest/domain/user/business/UserAuthorizationService.java
+++ b/src/main/java/de/filefighter/rest/domain/user/business/UserAuthorizationService.java
@@ -1,5 +1,6 @@
package de.filefighter.rest.domain.user.business;
+import de.filefighter.rest.domain.common.Utils;
import de.filefighter.rest.domain.token.data.dto.AccessToken;
import de.filefighter.rest.domain.user.data.dto.User;
import de.filefighter.rest.domain.user.data.persistance.UserEntity;
@@ -15,6 +16,9 @@
import java.nio.charset.StandardCharsets;
import java.util.Base64;
+import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BASIC_PREFIX;
+import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BEARER_PREFIX;
+
@Service
public class UserAuthorizationService {
@@ -28,7 +32,9 @@ public UserAuthorizationService(UserRepository userRepository, UserDtoService us
this.userDtoService = userDtoService;
}
- public User authenticateUserWithUsernameAndPassword(String base64encodedUserAndPassword) {
+ public User authenticateUserWithUsernameAndPassword(String base64encodedUserAndPasswordWithHeader) {
+ String base64encodedUserAndPassword = Utils.validateAuthorizationHeader(AUTHORIZATION_BASIC_PREFIX, base64encodedUserAndPasswordWithHeader);
+
String decodedUsernameAndPassword = "";
try {
byte[] decodedValue = Base64.getDecoder().decode(base64encodedUserAndPassword);
@@ -54,17 +60,20 @@ public User authenticateUserWithUsernameAndPassword(String base64encodedUserAndP
}
public User authenticateUserWithRefreshToken(String refreshToken) {
- UserEntity userEntity = userRepository.findByRefreshToken(refreshToken);
+ String cleanValue = Utils.validateAuthorizationHeader(AUTHORIZATION_BEARER_PREFIX, refreshToken);
+ UserEntity userEntity = userRepository.findByRefreshToken(cleanValue);
if (null == userEntity)
throw new UserNotAuthenticatedException("No user found for this Refresh Token.");
return userDtoService.createDto(userEntity);
}
- public void authenticateUserWithAccessToken(AccessToken accessToken) {
+ public User authenticateUserWithAccessToken(AccessToken accessToken) {
UserEntity userEntity = userRepository.findByUserId(accessToken.getUserId());
if (null == userEntity)
throw new UserNotAuthenticatedException(accessToken.getUserId());
+
+ return userDtoService.createDto(userEntity);
}
public void authenticateUserWithAccessTokenAndGroup(AccessToken accessToken, Groups groups) {
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 34a17c80..041d367d 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
@@ -9,13 +9,20 @@
import de.filefighter.rest.domain.user.data.persistance.UserRepository;
import de.filefighter.rest.domain.user.exceptions.UserNotFoundException;
import de.filefighter.rest.domain.user.exceptions.UserNotRegisteredException;
+import de.filefighter.rest.domain.user.exceptions.UserNotUpdatedException;
import de.filefighter.rest.domain.user.group.GroupRepository;
+import de.filefighter.rest.domain.user.group.Groups;
import de.filefighter.rest.rest.exceptions.RequestDidntMeetFormalRequirementsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
+import java.util.Arrays;
import java.util.regex.Pattern;
import static de.filefighter.rest.domain.common.Utils.stringIsValid;
@@ -26,16 +33,19 @@ public class UserBusinessService {
private final UserRepository userRepository;
private final UserDtoService userDtoService;
private final GroupRepository groupRepository;
+ private final MongoTemplate mongoTemplate;
+
private static final Logger LOG = LoggerFactory.getLogger(UserBusinessService.class);
@Value("${filefighter.disable-password-check}")
public boolean passwordCheckDisabled;
- public UserBusinessService(UserRepository userRepository, UserDtoService userDtoService, GroupRepository groupRepository) {
+ public UserBusinessService(UserRepository userRepository, UserDtoService userDtoService, GroupRepository groupRepository, MongoTemplate mongoTemplate) {
this.userRepository = userRepository;
this.userDtoService = userDtoService;
this.groupRepository = groupRepository;
+ this.mongoTemplate = mongoTemplate;
}
public long getUserCount() {
@@ -97,14 +107,15 @@ public void registerNewUser(UserRegisterForm newUser) {
// check pws.
String password = newUser.getPassword();
- passwordIsValid(password);
+ if (!passwordIsValid(password))
+ throw new UserNotRegisteredException("Password needs to be at least 8 characters long and, contains at least one uppercase and lowercase letter and a number.");
String confirmationPassword = newUser.getConfirmationPassword();
if (!password.contentEquals(confirmationPassword))
throw new UserNotRegisteredException("Passwords do not match.");
- if(password.toLowerCase().contains(username.toLowerCase()))
+ if (password.toLowerCase().contains(username.toLowerCase()))
throw new UserNotRegisteredException("Username must not appear in password.");
//check groups
@@ -130,14 +141,93 @@ public void registerNewUser(UserRegisterForm newUser) {
.build());
}
- public void passwordIsValid(String password) {
+ public boolean passwordIsValid(String password) {
if (!Utils.stringIsValid(password))
- throw new UserNotRegisteredException("Password was empty.");
+ return false;
- if(this.passwordCheckDisabled) return;
+ if (this.passwordCheckDisabled) return true;
- Pattern pattern = Pattern.compile("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=\\S+$).{8,20}$");
- if (!pattern.matcher(password).matches())
- throw new UserNotRegisteredException("Password needs to be at least 8 characters long and, contains at least one uppercase and lowercase letter and a number.");
+ Pattern pattern = Pattern.compile("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=\\S+).{8,20}$");
+ return pattern.matcher(password).matches();
+ }
+
+ public void updateUser(long userId, UserRegisterForm userToUpdate, User authenticatedUser) {
+ if (null == userToUpdate)
+ throw new UserNotUpdatedException("No updates specified.");
+
+ if(null == authenticatedUser.getGroups())
+ throw new UserNotUpdatedException("Authenticated User is not allowed");
+
+ boolean authenticatedUserIsAdmin = Arrays.stream(authenticatedUser.getGroups()).anyMatch(g -> g == Groups.ADMIN);
+ if (userId != authenticatedUser.getId() && !authenticatedUserIsAdmin)
+ throw new UserNotUpdatedException("Only Admins are allowed to update other users.");
+
+ UserEntity userEntityToUpdate = userRepository.findByUserId(userId);
+ Update newUpdate = new Update();
+ boolean changesWereMade = false;
+
+ // username
+ String username = userToUpdate.getUsername();
+ if (null != username) {
+ if (!stringIsValid(username))
+ throw new UserNotUpdatedException("Wanted to change username, but username was not valid.");
+
+ User user = null;
+ try {
+ user = this.findUserByUsername(username);
+ } catch (UserNotFoundException ignored) {
+ LOG.info("Username '{}' is free to use.", username);
+ }
+
+ if (null != user)
+ throw new UserNotUpdatedException("Username already taken.");
+
+ changesWereMade = true;
+ newUpdate.set("username", username);
+ }
+
+ // pw
+ if (null != userToUpdate.getPassword()) {
+ String password = userToUpdate.getPassword();
+ String confirmation = userToUpdate.getConfirmationPassword();
+
+ if (!stringIsValid(password) || !stringIsValid(confirmation))
+ throw new UserNotUpdatedException("Wanted to change password, but password was not valid.");
+
+ if (!passwordIsValid(password))
+ throw new UserNotUpdatedException("Password needs to be at least 8 characters long and, contains at least one uppercase and lowercase letter and a number.");
+
+ if (!password.contentEquals(confirmation))
+ throw new UserNotUpdatedException("Passwords do not match.");
+
+ if (password.toLowerCase().contains(userEntityToUpdate.getLowercaseUsername()))
+ throw new UserNotUpdatedException("Username must not appear in password.");
+
+ changesWereMade = true;
+ newUpdate.set("password", password);
+ }
+
+ // groups
+ if (null != userToUpdate.getGroupIds()) {
+ try {
+ for (Groups group : groupRepository.getGroupsByIds(userToUpdate.getGroupIds())) {
+ if (group == Groups.ADMIN && !authenticatedUserIsAdmin)
+ throw new UserNotUpdatedException("Only admins can add users to group " + Groups.ADMIN.getDisplayName());
+ }
+ } catch (IllegalArgumentException exception) {
+ throw new UserNotUpdatedException("One or more groups do not exist.");
+ }
+
+ changesWereMade = true;
+ newUpdate.set("groupIds", userToUpdate.getGroupIds());
+ }
+
+ if(!changesWereMade)
+ throw new UserNotUpdatedException("No changes were made.");
+
+ Query query = new Query();
+ query.addCriteria(Criteria.where("userId").is(userId));
+ mongoTemplate.findAndModify(query, newUpdate, UserEntity.class);
}
}
+
diff --git a/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotUpdatedAdvise.java b/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotUpdatedAdvise.java
new file mode 100644
index 00000000..24bd8ab7
--- /dev/null
+++ b/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotUpdatedAdvise.java
@@ -0,0 +1,22 @@
+package de.filefighter.rest.domain.user.exceptions;
+
+import de.filefighter.rest.rest.ServerResponse;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ControllerAdvice
+class UserNotUpdatedAdvise {
+
+ @ResponseBody
+ @ExceptionHandler(UserNotUpdatedException.class)
+ @ResponseStatus(HttpStatus.CONFLICT)
+ ResponseEntity userNotUpdatedExceptionHandler(UserNotUpdatedException ex) {
+ LoggerFactory.getLogger(UserNotUpdatedException.class).warn(ex.getMessage());
+ return new ResponseEntity<>(new ServerResponse(HttpStatus.CONFLICT, ex.getMessage()), HttpStatus.CONFLICT);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotUpdatedException.java b/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotUpdatedException.java
new file mode 100644
index 00000000..0c29e4c8
--- /dev/null
+++ b/src/main/java/de/filefighter/rest/domain/user/exceptions/UserNotUpdatedException.java
@@ -0,0 +1,11 @@
+package de.filefighter.rest.domain.user.exceptions;
+
+public class UserNotUpdatedException extends RuntimeException{
+ public UserNotUpdatedException() {
+ super("User could not get updated");
+ }
+
+ public UserNotUpdatedException(String reason) {
+ super("User could not get updated. "+reason);
+ }
+}
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 85998f47..4c379aec 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
@@ -64,13 +64,14 @@ public ResponseEntity getUserInfo(
return userRestService.getUserByUserIdAuthenticateWithAccessToken(accessToken, userId);
}
- @PutMapping(USER_BASE_URI + "edit")
- public ResponseEntity updateUser(
+ @PutMapping(USER_BASE_URI + "{userId}/edit")
+ public ResponseEntity updateUser(
+ @PathVariable long userId,
@RequestHeader(value = "Authorization", defaultValue = AUTHORIZATION_BEARER_PREFIX + "token") String accessToken,
@RequestBody UserRegisterForm updatedUser) {
- LOG.info("Updated User and Token {}, with form {}.", accessToken, updatedUser);
- return userRestService.updateUserWithAccessToken(updatedUser, accessToken);
+ LOG.info("Updated User {} and Token {}, with form {}.", userId, accessToken, updatedUser);
+ return userRestService.updateUserByUserIdAuthenticateWithAccessToken(updatedUser, userId, accessToken);
}
@GetMapping(USER_BASE_URI + "find")
@@ -78,7 +79,7 @@ public ResponseEntity findUserByUsername(
@RequestHeader(value = "Authorization", defaultValue = AUTHORIZATION_BEARER_PREFIX + "token") String accessToken,
@RequestParam(name = "username", value = "username") String username
) {
- LOG.info("Requested finding User with the username {} and Token {}", username, accessToken);
+ LOG.info("Requested finding User with the username {} and Token {}", username, accessToken);
return userRestService.findUserByUsernameAndAccessToken(username, accessToken);
}
}
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 99075988..013d01b5 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,6 +1,5 @@
package de.filefighter.rest.domain.user.rest;
-import de.filefighter.rest.domain.common.Utils;
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;
@@ -13,8 +12,6 @@
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
-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.user.group.Groups.ADMIN;
@@ -33,36 +30,38 @@ public UserRestService(UserBusinessService userBusinessService, UserAuthorizatio
@Override
public ResponseEntity getUserByUserIdAuthenticateWithAccessToken(String accessToken, long userId) {
- AccessToken validAccessToken = accessTokenBusinessService.validateAccessTokenValue(accessToken);
+ AccessToken validAccessToken = accessTokenBusinessService.validateAccessTokenValueWithHeader(accessToken);
userAuthorizationService.authenticateUserWithAccessToken(validAccessToken);
User user = userBusinessService.getUserById(userId);
return new ResponseEntity<>(user, HttpStatus.OK);
}
@Override
- public ResponseEntity getRefreshTokenWithUsernameAndPassword(String base64encodedUserAndPassword) {
- String cleanValue = Utils.validateAuthorizationHeader(AUTHORIZATION_BASIC_PREFIX, base64encodedUserAndPassword);
- User authenticatedUser = userAuthorizationService.authenticateUserWithUsernameAndPassword(cleanValue);
+ public ResponseEntity getRefreshTokenWithUsernameAndPassword(String base64encodedUserAndPasswordWithHeader) {
+ User authenticatedUser = userAuthorizationService.authenticateUserWithUsernameAndPassword(base64encodedUserAndPasswordWithHeader);
RefreshToken refreshToken = userBusinessService.getRefreshTokenForUser(authenticatedUser);
return new ResponseEntity<>(refreshToken, HttpStatus.OK);
}
@Override
- public ResponseEntity getAccessTokenByRefreshToken(String refreshToken) {
- String cleanValue = Utils.validateAuthorizationHeader(AUTHORIZATION_BEARER_PREFIX, refreshToken);
- User user = userAuthorizationService.authenticateUserWithRefreshToken(cleanValue);
+ public ResponseEntity getAccessTokenByRefreshToken(String refreshTokenWithHeader) {
+ User user = userAuthorizationService.authenticateUserWithRefreshToken(refreshTokenWithHeader);
AccessToken accessToken = accessTokenBusinessService.getValidAccessTokenForUser(user);
return new ResponseEntity<>(accessToken, HttpStatus.OK);
}
@Override
- public ResponseEntity updateUserWithAccessToken(UserRegisterForm updatedUser, String accessToken) {
- return null;
+ public ResponseEntity updateUserByUserIdAuthenticateWithAccessToken(UserRegisterForm updatedUser, long userId, String accessTokenValue) {
+ AccessToken accessToken = accessTokenBusinessService.validateAccessTokenValueWithHeader(accessTokenValue);
+ User authenticatedUser = userAuthorizationService.authenticateUserWithAccessToken(accessToken);
+ userBusinessService.updateUser(userId, updatedUser, authenticatedUser);
+ ServerResponse response = new ServerResponse(HttpStatus.CREATED, "User successfully updated.");
+ return new ResponseEntity<>(response, HttpStatus.CREATED);
}
@Override
public ResponseEntity registerNewUserWithAccessToken(UserRegisterForm newUser, String accessToken) {
- AccessToken validAccessToken = accessTokenBusinessService.validateAccessTokenValue(accessToken);
+ AccessToken validAccessToken = accessTokenBusinessService.validateAccessTokenValueWithHeader(accessToken);
userAuthorizationService.authenticateUserWithAccessTokenAndGroup(validAccessToken, ADMIN);
userBusinessService.registerNewUser(newUser);
return new ResponseEntity<>(new ServerResponse(HttpStatus.CREATED, "User successfully created."), HttpStatus.CREATED);
@@ -70,7 +69,7 @@ public ResponseEntity registerNewUserWithAccessToken(UserRegiste
@Override
public ResponseEntity findUserByUsernameAndAccessToken(String username, String accessToken) {
- AccessToken token = accessTokenBusinessService.validateAccessTokenValue(accessToken);
+ AccessToken token = accessTokenBusinessService.validateAccessTokenValueWithHeader(accessToken);
userAuthorizationService.authenticateUserWithAccessToken(token);
User foundUser = userBusinessService.findUserByUsername(username);
return new ResponseEntity<>(foundUser, HttpStatus.OK);
diff --git a/src/main/java/de/filefighter/rest/domain/user/rest/UserRestServiceInterface.java b/src/main/java/de/filefighter/rest/domain/user/rest/UserRestServiceInterface.java
index ed121e17..b653caa4 100644
--- a/src/main/java/de/filefighter/rest/domain/user/rest/UserRestServiceInterface.java
+++ b/src/main/java/de/filefighter/rest/domain/user/rest/UserRestServiceInterface.java
@@ -11,7 +11,7 @@ public interface UserRestServiceInterface {
ResponseEntity getUserByUserIdAuthenticateWithAccessToken(String accessToken, long userId);
ResponseEntity getRefreshTokenWithUsernameAndPassword(String base64encodedUserAndPassword);
ResponseEntity getAccessTokenByRefreshToken(String refreshToken);
- ResponseEntity updateUserWithAccessToken(UserRegisterForm updatedUser, String accessToken);
+ ResponseEntity updateUserByUserIdAuthenticateWithAccessToken(UserRegisterForm updatedUser, long userId, String accessToken);
ResponseEntity registerNewUserWithAccessToken(UserRegisterForm newUser, String accessToken);
ResponseEntity findUserByUsernameAndAccessToken(String username, String accessToken);
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 10eb542e..05b88aea 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -8,5 +8,5 @@ spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
#-------------------Custom------------------
filefighter.version=0.0.4
-filefighter.date=19.11.2020
+filefighter.date=21.11.2020
filefighter.disable-password-check=false
\ No newline at end of file
diff --git a/src/test/java/de/filefighter/rest/TestUtils.java b/src/test/java/de/filefighter/rest/TestUtils.java
index e4d19641..4e0247ee 100644
--- a/src/test/java/de/filefighter/rest/TestUtils.java
+++ b/src/test/java/de/filefighter/rest/TestUtils.java
@@ -14,7 +14,7 @@ public static String serializeUserRequest(String confirmationPassword, int[] gro
jsonString.append("\"groupIds\": ").append(Arrays.toString(groupIds)).append(",");
}
if (password != null) {
- jsonString.append("\"password\": \"").append(password).append(username != null?"\",":"");
+ jsonString.append("\"password\": \"").append(password).append(username != null ? "\"," : "\"");
}
if (username != null) {
jsonString.append("\"username\": \"").append(username).append("\"");
diff --git a/src/test/java/de/filefighter/rest/cucumber/UserEditInformationSteps.java b/src/test/java/de/filefighter/rest/cucumber/UserEditInformationSteps.java
index e2341aa1..b2f4d708 100644
--- a/src/test/java/de/filefighter/rest/cucumber/UserEditInformationSteps.java
+++ b/src/test/java/de/filefighter/rest/cucumber/UserEditInformationSteps.java
@@ -11,30 +11,39 @@
import static de.filefighter.rest.configuration.RestConfiguration.*;
public class UserEditInformationSteps extends RestApplicationIntegrationTest {
- @When("user requests change of username with value {string} and accessToken {string}")
- public void userRequestsChangeOfUsernameWithValueAndAccessTokenAndId(String newUsername, String accessToken) {
+ @When("user requests change of username with value {string} userId {long} and accessToken {string}")
+ public void userRequestsChangeOfUsernameWithValueAndAccessTokenAndId(String newUsername, long userId, String accessToken) {
String authHeaderString = AUTHORIZATION_BEARER_PREFIX + accessToken;
- String url = BASE_API_URI + USER_BASE_URI + "edit";
+ String url = BASE_API_URI + USER_BASE_URI + userId + "/edit";
HashMap authHeader = new HashMap<>();
authHeader.put("Authorization", authHeaderString);
- String postBody= serializeUserRequest(null,null,null,newUsername);
- executeRestApiCall(HttpMethod.PUT, url, authHeader,postBody);
+ String postBody = serializeUserRequest(null, null, null, newUsername);
+ executeRestApiCall(HttpMethod.PUT, url, authHeader, postBody);
}
- @When("user requests change of password with value {string} and accessToken {string} and id {string}")
- public void userRequestsChangeOfPasswordWithValueAndAccessTokenAndId(String newPassword, String accessToken, String userId) {
+ @When("user requests change of password with value {string} userId {long} and accessToken {string}")
+ public void userRequestsChangeOfPasswordWithValueAndAccessTokenAndId(String newPassword, long userId, String accessToken) {
String authHeaderString = AUTHORIZATION_BEARER_PREFIX + accessToken;
String url = BASE_API_URI + USER_BASE_URI + userId + "/edit";
HashMap authHeader = new HashMap<>();
authHeader.put("Authorization", authHeaderString);
+ String postBody = serializeUserRequest(newPassword, null, newPassword, null);
+ executeRestApiCall(HttpMethod.PUT, url, authHeader, postBody);
+ }
- String postBody=serializeUserRequest(newPassword,null,newPassword,null);
+ @When("user requests change of password with no changes, userId {long} and accessToken {string}")
+ public void userRequestsChangeOfPasswordWithNoChangesUserIdLongAndAccessTokenString(long userId, String accessToken) {
+ String authHeaderString = AUTHORIZATION_BEARER_PREFIX + accessToken;
+ String url = BASE_API_URI + USER_BASE_URI + userId + "/edit";
+ HashMap authHeader = new HashMap<>();
+ authHeader.put("Authorization", authHeaderString);
+ String postBody = serializeUserRequest(null, null, null, null);
- executeRestApiCall(HttpMethod.GET, url, authHeader,postBody);
+ executeRestApiCall(HttpMethod.PUT, url, authHeader, postBody);
}
}
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 398254ec..af098330 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
@@ -152,7 +152,7 @@ void findAccessTokenByValueSuccessfully() {
@Test
void generateRandomTokenValue() {
- String generatedToken = accessTokenBusinessService.generateRandomTokenValue();
+ String generatedToken = AccessTokenBusinessService.generateRandomTokenValue();
assertEquals(36, generatedToken.length());
}
@@ -162,13 +162,13 @@ void validateAccessTokenValueWithWrongHeader() {
String header1 = "";
assertThrows(RequestDidntMeetFormalRequirementsException.class, () ->
- accessTokenBusinessService.validateAccessTokenValue(header0)
+ accessTokenBusinessService.validateAccessTokenValueWithHeader(header0)
);
assertThrows(RequestDidntMeetFormalRequirementsException.class, () ->
- accessTokenBusinessService.validateAccessTokenValue(header1)
+ accessTokenBusinessService.validateAccessTokenValueWithHeader(header1)
);
assertThrows(RequestDidntMeetFormalRequirementsException.class, () ->
- accessTokenBusinessService.validateAccessTokenValue(AUTHORIZATION_BEARER_PREFIX)
+ accessTokenBusinessService.validateAccessTokenValueWithHeader(AUTHORIZATION_BEARER_PREFIX)
);
}
@@ -179,7 +179,7 @@ void validateAccessTokenValueButTokenDoesNotExist() {
when(accessTokenRepositoryMock.findByValue("something")).thenReturn(null);
assertThrows(UserNotAuthenticatedException.class, () ->
- accessTokenBusinessService.validateAccessTokenValue(header)
+ accessTokenBusinessService.validateAccessTokenValueWithHeader(header)
);
}
@@ -192,7 +192,7 @@ void validateAccessTokenValue() {
when(accessTokenRepositoryMock.findByValue("something")).thenReturn(accessTokenEntity);
when(accessTokenDtoServiceMock.createDto(accessTokenEntity)).thenReturn(expected);
- AccessToken actual = accessTokenBusinessService.validateAccessTokenValue(header);
+ AccessToken actual = accessTokenBusinessService.validateAccessTokenValueWithHeader(header);
assertEquals(expected, actual);
}
diff --git a/src/test/java/de/filefighter/rest/domain/user/business/UserAuthorizationServiceUnitTest.java b/src/test/java/de/filefighter/rest/domain/user/business/UserAuthorizationServiceUnitTest.java
index f524c720..b72374d0 100644
--- a/src/test/java/de/filefighter/rest/domain/user/business/UserAuthorizationServiceUnitTest.java
+++ b/src/test/java/de/filefighter/rest/domain/user/business/UserAuthorizationServiceUnitTest.java
@@ -1,5 +1,6 @@
package de.filefighter.rest.domain.user.business;
+import de.filefighter.rest.configuration.RestConfiguration;
import de.filefighter.rest.domain.token.data.dto.AccessToken;
import de.filefighter.rest.domain.user.data.dto.User;
import de.filefighter.rest.domain.user.data.persistance.UserEntity;
@@ -9,6 +10,7 @@
import de.filefighter.rest.rest.exceptions.RequestDidntMeetFormalRequirementsException;
import org.junit.jupiter.api.Test;
+import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BASIC_PREFIX;
import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BEARER_PREFIX;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;
@@ -24,9 +26,9 @@ class UserAuthorizationServiceUnitTest {
@Test
void authenticateUserWithUsernameAndPasswordThrows() {
- String matchesButIsNotSupportedEncoding = "���";
- String matchesButDoesNotMeetRequirements = "dWdhYnVnYQ==";
- String matchesButUserWasNotFound = "dXNlcjp1c2Vy";
+ String matchesButIsNotSupportedEncoding = AUTHORIZATION_BASIC_PREFIX + "���";
+ String matchesButDoesNotMeetRequirements = AUTHORIZATION_BASIC_PREFIX + "dWdhYnVnYQ==";
+ String matchesButUserWasNotFound = AUTHORIZATION_BASIC_PREFIX + "dXNlcjpwYXNzd29yZA==";
assertThrows(RuntimeException.class, () ->
userAuthorizationService.authenticateUserWithUsernameAndPassword(matchesButIsNotSupportedEncoding)
@@ -35,7 +37,7 @@ void authenticateUserWithUsernameAndPasswordThrows() {
userAuthorizationService.authenticateUserWithUsernameAndPassword(matchesButDoesNotMeetRequirements)
);
- when(userRepositoryMock.findByUsernameAndPassword("user", "user")).thenReturn(null);
+ when(userRepositoryMock.findByUsernameAndPassword("user", "password")).thenReturn(null);
assertThrows(UserNotAuthenticatedException.class, () ->
userAuthorizationService.authenticateUserWithUsernameAndPassword(matchesButUserWasNotFound));
@@ -43,7 +45,7 @@ void authenticateUserWithUsernameAndPasswordThrows() {
@Test
void authenticateUserWithUsernameAndPasswordWorksCorrectly() {
- String header = "dXNlcjpwYXNzd29yZA=="; // user:password
+ String header = AUTHORIZATION_BASIC_PREFIX + "dXNlcjpwYXNzd29yZA=="; // user:password
User dummyUser = User.builder().build();
UserEntity dummyEntity = UserEntity.builder().build();
@@ -68,13 +70,14 @@ void authenticateUserWithRefreshTokenThrowsExceptions() {
@Test
void authenticateUserWithRefreshTokenWorksCorrectly() {
String refreshToken = "Something";
+ String authString = AUTHORIZATION_BEARER_PREFIX + refreshToken;
UserEntity dummyEntity = UserEntity.builder().build();
User dummyUser = User.builder().build();
when(userRepositoryMock.findByRefreshToken(refreshToken)).thenReturn(dummyEntity);
when(userDtoServiceMock.createDto(dummyEntity)).thenReturn(dummyUser);
- User actualUser = userAuthorizationService.authenticateUserWithRefreshToken(refreshToken);
+ User actualUser = userAuthorizationService.authenticateUserWithRefreshToken(authString);
assertEquals(dummyUser, actualUser);
}
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 e715dc55..3cfab6ef 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
@@ -7,10 +7,13 @@
import de.filefighter.rest.domain.user.data.persistance.UserRepository;
import de.filefighter.rest.domain.user.exceptions.UserNotFoundException;
import de.filefighter.rest.domain.user.exceptions.UserNotRegisteredException;
+import de.filefighter.rest.domain.user.exceptions.UserNotUpdatedException;
import de.filefighter.rest.domain.user.group.GroupRepository;
+import de.filefighter.rest.domain.user.group.Groups;
import de.filefighter.rest.rest.exceptions.RequestDidntMeetFormalRequirementsException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.springframework.data.mongodb.core.MongoTemplate;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
@@ -22,11 +25,12 @@ class UserBusinessServiceUnitTest {
private final UserRepository userRepositoryMock = mock(UserRepository.class);
private final UserDtoService userDtoServiceMock = mock(UserDtoService.class);
private final GroupRepository groupRepositoryMock = mock(GroupRepository.class);
+ private final MongoTemplate mongoTemplateMock = mock(MongoTemplate.class);
private UserBusinessService userBusinessService;
@BeforeEach
void setUp() {
- userBusinessService = new UserBusinessService(userRepositoryMock, userDtoServiceMock, groupRepositoryMock);
+ userBusinessService = new UserBusinessService(userRepositoryMock, userDtoServiceMock, groupRepositoryMock, mongoTemplateMock);
}
@Test
@@ -138,17 +142,17 @@ void findUserByUsernameWorksCorrectly() {
}
@Test
- void passwordIsValidThrows() {
+ void passwordIsValidReturnsFalse() {
String isEmpty = "";
String[] doNotMatch = new String[]{"pw", "password", "Password", "Password\\", "asdfghjkljasdasda123AS?213+dfghjkfghjkghjk"};
- assertThrows(UserNotRegisteredException.class, () ->
- userBusinessService.passwordIsValid(isEmpty));
+ boolean actualState = userBusinessService.passwordIsValid(isEmpty);
for (String string : doNotMatch) {
- assertThrows(UserNotRegisteredException.class, () ->
- userBusinessService.passwordIsValid(string));
+ assertFalse(userBusinessService.passwordIsValid(string));
}
+
+ assertFalse(actualState);
}
@Test
@@ -200,7 +204,7 @@ void registerNewUserThrows() {
}
@Test
- void registerNewUserWorks(){
+ void registerNewUserWorks() {
String username = "username";
String password = "validPassword1234";
String confPassword = "validPassword1234";
@@ -215,4 +219,131 @@ void registerNewUserWorks(){
assertDoesNotThrow(() -> userBusinessService.registerNewUser(userRegisterForm));
}
+
+ @Test
+ void updateUserThrows() {
+ final UserRegisterForm userRegisterForm = null;
+ long userId = 420;
+ User authenticatedUser = User.builder().build();
+ assertThrows(UserNotUpdatedException.class, () ->
+ userBusinessService.updateUser(userId, userRegisterForm, authenticatedUser));
+
+ UserRegisterForm userRegisterForm1 = UserRegisterForm.builder().build();
+ assertThrows(UserNotUpdatedException.class, () ->
+ userBusinessService.updateUser(userId, userRegisterForm1, authenticatedUser));
+
+ authenticatedUser.setGroups(new Groups[]{Groups.UNDEFINED});
+ assertThrows(UserNotUpdatedException.class, () ->
+ userBusinessService.updateUser(userId, userRegisterForm1, authenticatedUser));
+
+ authenticatedUser.setGroups(new Groups[]{Groups.ADMIN});
+ assertThrows(UserNotUpdatedException.class, () ->
+ userBusinessService.updateUser(userId, userRegisterForm1, authenticatedUser));
+ }
+
+ @Test
+ void updateUserNameThrows() {
+ final UserRegisterForm userRegisterForm = UserRegisterForm.builder().build();
+ long userId = 420;
+ User authenticatedUser = User.builder().id(userId).groups(new Groups[]{Groups.FAMILY}).build();
+ UserEntity dummyEntity = UserEntity.builder().build();
+
+ userRegisterForm.setUsername("");
+ assertThrows(UserNotUpdatedException.class, () ->
+ userBusinessService.updateUser(userId, userRegisterForm, authenticatedUser));
+
+ String validUserName = "ValidUserNameButExists.";
+ userRegisterForm.setUsername(validUserName);
+ when(userRepositoryMock.findByLowercaseUsername(validUserName.toLowerCase())).thenReturn(dummyEntity);
+ when(userDtoServiceMock.createDto(dummyEntity)).thenReturn(User.builder().build());
+ assertThrows(UserNotUpdatedException.class, () ->
+ userBusinessService.updateUser(userId, userRegisterForm, authenticatedUser));
+ }
+
+
+ @Test
+ void updateUserNameWorks() {
+ final UserRegisterForm userRegisterForm = UserRegisterForm.builder().username("newUserName").build();
+ long userId = 420;
+ User authenticatedUser = User.builder().id(userId).groups(new Groups[]{Groups.FAMILY}).build();
+
+ assertDoesNotThrow(() -> userBusinessService.updateUser(userId, userRegisterForm, authenticatedUser));
+ }
+
+ @Test
+ void updatePasswordThrows() {
+ final UserRegisterForm userRegisterForm = UserRegisterForm.builder().build();
+ long userId = 420;
+ User authenticatedUser = User.builder().id(userId).groups(new Groups[]{Groups.FAMILY}).build();
+ UserEntity dummyEntity = UserEntity.builder().userId(userId).lowercaseUsername("password").build();
+
+ userRegisterForm.setPassword("");
+ assertThrows(UserNotUpdatedException.class, () ->
+ userBusinessService.updateUser(userId, userRegisterForm, authenticatedUser), "Wanted to change password, but password was not valid.");
+
+ userRegisterForm.setPassword("somepw");
+ userRegisterForm.setConfirmationPassword("somepw");
+ assertThrows(UserNotUpdatedException.class, () ->
+ userBusinessService.updateUser(userId, userRegisterForm, authenticatedUser), "Password needs to be at least 8 characters long and, contains at least one uppercase and lowercase letter and a number.");
+
+ userRegisterForm.setPassword("Somepw12345");
+ userRegisterForm.setConfirmationPassword("Somepw1234");
+ assertThrows(UserNotUpdatedException.class, () ->
+ userBusinessService.updateUser(userId, userRegisterForm, authenticatedUser), "Passwords do not match.");
+
+ String validPassword ="ValidPassword1234!=";
+ userRegisterForm.setPassword(validPassword);
+ userRegisterForm.setConfirmationPassword(validPassword);
+ when(userRepositoryMock.findByUserId(userId)).thenReturn(dummyEntity);
+ assertThrows(UserNotUpdatedException.class, () ->
+ userBusinessService.updateUser(userId, userRegisterForm, authenticatedUser), "Username must not appear in password.");
+ }
+
+ @Test
+ void updatePasswordWorks() {
+ String password = "validPassword1234";
+ final UserRegisterForm userRegisterForm = UserRegisterForm.builder().password(password).confirmationPassword(password).build();
+ long userId = 420;
+ User authenticatedUser = User.builder().id(userId).groups(new Groups[]{Groups.FAMILY}).build();
+ UserEntity dummyEntity = UserEntity.builder().userId(userId).lowercaseUsername("UGABUGA").build();
+
+ when(userRepositoryMock.findByUserId(userId)).thenReturn(dummyEntity);
+ assertDoesNotThrow(() -> userBusinessService.updateUser(userId, userRegisterForm, authenticatedUser));
+ }
+
+ @Test
+ void updateGroupsThrows() {
+ final UserRegisterForm userRegisterForm = UserRegisterForm.builder().build();
+ long userId = 420;
+ User authenticatedUser = User.builder().id(userId).groups(new Groups[]{Groups.FAMILY}).build();
+ UserEntity dummyEntity = UserEntity.builder().userId(userId).lowercaseUsername("password").build();
+
+ long[] groups = new long[]{0};
+ userRegisterForm.setGroupIds(groups);
+ when(userRepositoryMock.findByUserId(userId)).thenReturn(dummyEntity);
+ when(groupRepositoryMock.getGroupsByIds(groups)).thenReturn(new Groups[]{Groups.ADMIN});
+ assertThrows(UserNotUpdatedException.class, () ->
+ userBusinessService.updateUser(userId, userRegisterForm, authenticatedUser));
+
+ groups = new long[]{123032,1230213};
+ userRegisterForm.setGroupIds(groups);
+ when(userRepositoryMock.findByUserId(userId)).thenReturn(dummyEntity);
+ when(groupRepositoryMock.getGroupsByIds(groups)).thenThrow(new IllegalArgumentException("id doesnt belong to a group"));
+ assertThrows(UserNotUpdatedException.class, () ->
+ userBusinessService.updateUser(userId, userRegisterForm, authenticatedUser));
+ }
+
+ @Test
+ void updateGroupsWorks() {
+ final UserRegisterForm userRegisterForm = UserRegisterForm.builder().build();
+ long userId = 420;
+ User authenticatedUser = User.builder().id(userId).groups(new Groups[]{Groups.FAMILY}).build();
+ UserEntity dummyEntity = UserEntity.builder().userId(userId).lowercaseUsername("password").build();
+
+ long[] groups = new long[]{0};
+ userRegisterForm.setGroupIds(groups);
+ when(userRepositoryMock.findByUserId(userId)).thenReturn(dummyEntity);
+ when(groupRepositoryMock.getGroupsByIds(groups)).thenReturn(new Groups[]{Groups.FAMILY});
+ assertDoesNotThrow(() -> userBusinessService.updateUser(userId, userRegisterForm, authenticatedUser));
+ }
}
\ 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 da99ac80..95abde30 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
@@ -75,14 +75,13 @@ void getUserInfoWithAccessToken() {
@Test
void updateUserWithAccessToken() {
- User user = User.builder().id(420).groups(null).username("kevin").build();
- ResponseEntity expectedUser = new ResponseEntity<>(user, OK);
+ ResponseEntity expectedResponse = new ResponseEntity<>(new ServerResponse(CREATED, "uga"), CREATED);
UserRegisterForm userRegisterForm = UserRegisterForm.builder().build();
- when(userRestServiceMock.updateUserWithAccessToken(userRegisterForm, "token")).thenReturn(expectedUser);
- ResponseEntity actualUser = userRestController.updateUser("token", userRegisterForm);
+ when(userRestServiceMock.updateUserByUserIdAuthenticateWithAccessToken(userRegisterForm, 0, "token")).thenReturn(expectedResponse);
+ ResponseEntity actualResponse = userRestController.updateUser(0, "token", userRegisterForm);
- assertEquals(expectedUser, actualUser);
+ assertEquals(expectedResponse, actualResponse);
}
@Test
diff --git a/src/test/resources/UserEditInformation.feature b/src/test/resources/UserEditInformation.feature
index 5882d829..8b0bbf5e 100644
--- a/src/test/resources/UserEditInformation.feature
+++ b/src/test/resources/UserEditInformation.feature
@@ -1,51 +1,39 @@
-#Feature: Edit User Details
-# As a user
-# I want to be able to change my username and password
-#
-# Background:
-# Given database is empty
-# And user with id 1234 exists and has username "user", password "secure_password"
-# And accessToken with value "accessToken" exists for user 1234
-#
-# Scenario: Successful change of username
-# When user requests change of username with value "kangaroo" and accessToken "accessToken"
-# Then response contains key "message" and value "Username successfully changed."
-# And response status code is 201
-#
-# Scenario: Successful change of password
-# When user requests change of password with value "pig-system" and accessToken "accessToken" and id "1234"
-# Then response contains key "message" and value "Password successfully changed."
-# And response status code is 201
-#
-# Scenario: Failed change of username; new username equals old username
-# When user requests change of username with value "user" and accessToken "accessToken"
-# Then response contains key "message" and value "No changes."
-# And response status code is 409
-# And response contains key "status" and value "conflict"
-#
-# Scenario: Failed change of username; new username already assigned
-# Given user with id 1235 exists and has username "kangaroo", password "secure_password"
-# When user requests change of username with value "kangaroo" and accessToken "accessToken"
-# Then response contains key "message" and value "Username already assigned."
-# And response status code is 409
-# And response contains key "status" and value "conflict"
-#
-# Scenario: Failed change of password; new password equals old password
-# When user requests change of password with value "secure_password" and accessToken "accessToken" and id "1234"
-# Then response contains key "message" and value "No changes."
-# And response status code is 409
-# And response contains key "status" and value "conflict"
-#
-# Scenario: Failed change of password; new password contains username
-# When user requests change of password with value "user123" and accessToken "accessToken" and id "1234"
-# Then response contains key "message" and value "Username must not appear in password."
-# And response status code is 409
-# And response contains key "status" and value "conflict"
-#
-# Scenario: Failed change of password; new password appears in list of top 10k passwords
-# When user requests change of password with value "vietnam" and accessToken "accessToken" and id "1234"
-# Then response status code is 409
-# And response contains key "message" and value "Password must not appear in the top 10000 most common passwords."
-# And response contains key "status" and value "conflict"
-#
-# #https://github.com/iryndin/10K-Most-Popular-Passwords/blob/master/passwords.txt
\ No newline at end of file
+Feature: Edit User Details
+ As a user
+ I want to be able to change my username and password
+
+ Background:
+ Given database is empty
+ And user with id 1234 exists and has username "user", password "secure_password"
+ And accessToken with value "accessToken" exists for user 1234
+
+ Scenario: Successful change of username
+ When user requests change of username with value "kangaroo" userId 1234 and accessToken "accessToken"
+ Then response contains key "message" and value "User successfully updated."
+ And response contains key "status" and value "Created"
+ And response status code is 201
+
+ Scenario: Successful change of password
+ When user requests change of password with value "pigSystem1234" userId 1234 and accessToken "accessToken"
+ Then response contains key "message" and value "User successfully updated."
+ And response contains key "status" and value "Created"
+ And response status code is 201
+
+ Scenario: Failed change of username; new username already assigned
+ Given user with id 1235 exists and has username "kangaroo", password "secure_password"
+ When user requests change of username with value "kangaroo" userId 1234 and accessToken "accessToken"
+ Then response contains key "message" and value "User could not get updated. Username already taken."
+ And response status code is 409
+ And response contains key "status" and value "Conflict"
+
+ Scenario: Failed change of password; new password contains username
+ When user requests change of password with value "User123asd" userId 1234 and accessToken "accessToken"
+ Then response contains key "message" and value "User could not get updated. Username must not appear in password."
+ And response status code is 409
+ And response contains key "status" and value "Conflict"
+
+ Scenario: Failed change of user. No Changes
+ When user requests change of password with no changes, userId 1234 and accessToken "accessToken"
+ Then response contains key "message" and value "User could not get updated. No changes were made."
+ And response contains key "status" and value "Conflict"
+ And response status code is 409