Skip to content
This repository was archived by the owner on Apr 5, 2024. It is now read-only.

Commit c05ced3

Browse files
authored
FF-180 RestApi refactor input validation checks. (#47)
* FF-180 RestApi refactor input validation checks. * Added coverage checks. * Fixed Merge Bug.
1 parent a4f4f5d commit c05ced3

File tree

12 files changed

+219
-217
lines changed

12 files changed

+219
-217
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
docker start FileFighterDB
2727
-
2828
name: Run Tests and update Sonar
29-
run: mvn clean test sonar:sonar -s ./settings.xml
29+
run: mvn clean verify sonar:sonar -s ./settings.xml
3030
env:
3131
SONAR_LOGIN: ${{ secrets.SONAR_LOGIN }}
3232
SONAR_PASSWORD: ${{ secrets.SONAR_PASSWORD }}

pom.xml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,38 @@
153153
<goal>report</goal>
154154
</goals>
155155
</execution>
156+
<!-- attached to Maven verify phase -->
157+
<execution>
158+
<id>check</id>
159+
<phase>verify</phase>
160+
<goals>
161+
<goal>check</goal>
162+
</goals>
163+
<configuration>
164+
<rules>
165+
<rule>
166+
<element>CLASS</element>
167+
<includes>
168+
<include>*BusinessService</include>
169+
<include>*Controller</include>
170+
<include>*Service</include>
171+
</includes>
172+
<excludes>
173+
<!-- NOT IMPLEMENTED YET -->
174+
<exclude>*FileSystemRestService</exclude>
175+
<exclude>*PermissionRestService</exclude>
176+
</excludes>
177+
<limits>
178+
<limit>
179+
<counter>LINE</counter>
180+
<value>COVEREDRATIO</value>
181+
<minimum>95%</minimum>
182+
</limit>
183+
</limits>
184+
</rule>
185+
</rules>
186+
</configuration>
187+
</execution>
156188
</executions>
157189
</plugin>
158190
</plugins>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package de.filefighter.rest.domain.common;
2+
3+
import de.filefighter.rest.rest.exceptions.RequestDidntMeetFormalRequirementsException;
4+
import org.springframework.stereotype.Service;
5+
6+
@Service
7+
public class InputSanitizerService {
8+
9+
public static boolean stringIsValid(String s) {
10+
return !(null == s || s.isEmpty() || s.isBlank());
11+
}
12+
13+
/**
14+
*
15+
* Sanitizes a String, so it can be used.
16+
* @param string String that needs to be sanitized.
17+
* @return string without whitespaces and without illegal characters.
18+
* @throws RequestDidntMeetFormalRequirementsException when string was empty.
19+
*/
20+
public static String sanitizeString(String string) {
21+
if(!InputSanitizerService.stringIsValid(string))
22+
throw new RequestDidntMeetFormalRequirementsException("String was empty");
23+
return string.replaceAll("\\s", "");
24+
}
25+
26+
public String sanitizeRequestHeader(String header, String testString) {
27+
if (!(stringIsValid(testString) && stringIsValid(header)))
28+
throw new RequestDidntMeetFormalRequirementsException("Header does not contain a valid String.");
29+
30+
if (!testString.matches("^" + header + "[^\\s](.*)$"))
31+
throw new RequestDidntMeetFormalRequirementsException("Header does not contain '" + header + "', or format is invalid.");
32+
String[] split = testString.split(header);
33+
return split[1];
34+
}
35+
36+
public String sanitizeTokenValue(String tokenValue){
37+
return InputSanitizerService.sanitizeString(tokenValue);
38+
}
39+
}

src/main/java/de/filefighter/rest/domain/common/Utils.java

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/main/java/de/filefighter/rest/domain/token/business/AccessTokenBusinessService.java

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,13 @@
66
import de.filefighter.rest.domain.user.data.dto.User;
77
import de.filefighter.rest.domain.user.exceptions.UserNotAuthenticatedException;
88
import de.filefighter.rest.rest.exceptions.FileFighterDataException;
9-
import de.filefighter.rest.rest.exceptions.RequestDidntMeetFormalRequirementsException;
109
import org.slf4j.Logger;
1110
import org.slf4j.LoggerFactory;
1211
import org.springframework.stereotype.Service;
1312

1413
import java.time.Instant;
1514
import java.util.UUID;
1615

17-
import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BEARER_PREFIX;
18-
import static de.filefighter.rest.domain.common.Utils.stringIsValid;
19-
import static de.filefighter.rest.domain.common.Utils.validateAuthorizationHeader;
20-
2116
@Service
2217
public class AccessTokenBusinessService {
2318

@@ -67,9 +62,6 @@ public AccessToken getValidAccessTokenForUser(User user) {
6762
}
6863

6964
public AccessToken findAccessTokenByValueAndUserId(String accessTokenValue, long userId) {
70-
if (!stringIsValid(accessTokenValue))
71-
throw new RequestDidntMeetFormalRequirementsException("Value of AccessToken was not valid.");
72-
7365
AccessTokenEntity accessTokenEntity = accessTokenRepository.findByUserIdAndValue(userId, accessTokenValue);
7466
if (null == accessTokenEntity)
7567
throw new UserNotAuthenticatedException(userId);
@@ -78,26 +70,13 @@ public AccessToken findAccessTokenByValueAndUserId(String accessTokenValue, long
7870
}
7971

8072
public AccessToken findAccessTokenByValue(String accessTokenValue) {
81-
if (!stringIsValid(accessTokenValue))
82-
throw new RequestDidntMeetFormalRequirementsException("Value of AccessToken was not valid.");
83-
8473
AccessTokenEntity accessTokenEntity = accessTokenRepository.findByValue(accessTokenValue);
8574
if (null == accessTokenEntity)
8675
throw new UserNotAuthenticatedException("AccessToken not found.");
8776

8877
return accessTokenDtoService.createDto(accessTokenEntity);
8978
}
9079

91-
92-
public AccessToken validateAccessTokenValueWithHeader(String accessTokenValue) {
93-
String cleanValue = validateAuthorizationHeader(AUTHORIZATION_BEARER_PREFIX, accessTokenValue);
94-
AccessTokenEntity accessTokenEntity = accessTokenRepository.findByValue(cleanValue);
95-
if (null == accessTokenEntity)
96-
throw new UserNotAuthenticatedException("AccessToken not found.");
97-
98-
return accessTokenDtoService.createDto(accessTokenEntity);
99-
}
100-
10180
public static String generateRandomTokenValue() {
10281
return UUID.randomUUID().toString();
10382
}

src/main/java/de/filefighter/rest/domain/user/business/UserAuthorizationService.java

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package de.filefighter.rest.domain.user.business;
22

3-
import de.filefighter.rest.domain.common.Utils;
3+
import de.filefighter.rest.domain.common.InputSanitizerService;
44
import de.filefighter.rest.domain.token.data.dto.AccessToken;
55
import de.filefighter.rest.domain.user.data.dto.User;
66
import de.filefighter.rest.domain.user.data.persistance.UserEntity;
@@ -15,10 +15,6 @@
1515
import java.nio.charset.StandardCharsets;
1616
import java.util.Base64;
1717

18-
import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BASIC_PREFIX;
19-
import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BEARER_PREFIX;
20-
import static de.filefighter.rest.domain.common.Utils.removeWhiteSpaces;
21-
2218
@Service
2319
public class UserAuthorizationService {
2420

@@ -32,9 +28,7 @@ public UserAuthorizationService(UserRepository userRepository, UserDtoService us
3228
this.userDtoService = userDtoService;
3329
}
3430

35-
public User authenticateUserWithUsernameAndPassword(String base64encodedUserAndPasswordWithHeader) {
36-
String base64encodedUserAndPassword = Utils.validateAuthorizationHeader(AUTHORIZATION_BASIC_PREFIX, base64encodedUserAndPasswordWithHeader);
37-
31+
public User authenticateUserWithUsernameAndPassword(String base64encodedUserAndPassword) {
3832
String decodedUsernameAndPassword = "";
3933
try {
4034
byte[] decodedValue = Base64.getDecoder().decode(base64encodedUserAndPassword);
@@ -49,8 +43,8 @@ public User authenticateUserWithUsernameAndPassword(String base64encodedUserAndP
4943
if (split.length != 2)
5044
throw new RequestDidntMeetFormalRequirementsException("Credentials didnt meet formal requirements.");
5145

52-
String lowerCaseUsername = removeWhiteSpaces(split[0].toLowerCase()); //no nullPointerException possible here.
53-
String password = split[1];
46+
String lowerCaseUsername = InputSanitizerService.sanitizeString(split[0].toLowerCase());
47+
String password = InputSanitizerService.sanitizeString(split[1]);
5448

5549
UserEntity userEntity = userRepository.findByLowercaseUsernameAndPassword(lowerCaseUsername, password);
5650
if (null == userEntity)
@@ -60,8 +54,7 @@ public User authenticateUserWithUsernameAndPassword(String base64encodedUserAndP
6054
}
6155

6256
public User authenticateUserWithRefreshToken(String refreshToken) {
63-
String cleanValue = Utils.validateAuthorizationHeader(AUTHORIZATION_BEARER_PREFIX, refreshToken);
64-
UserEntity userEntity = userRepository.findByRefreshToken(cleanValue);
57+
UserEntity userEntity = userRepository.findByRefreshToken(refreshToken);
6558
if (null == userEntity)
6659
throw new UserNotAuthenticatedException("No user found for this Refresh Token.");
6760

src/main/java/de/filefighter/rest/domain/user/business/UserBusinessService.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package de.filefighter.rest.domain.user.business;
22

3-
import de.filefighter.rest.domain.common.Utils;
43
import de.filefighter.rest.domain.token.business.AccessTokenBusinessService;
54
import de.filefighter.rest.domain.token.data.dto.RefreshToken;
65
import de.filefighter.rest.domain.user.data.dto.User;
@@ -26,8 +25,8 @@
2625
import java.util.Arrays;
2726
import java.util.regex.Pattern;
2827

29-
import static de.filefighter.rest.domain.common.Utils.removeWhiteSpaces;
30-
import static de.filefighter.rest.domain.common.Utils.stringIsValid;
28+
import static de.filefighter.rest.domain.common.InputSanitizerService.sanitizeString;
29+
import static de.filefighter.rest.domain.common.InputSanitizerService.stringIsValid;
3130

3231
@Service
3332
public class UserBusinessService {
@@ -37,7 +36,6 @@ public class UserBusinessService {
3736
private final GroupRepository groupRepository;
3837
private final MongoTemplate mongoTemplate;
3938

40-
4139
private static final Logger LOG = LoggerFactory.getLogger(UserBusinessService.class);
4240
public static final int USER_ID_MAX = 99999999;
4341

@@ -86,7 +84,7 @@ public User findUserByUsername(String username) {
8684
if (!stringIsValid(username))
8785
throw new RequestDidntMeetFormalRequirementsException("Username was not valid.");
8886

89-
String lowercaseUsername = removeWhiteSpaces(username.toLowerCase());
87+
String lowercaseUsername = sanitizeString(username.toLowerCase());
9088

9189
UserEntity entity = userRepository.findByLowercaseUsername(lowercaseUsername);
9290
if (null == entity)
@@ -146,7 +144,7 @@ public void registerNewUser(UserRegisterForm newUser) {
146144
}
147145

148146
public boolean passwordIsValid(String password) {
149-
if (!Utils.stringIsValid(password))
147+
if (!stringIsValid(password))
150148
return false;
151149

152150
if (this.passwordCheckDisabled) return true;

src/main/java/de/filefighter/rest/domain/user/rest/UserRestService.java

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package de.filefighter.rest.domain.user.rest;
22

3+
import de.filefighter.rest.domain.common.InputSanitizerService;
34
import de.filefighter.rest.domain.token.business.AccessTokenBusinessService;
45
import de.filefighter.rest.domain.token.data.dto.AccessToken;
56
import de.filefighter.rest.domain.token.data.dto.RefreshToken;
@@ -12,6 +13,8 @@
1213
import org.springframework.http.ResponseEntity;
1314
import org.springframework.stereotype.Service;
1415

16+
import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BASIC_PREFIX;
17+
import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BEARER_PREFIX;
1518
import static de.filefighter.rest.domain.user.group.Groups.ADMIN;
1619

1720

@@ -21,56 +24,75 @@ public class UserRestService implements UserRestServiceInterface {
2124
private final UserBusinessService userBusinessService;
2225
private final UserAuthorizationService userAuthorizationService;
2326
private final AccessTokenBusinessService accessTokenBusinessService;
27+
private final InputSanitizerService inputSanitizerService;
2428

25-
public UserRestService(UserBusinessService userBusinessService, UserAuthorizationService userAuthorizationService, AccessTokenBusinessService accessTokenBusinessService) {
29+
public UserRestService(UserBusinessService userBusinessService, UserAuthorizationService userAuthorizationService, AccessTokenBusinessService accessTokenBusinessService, InputSanitizerService inputSanitizerService) {
2630
this.userBusinessService = userBusinessService;
2731
this.userAuthorizationService = userAuthorizationService;
2832
this.accessTokenBusinessService = accessTokenBusinessService;
33+
this.inputSanitizerService = inputSanitizerService;
2934
}
3035

3136
@Override
32-
public ResponseEntity<User> getUserByUserIdAuthenticateWithAccessToken(String accessToken, long userId) {
33-
AccessToken validAccessToken = accessTokenBusinessService.validateAccessTokenValueWithHeader(accessToken);
37+
public ResponseEntity<User> getUserByUserIdAuthenticateWithAccessToken(String accessTokenWithHeader, long userId) {
38+
String sanitizedHeaderValue = inputSanitizerService.sanitizeRequestHeader(AUTHORIZATION_BEARER_PREFIX, accessTokenWithHeader);
39+
String sanitizedTokenString = inputSanitizerService.sanitizeTokenValue(sanitizedHeaderValue);
40+
41+
AccessToken validAccessToken = accessTokenBusinessService.findAccessTokenByValue(sanitizedTokenString);
3442
userAuthorizationService.authenticateUserWithAccessToken(validAccessToken);
3543
User user = userBusinessService.getUserById(userId);
3644
return new ResponseEntity<>(user, HttpStatus.OK);
3745
}
3846

3947
@Override
4048
public ResponseEntity<RefreshToken> getRefreshTokenWithUsernameAndPassword(String base64encodedUserAndPasswordWithHeader) {
41-
User authenticatedUser = userAuthorizationService.authenticateUserWithUsernameAndPassword(base64encodedUserAndPasswordWithHeader);
49+
String sanitizedHeaderValue = inputSanitizerService.sanitizeRequestHeader(AUTHORIZATION_BASIC_PREFIX, base64encodedUserAndPasswordWithHeader);
50+
51+
User authenticatedUser = userAuthorizationService.authenticateUserWithUsernameAndPassword(sanitizedHeaderValue);
4252
RefreshToken refreshToken = userBusinessService.getRefreshTokenForUser(authenticatedUser);
4353
return new ResponseEntity<>(refreshToken, HttpStatus.OK);
4454
}
4555

4656
@Override
4757
public ResponseEntity<AccessToken> getAccessTokenByRefreshToken(String refreshTokenWithHeader) {
48-
User user = userAuthorizationService.authenticateUserWithRefreshToken(refreshTokenWithHeader);
58+
String sanitizedHeaderValue = inputSanitizerService.sanitizeRequestHeader(AUTHORIZATION_BEARER_PREFIX, refreshTokenWithHeader);
59+
String sanitizedTokenString = inputSanitizerService.sanitizeTokenValue(sanitizedHeaderValue);
60+
61+
User user = userAuthorizationService.authenticateUserWithRefreshToken(sanitizedTokenString);
4962
AccessToken accessToken = accessTokenBusinessService.getValidAccessTokenForUser(user);
5063
return new ResponseEntity<>(accessToken, HttpStatus.OK);
5164
}
5265

5366
@Override
54-
public ResponseEntity<ServerResponse> updateUserByUserIdAuthenticateWithAccessToken(UserRegisterForm updatedUser, long userId, String accessTokenValue) {
55-
AccessToken accessToken = accessTokenBusinessService.validateAccessTokenValueWithHeader(accessTokenValue);
67+
public ResponseEntity<ServerResponse> updateUserByUserIdAuthenticateWithAccessToken(UserRegisterForm updatedUser, long userId, String accessTokenHeader) {
68+
String sanitizedHeaderValue = inputSanitizerService.sanitizeRequestHeader(AUTHORIZATION_BEARER_PREFIX, accessTokenHeader);
69+
String sanitizedTokenString = inputSanitizerService.sanitizeTokenValue(sanitizedHeaderValue);
70+
71+
AccessToken accessToken = accessTokenBusinessService.findAccessTokenByValue(sanitizedTokenString);
5672
User authenticatedUser = userAuthorizationService.authenticateUserWithAccessToken(accessToken);
5773
userBusinessService.updateUser(userId, updatedUser, authenticatedUser);
5874
ServerResponse response = new ServerResponse(HttpStatus.CREATED, "User successfully updated.");
5975
return new ResponseEntity<>(response, HttpStatus.CREATED);
6076
}
6177

6278
@Override
63-
public ResponseEntity<ServerResponse> registerNewUserWithAccessToken(UserRegisterForm newUser, String accessToken) {
64-
AccessToken validAccessToken = accessTokenBusinessService.validateAccessTokenValueWithHeader(accessToken);
79+
public ResponseEntity<ServerResponse> registerNewUserWithAccessToken(UserRegisterForm newUser, String accessTokenHeader) {
80+
String sanitizedHeaderValue = inputSanitizerService.sanitizeRequestHeader(AUTHORIZATION_BEARER_PREFIX, accessTokenHeader);
81+
String sanitizedTokenString = inputSanitizerService.sanitizeTokenValue(sanitizedHeaderValue);
82+
83+
AccessToken validAccessToken = accessTokenBusinessService.findAccessTokenByValue(sanitizedTokenString);
6584
userAuthorizationService.authenticateUserWithAccessTokenAndGroup(validAccessToken, ADMIN);
6685
userBusinessService.registerNewUser(newUser);
6786
return new ResponseEntity<>(new ServerResponse(HttpStatus.CREATED, "User successfully created."), HttpStatus.CREATED);
6887
}
6988

7089
@Override
71-
public ResponseEntity<User> findUserByUsernameAndAccessToken(String username, String accessToken) {
72-
AccessToken token = accessTokenBusinessService.validateAccessTokenValueWithHeader(accessToken);
73-
userAuthorizationService.authenticateUserWithAccessToken(token);
90+
public ResponseEntity<User> findUserByUsernameAndAccessToken(String username, String accessTokenHeader) {
91+
String sanitizedHeaderValue = inputSanitizerService.sanitizeRequestHeader(AUTHORIZATION_BEARER_PREFIX, accessTokenHeader);
92+
String sanitizedTokenString = inputSanitizerService.sanitizeTokenValue(sanitizedHeaderValue);
93+
94+
AccessToken accessToken = accessTokenBusinessService.findAccessTokenByValue(sanitizedTokenString);
95+
userAuthorizationService.authenticateUserWithAccessToken(accessToken);
7496
User foundUser = userBusinessService.findUserByUsername(username);
7597
return new ResponseEntity<>(foundUser, HttpStatus.OK);
7698
}

0 commit comments

Comments
 (0)