Skip to content
This repository was archived by the owner on Apr 5, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .run/JUnit Tests.run.xml
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run only Unit Tests" type="JUnit" factoryName="JUnit">
<module name="RestApi" />
<useClassPathOnly />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<option name="ALTERNATIVE_JRE_PATH" value="11" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="pattern" />
<option name="PARAMETERS" value="" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject" />
</option>
<patterns>
<pattern testClass="^(.*)UnitTest$" />
</patterns>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
public class RestConfiguration {

//Custom static constants
public static final String BASE_API_URI = "/api/v1/";
public static final String BASE_API_URI = "/api/v1";
public static final String AUTHORIZATION_BASIC_PREFIX = "Basic: ";
public static final String AUTHORIZATION_BEARER_PREFIX = "Bearer: ";
public static final String FS_BASE_URI = "/filesystem/";
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/de/filefighter/rest/domain/common/Utils.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
package de.filefighter.rest.domain.common;

import de.filefighter.rest.rest.exceptions.RequestDidntMeetFormalRequirementsException;

public class Utils {

public static boolean stringIsValid(String s){
return !(null == s || s.isEmpty() || s.isBlank());
}

public static String validateAuthorizationHeader(String header, String testString){
if(!stringIsValid(testString))
throw new RequestDidntMeetFormalRequirementsException("Header does not contain a valid String.");

if (!testString.matches("^" + header + "[^\\s](.*)$"))
throw new RequestDidntMeetFormalRequirementsException("Header does not contain '" + header + "', or format is invalid.");
String[] split = testString.split(header);
return split[1];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
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;
import static de.filefighter.rest.domain.common.Utils.validateAuthorizationHeader;

@Service
public class AccessTokenBusinessService {
Expand Down Expand Up @@ -81,10 +82,13 @@ public AccessToken findAccessTokenByValue(String accessTokenValue) {
}


public String checkBearerHeader(String accessTokenValue) {
if (!accessTokenValue.matches("^" + AUTHORIZATION_BEARER_PREFIX + "[^\\s](.*)$"))
throw new RequestDidntMeetFormalRequirementsException("Header does not contain '" + AUTHORIZATION_BEARER_PREFIX + "', or format is invalid.");
return accessTokenValue.split(AUTHORIZATION_BEARER_PREFIX)[1];
public AccessToken validateAccessTokenValue(String accessTokenValue) {
String cleanValue = validateAuthorizationHeader(AUTHORIZATION_BEARER_PREFIX, accessTokenValue);
AccessTokenEntity accessTokenEntity = accessTokenRepository.findByValue(cleanValue);
if (null == accessTokenEntity)
throw new UserNotAuthenticatedException("AccessToken not found.");

return accessTokenDtoService.createDto(accessTokenEntity);
}

public String generateRandomTokenValue() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
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;
import de.filefighter.rest.domain.user.data.persistance.UserRepository;
import de.filefighter.rest.domain.user.exceptions.UserNotAuthenticatedException;
import de.filefighter.rest.rest.exceptions.RequestDidntMeetFormalRequirementsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;
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 {

private final UserRepository userRepository;
private final UserDtoService userDtoService;

private static final Logger LOG = LoggerFactory.getLogger(UserAuthorizationService.class);

public UserAuthorizationService(UserRepository userRepository, UserDtoService userDtoService) {
this.userRepository = userRepository;
this.userDtoService = userDtoService;
}

public User authenticateUserWithUsernameAndPassword(String base64encodedUserAndPassword) {
String decodedUsernameUndPassword;
try {
byte[] decodedValue = Base64.getDecoder().decode(base64encodedUserAndPassword);
decodedUsernameUndPassword = new String(decodedValue, StandardCharsets.UTF_8.toString());
} catch (UnsupportedEncodingException | IllegalArgumentException ex) {
LOG.warn("Found UnsupportedEncodingException in {}", base64encodedUserAndPassword);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe you shoud not log the password :)

throw new RuntimeException(ex);
}

String[] split = decodedUsernameUndPassword.strip().split(":");

if (split.length != 2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so ":" is not allowed in the password?

throw new RequestDidntMeetFormalRequirementsException("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 with this username and password.");

return userDtoService.createDto(userEntity);
}

public User authenticateUserWithRefreshToken(String refreshToken) {
UserEntity userEntity = userRepository.findByRefreshToken(refreshToken);
if (null == userEntity)
throw new UserNotAuthenticatedException("No user found for this Refresh Token.");

return userDtoService.createDto(userEntity);
}

public void authenticateUserWithAccessToken(AccessToken accessToken) {
UserEntity userEntity = userRepository.findByUserId(accessToken.getUserId());
if (null == userEntity)
throw new UserNotAuthenticatedException(accessToken.getUserId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ 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;
Expand All @@ -38,45 +36,19 @@ public long getUserCount() {
return userRepository.count();
}

public User getUserByUsernameAndPassword(String base64encodedUserAndPasswordWithHeaderPrefix) {
if (!stringIsValid(base64encodedUserAndPasswordWithHeaderPrefix))
throw new RequestDidntMeetFormalRequirementsException("Header was empty.");

//TODO: maybe filter unsupported characters?
if (!base64encodedUserAndPasswordWithHeaderPrefix.matches("^" + AUTHORIZATION_BASIC_PREFIX + "[^\\s](.*)$"))
throw new RequestDidntMeetFormalRequirementsException("Header does not contain '" + AUTHORIZATION_BASIC_PREFIX + "', or format is invalid.");

String[] split = base64encodedUserAndPasswordWithHeaderPrefix.split(AUTHORIZATION_BASIC_PREFIX);

base64encodedUserAndPasswordWithHeaderPrefix = split[1];
String decodedUsernameUndPassword;
try {
byte[] decodedValue = Base64.getDecoder().decode(base64encodedUserAndPasswordWithHeaderPrefix);
decodedUsernameUndPassword = new String(decodedValue, StandardCharsets.UTF_8.toString());
} catch (UnsupportedEncodingException | IllegalArgumentException ex) {
LOG.warn("Found UnsupportedEncodingException in {}", base64encodedUserAndPasswordWithHeaderPrefix);
throw new RuntimeException(ex);
public User getUserById(long userId) {
UserEntity userEntity = userRepository.findByUserId(userId);
if (null == userEntity) {
throw new UserNotFoundException(userId);
}

split = decodedUsernameUndPassword.strip().split(":");

if (split.length != 2)
throw new RequestDidntMeetFormalRequirementsException("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 with this username and password.");

return userDtoService.createDto(userEntity);
}

public RefreshToken getRefreshTokenForUser(User user) {
UserEntity userEntity = userRepository.findByUserIdAndUsername(user.getId(), user.getUsername());
if (null == userEntity)
throw new UserNotAuthenticatedException(user.getId());
throw new UserNotFoundException(user.getId());

String refreshTokenValue = userEntity.getRefreshToken();

Expand All @@ -90,33 +62,11 @@ public RefreshToken getRefreshTokenForUser(User user) {
.build();
}

public User getUserByRefreshTokenAndUserId(String refreshToken, long userId) {
if (!stringIsValid(refreshToken))
throw new RequestDidntMeetFormalRequirementsException("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(userId);

return userDtoService.createDto(userEntity);
}

public User findUserByUsername(String username) {
if (!stringIsValid(username))
throw new RequestDidntMeetFormalRequirementsException("Username was not valid.");

String lowercaseUsername = username.toLowerCase().replace(" ","");
String lowercaseUsername = username.toLowerCase().replace(" ", "");

UserEntity entity = userRepository.findByLowercaseUsername(lowercaseUsername);
if (null == entity)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
public interface UserRepository extends MongoRepository<UserEntity, String> {
UserEntity findByUserIdAndUsername(long userId, String username);
UserEntity findByUsernameAndPassword(String username, String password);
UserEntity findByRefreshTokenAndUserId(String refreshToken, long userId);
UserEntity findByRefreshToken(String refreshToken);
UserEntity findByUserId(long userId);
UserEntity findByLowercaseUsername(String lowercaseUsername);
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,40 +37,38 @@ public ResponseEntity<User> registerNewUser(
}

@GetMapping(USER_BASE_URI + "login")
public ResponseEntity<RefreshToken> loginUserWithUsernameAndPassword(
public ResponseEntity<RefreshToken> loginWithUsernameAndPassword(
@RequestHeader(value = "Authorization", defaultValue = AUTHORIZATION_BASIC_PREFIX + "S2V2aW46MTIzNA==") String base64encodedUserAndPassword) {

LOG.info("Requested Login.");
return userRestService.getRefreshTokenWithUsernameAndPassword(base64encodedUserAndPassword);
}

@GetMapping(USER_BASE_URI + "{userId}/login")
public ResponseEntity<AccessToken> getAccessTokenAndUserInfoByRefreshTokenAndUserId(
@PathVariable long userId,
@GetMapping(USER_BASE_URI + "auth")
public ResponseEntity<AccessToken> getAccessToken(
@RequestHeader(value = "Authorization", defaultValue = AUTHORIZATION_BEARER_PREFIX + "token") String refreshToken) {

LOG.info("Requested login for user {} with token {}.", userId, refreshToken);
return userRestService.getAccessTokenByRefreshTokenAndUserId(refreshToken, userId);
LOG.info("Requested login for token {}.", refreshToken);
return userRestService.getAccessTokenByRefreshToken(refreshToken);
}


@GetMapping(USER_BASE_URI + "{userId}/info")
public ResponseEntity<User> getUserInfoWithAccessToken(
public ResponseEntity<User> getUserInfo(
@PathVariable long userId,
@RequestHeader(value = "Authorization", defaultValue = AUTHORIZATION_BEARER_PREFIX + "token") String accessToken) {

LOG.info("Requested User {} with token {}.", userId, accessToken);
return userRestService.getUserByAccessTokenAndUserId(accessToken, userId);
return userRestService.getUserByUserIdAuthenticateWithAccessToken(accessToken, userId);
}

@PutMapping(USER_BASE_URI + "{userId}/edit")
public ResponseEntity<User> updateUserWithAccessToken(
@PathVariable long userId,
@PutMapping(USER_BASE_URI + "edit")
public ResponseEntity<User> updateUser(
@RequestHeader(value = "Authorization", defaultValue = AUTHORIZATION_BEARER_PREFIX + "token") String accessToken,
@RequestBody UserRegisterForm updatedUser) {

LOG.info("Updated User with the id {} and Token {}, with form {}.", userId, accessToken, updatedUser);
return userRestService.updateUserByAccessTokenAndUserId(updatedUser, accessToken, userId);
LOG.info("Updated User and Token {}, with form {}.", accessToken, updatedUser);
return userRestService.updateUserWithAccessToken(updatedUser, accessToken);
}

@GetMapping(USER_BASE_URI + "find")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,52 +1,61 @@
package de.filefighter.rest.domain.user.rest;

import de.filefighter.rest.configuration.RestConfiguration;
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;
import de.filefighter.rest.domain.user.business.UserAuthorizationService;
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;

import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BASIC_PREFIX;
import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BEARER_PREFIX;


@Service
public class UserRestService implements UserRestServiceInterface {

private final UserBusinessService userBusinessService;
private final UserAuthorizationService userAuthorizationService;
private final AccessTokenBusinessService accessTokenBusinessService;

public UserRestService(UserBusinessService userBusinessService, AccessTokenBusinessService accessTokenBusinessService) {
public UserRestService(UserBusinessService userBusinessService, UserAuthorizationService userAuthorizationService, AccessTokenBusinessService accessTokenBusinessService) {
this.userBusinessService = userBusinessService;
this.userAuthorizationService = userAuthorizationService;
this.accessTokenBusinessService = accessTokenBusinessService;
}

@Override
public ResponseEntity<User> getUserByAccessTokenAndUserId(String accessTokenValue, long userId) {
String cleanValue = accessTokenBusinessService.checkBearerHeader(accessTokenValue);
AccessToken accessToken = accessTokenBusinessService.findAccessTokenByValueAndUserId(cleanValue, userId);
User user = userBusinessService.getUserByAccessTokenAndUserId(accessToken, userId);
public ResponseEntity<User> getUserByUserIdAuthenticateWithAccessToken(String accessToken, long userId) {
AccessToken validAccessToken = accessTokenBusinessService.validateAccessTokenValue(accessToken);
userAuthorizationService.authenticateUserWithAccessToken(validAccessToken);
User user = userBusinessService.getUserById(userId);
return new ResponseEntity<>(user, HttpStatus.OK);
}

@Override
public ResponseEntity<RefreshToken> getRefreshTokenWithUsernameAndPassword(String base64encodedUserAndPassword) {
User user = userBusinessService.getUserByUsernameAndPassword(base64encodedUserAndPassword);
RefreshToken refreshToken = userBusinessService.getRefreshTokenForUser(user);
String cleanValue = Utils.validateAuthorizationHeader(AUTHORIZATION_BASIC_PREFIX, base64encodedUserAndPassword);
User authenticatedUser = userAuthorizationService.authenticateUserWithUsernameAndPassword(cleanValue);
RefreshToken refreshToken = userBusinessService.getRefreshTokenForUser(authenticatedUser);
return new ResponseEntity<>(refreshToken, HttpStatus.OK);
}

@Override
public ResponseEntity<AccessToken> getAccessTokenByRefreshTokenAndUserId(String refreshToken, long userId) {
String cleanValue = accessTokenBusinessService.checkBearerHeader(refreshToken);
User user = userBusinessService.getUserByRefreshTokenAndUserId(cleanValue, userId);
public ResponseEntity<AccessToken> getAccessTokenByRefreshToken(String refreshToken) {
String cleanValue = Utils.validateAuthorizationHeader(AUTHORIZATION_BEARER_PREFIX, refreshToken);
User user = userAuthorizationService.authenticateUserWithRefreshToken(cleanValue);
AccessToken accessToken = accessTokenBusinessService.getValidAccessTokenForUser(user);
return new ResponseEntity<>(accessToken, HttpStatus.OK);
}

@Override
public ResponseEntity<User> updateUserByAccessTokenAndUserId(UserRegisterForm updatedUser, String accessToken, long userId) {
public ResponseEntity<User> updateUserWithAccessToken(UserRegisterForm updatedUser, String accessToken) {
return null;
}

Expand All @@ -57,8 +66,8 @@ public ResponseEntity<User> registerNewUserWithAccessToken(UserRegisterForm newU

@Override
public ResponseEntity<User> findUserByUsernameAndAccessToken(String username, String accessToken) {
String cleanValue = accessTokenBusinessService.checkBearerHeader(accessToken);
AccessToken token = accessTokenBusinessService.findAccessTokenByValue(cleanValue);
AccessToken token = accessTokenBusinessService.validateAccessTokenValue(accessToken);
userAuthorizationService.authenticateUserWithAccessToken(token);
User foundUser = userBusinessService.findUserByUsername(username);
return new ResponseEntity<>(foundUser, HttpStatus.OK);
}
Expand Down
Loading