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

Commit a0b3a94

Browse files
FF-301 - Download Endpoint (#111)
* Added basic structure * FF-397 Download Cucumber Tests (#107) * FF-397 added basic download steps * FF-397 add more Screnarios for bad cases * updated basic structure with response header * Made Queries better * wrote the download logic * kinda works * fixed the paths again * Running Integrationtests * Removed nested class * Using cookies for download auth auth (#113) * add setting of token as cookie * add authenticationWithAccessToken method to AuthenticationService * Minor changes, renaming and fixing. Co-authored-by: open-schnick <[email protected]> * Download endpoint can now be used with cookies * Integration tested the cookie usage * Bumped up the version to v0.0.9 * Fixed a bug where the wrong folder was set as header Co-authored-by: valentin <[email protected]>
1 parent e9fc328 commit a0b3a94

29 files changed

+545
-68
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</parent>
1111
<groupId>de.filefighter</groupId>
1212
<artifactId>rest</artifactId>
13-
<version>0.0.8</version>
13+
<version>0.0.9</version>
1414
<name>RestApi</name>
1515
<description>RestApi for FileFighter</description>
1616

src/main/java/de/filefighter/rest/configuration/PrepareDataBase.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ CommandLineRunner initDataBaseDev(UserRepository userRepository, AccessTokenRepo
166166
log.error("Inserting Users " + MESSAGE_ON_FAILURE);
167167
}
168168

169-
if (fileSystemRepository.findAll().size() == 8) {
169+
if (fileSystemRepository.findAll().size() == 10) {
170170
log.info("Inserting FileSystemEntities " + MESSAGE_ON_SUCCESS);
171171
} else {
172172
log.error("Inserting FileSystemEntities " + MESSAGE_ON_FAILURE);
@@ -239,7 +239,7 @@ private void addDefaultAdminAndRuntimeUser(UserRepository userRepository, Passwo
239239
}
240240

241241
private void addTestingFileSystemItems(FileSystemRepository fileSystemRepository) {
242-
log.info("Inserting default fsItems:\n {}\n {}\n {}\n {}\n {}\n {}\n {}\n {}.",
242+
log.info("Inserting default fsItems:\n {}\n {}\n {}\n {}\n {}\n {}\n {}\n {}\n {}\n {}.",
243243
fileSystemRepository.save(FileSystemEntity.builder()
244244
.lastUpdatedBy(RUNTIME_USER_ID)
245245
.ownerId(1)
@@ -306,7 +306,7 @@ private void addTestingFileSystemItems(FileSystemRepository fileSystemRepository
306306
.typeId(FOLDER.getId())
307307
.editableFoGroupIds(new long[]{FAMILY.getGroupId()})
308308
.visibleForGroupIds(new long[]{FAMILY.getGroupId()})
309-
.itemIds(new long[]{4, 5, 6})
309+
.itemIds(new long[]{4, 5, 6, 8})
310310
.build()),
311311
fileSystemRepository.save(FileSystemEntity.builder()
312312
.lastUpdatedBy(1)
@@ -344,6 +344,29 @@ private void addTestingFileSystemItems(FileSystemRepository fileSystemRepository
344344
.size(1232)
345345
.typeId(VIDEO.getId())
346346
.mimeType("video/mp4")
347+
.build()),
348+
fileSystemRepository.save(FileSystemEntity.builder()
349+
.lastUpdatedBy(1)
350+
.lastUpdated(Instant.now().getEpochSecond())
351+
.ownerId(1)
352+
.fileSystemId(8)
353+
.isFile(false)
354+
.path("/somefolder/folder")
355+
.name("folder")
356+
.size(1232)
357+
.typeId(FOLDER.getId())
358+
.itemIds(new long[]{9})
359+
.build()),
360+
fileSystemRepository.save(FileSystemEntity.builder()
361+
.lastUpdatedBy(1)
362+
.lastUpdated(Instant.now().getEpochSecond())
363+
.ownerId(1)
364+
.fileSystemId(9)
365+
.isFile(true)
366+
.name("anotherVideo.mp4")
367+
.size(1232)
368+
.typeId(VIDEO.getId())
369+
.mimeType("video/mp4")
347370
.build())
348371
);
349372
}

src/main/java/de/filefighter/rest/configuration/RestConfiguration.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@ public class RestConfiguration {
66
public static final String BASE_API_URI = "v1";
77
public static final String AUTHORIZATION_BASIC_PREFIX = "Basic ";
88
public static final String AUTHORIZATION_BEARER_PREFIX = "Bearer ";
9+
public static final String AUTHORIZATION_ACCESS_TOKEN_COOKIE = "token";
910
public static final String FS_BASE_URI = "/filesystem/";
1011
public static final String FS_PATH_HEADER = "X-FF-PATH";
1112
public static final String FS_CURRENT_ID_HEADER = "X-FF-CURRENT";
13+
public static final String FS_DOWNLOAD_NAME_HEADER = "X-FF-NAME";
1214
public static final String USER_BASE_URI = "/users/";
1315
public static final String DEFAULT_ERROR_URI = "/error";
1416
public static final long RUNTIME_USER_ID = 0;
1517

1618
private RestConfiguration() {
1719
// Cannot be instantiated.
1820
}
19-
}
21+
}

src/main/java/de/filefighter/rest/domain/authentication/AuthenticationService.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
package de.filefighter.rest.domain.authentication;
22

33
import de.filefighter.rest.domain.common.InputSanitizerService;
4+
import de.filefighter.rest.domain.common.Pair;
5+
import de.filefighter.rest.domain.common.exceptions.RequestDidntMeetFormalRequirementsException;
46
import de.filefighter.rest.domain.token.business.AccessTokenBusinessService;
57
import de.filefighter.rest.domain.token.data.dto.AccessToken;
68
import de.filefighter.rest.domain.user.data.dto.User;
9+
import de.filefighter.rest.domain.user.exceptions.UserNotAuthenticatedException;
710
import de.filefighter.rest.domain.user.group.Group;
11+
import lombok.extern.log4j.Log4j2;
812
import org.springframework.stereotype.Service;
913

1014
import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BASIC_PREFIX;
1115
import static de.filefighter.rest.configuration.RestConfiguration.AUTHORIZATION_BEARER_PREFIX;
1216

1317
@Service
18+
@Log4j2
1419
public class AuthenticationService implements AuthenticationServiceInterface {
1520

1621
private final AuthenticationBusinessService authenticationBusinessService;
@@ -37,6 +42,13 @@ public User bearerAuthenticationWithAccessToken(String accessTokenWithHeader) {
3742
return authenticationBusinessService.authenticateUserWithAccessToken(accessToken);
3843
}
3944

45+
@Override
46+
public User cookieAuthenticationWithAccessToken(String accessTokenFromCookie) {
47+
String sanitizedTokenString = inputSanitizerService.sanitizeTokenValue(accessTokenFromCookie);
48+
AccessToken validAccessToken = accessTokenBusinessService.findAccessTokenByValue(sanitizedTokenString);
49+
return authenticationBusinessService.authenticateUserWithAccessToken(validAccessToken);
50+
}
51+
4052
@Override
4153
public User bearerAuthenticationWithRefreshToken(String refreshTokenWithHeader) {
4254
String sanitizedHeaderValue = inputSanitizerService.sanitizeRequestHeader(AUTHORIZATION_BEARER_PREFIX, refreshTokenWithHeader);
@@ -51,4 +63,25 @@ public void bearerAuthenticationWithAccessTokenAndGroup(String accessTokenWithHe
5163
AccessToken validAccessToken = accessTokenBusinessService.findAccessTokenByValue(sanitizedTokenString);
5264
authenticationBusinessService.authenticateUserWithAccessTokenAndGroup(validAccessToken, group);
5365
}
66+
67+
@Override
68+
public User authenticateUserWithCookieOrHeader(Pair<String, String> accessTokenValueOrHeader) {
69+
String tokenFromCookie = accessTokenValueOrHeader.getFirst();
70+
String tokenFromHeader = accessTokenValueOrHeader.getSecond();
71+
72+
User authenticatedUser = null;
73+
try {
74+
authenticatedUser = this.bearerAuthenticationWithAccessToken(tokenFromHeader);
75+
} catch (RequestDidntMeetFormalRequirementsException ex) {
76+
log.debug("Header {} was not valid. Trying cookies next...", tokenFromHeader);
77+
78+
try {
79+
authenticatedUser = this.cookieAuthenticationWithAccessToken(tokenFromCookie);
80+
} catch (RequestDidntMeetFormalRequirementsException exception) {
81+
log.debug("Cookie {} was also not valid. Throwing Exception...", tokenFromCookie);
82+
throw new UserNotAuthenticatedException("No user found with this authentication.");
83+
}
84+
}
85+
return authenticatedUser;
86+
}
5487
}

src/main/java/de/filefighter/rest/domain/authentication/AuthenticationServiceInterface.java

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

3+
import de.filefighter.rest.domain.common.Pair;
34
import de.filefighter.rest.domain.user.data.dto.User;
45
import de.filefighter.rest.domain.user.group.Group;
56

@@ -8,7 +9,11 @@ public interface AuthenticationServiceInterface {
89

910
User bearerAuthenticationWithAccessToken(String accessTokenWithHeader);
1011

12+
User cookieAuthenticationWithAccessToken(String accessToken);
13+
1114
User bearerAuthenticationWithRefreshToken(String refreshTokenWithHeader);
1215

1316
void bearerAuthenticationWithAccessTokenAndGroup(String accessTokenWithHeader, Group group);
17+
18+
User authenticateUserWithCookieOrHeader(Pair<String, String> accessTokenValueOrHeader);
1419
}

src/main/java/de/filefighter/rest/domain/filesystem/business/FileSystemBusinessService.java

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,31 @@
88
import de.filefighter.rest.domain.filesystem.data.persistence.FileSystemRepository;
99
import de.filefighter.rest.domain.filesystem.exceptions.FileSystemContentsNotAccessibleException;
1010
import de.filefighter.rest.domain.filesystem.exceptions.FileSystemItemCouldNotBeDeletedException;
11+
import de.filefighter.rest.domain.filesystem.exceptions.FileSystemItemCouldNotBeDownloadedException;
1112
import de.filefighter.rest.domain.filesystem.exceptions.FileSystemItemNotFoundException;
1213
import de.filefighter.rest.domain.filesystem.type.FileSystemType;
1314
import de.filefighter.rest.domain.filesystem.type.FileSystemTypeRepository;
1415
import de.filefighter.rest.domain.user.business.UserBusinessService;
1516
import de.filefighter.rest.domain.user.data.dto.User;
1617
import de.filefighter.rest.domain.user.exceptions.UserNotFoundException;
17-
import lombok.AllArgsConstructor;
1818
import lombok.extern.log4j.Log4j2;
1919
import org.springframework.stereotype.Service;
2020

2121
import java.util.ArrayList;
2222
import java.util.List;
23+
import java.util.Objects;
24+
import java.util.stream.Collectors;
2325

2426
@Log4j2
2527
@Service
2628
public class FileSystemBusinessService {
2729

30+
public static final String DELETION_FAILED_MSG = "Failed to delete FileSystemEntity with id ";
2831
private final FileSystemRepository fileSystemRepository;
2932
private final FileSystemHelperService fileSystemHelperService;
3033
private final FileSystemTypeRepository fileSystemTypeRepository;
3134
private final UserBusinessService userBusinessService;
3235

33-
public static final String DELETION_FAILED_MSG = "Failed to delete FileSystemEntity with id ";
34-
3536
public FileSystemBusinessService(FileSystemRepository fileSystemRepository, FileSystemHelperService fileSystemHelperService, FileSystemTypeRepository fileSystemTypeRepository, UserBusinessService userBusinessService) {
3637
this.fileSystemRepository = fileSystemRepository;
3738
this.fileSystemHelperService = fileSystemHelperService;
@@ -158,7 +159,7 @@ public List<FileSystemItem> deleteFileSystemItemById(long fsItemId, User authent
158159
return returnList;
159160
}
160161

161-
private RecursiveReturn recursivlyDeleteFileSystemEntity(FileSystemEntity parentEntity, User authenticatedUser, ArrayList<FileSystemItem> returnList) {
162+
private Pair<Boolean, Boolean> recursivlyDeleteFileSystemEntity(FileSystemEntity parentEntity, User authenticatedUser, ArrayList<FileSystemItem> returnList) {
162163
boolean foundNonDeletable = false;
163164
boolean foundInvisible = false;
164165

@@ -173,9 +174,9 @@ private RecursiveReturn recursivlyDeleteFileSystemEntity(FileSystemEntity parent
173174
for (FileSystemEntity item : items) {
174175
if (fileSystemHelperService.userIsAllowedToInteractWithFileSystemEntity(item, authenticatedUser, InteractionType.READ)) {
175176
if (fileSystemHelperService.userIsAllowedToInteractWithFileSystemEntity(item, authenticatedUser, InteractionType.DELETE)) {
176-
RecursiveReturn recursiveReturn = recursivlyDeleteFileSystemEntity(item, authenticatedUser, returnList);
177-
foundInvisible = recursiveReturn.foundInvisibleEntities || foundInvisible;
178-
foundNonDeletable = recursiveReturn.foundNonDeletableEntities || foundNonDeletable;
177+
Pair<Boolean, Boolean> recursiveReturn = recursivlyDeleteFileSystemEntity(item, authenticatedUser, returnList);
178+
foundInvisible = recursiveReturn.getFirst() || foundInvisible;
179+
foundNonDeletable = recursiveReturn.getSecond() || foundNonDeletable;
179180
} else {
180181
// a entity could not be removed disable the deletion of the parent folder. (current Entity)
181182
foundNonDeletable = true;
@@ -206,12 +207,57 @@ private RecursiveReturn recursivlyDeleteFileSystemEntity(FileSystemEntity parent
206207
returnList.add(fileSystemHelperService.createDTO(parentEntity, authenticatedUser, null));
207208
}
208209
}
209-
return new RecursiveReturn(foundInvisible, foundNonDeletable);
210+
return new Pair<>(foundInvisible, foundNonDeletable);
210211
}
211212

212-
@AllArgsConstructor
213-
private static class RecursiveReturn {
214-
private final boolean foundInvisibleEntities;
215-
private final boolean foundNonDeletableEntities;
213+
public Pair<List<FileSystemItem>, String> downloadFileSystemEntity(List<Long> ids, User authenticatedUser) {
214+
// validate input and check for parent
215+
if (ids.isEmpty())
216+
return new Pair<>(new ArrayList<>(), null);
217+
218+
List<FileSystemEntity> uncheckedEntities = ids.stream()
219+
.filter(Objects::nonNull)
220+
.map(id -> {
221+
FileSystemEntity possibleEntity = fileSystemRepository.findByFileSystemId(id);
222+
if (null == possibleEntity)
223+
throw new FileSystemItemCouldNotBeDownloadedException("FileSystemEntity does not exist or you are not allowed to see the entity.");
224+
225+
return possibleEntity;
226+
}).collect(Collectors.toList());
227+
228+
List<FileSystemEntity> checkedEntities = uncheckedEntities.stream()
229+
.filter(entity -> fileSystemHelperService.userIsAllowedToInteractWithFileSystemEntity(entity, authenticatedUser, InteractionType.READ))
230+
.collect(Collectors.toList());
231+
232+
if (checkedEntities.size() != uncheckedEntities.size()) {
233+
log.debug("Entities size and ids size does not match after validation. pre: {} / after: {}", uncheckedEntities.size(), checkedEntities.size());
234+
throw new FileSystemItemCouldNotBeDownloadedException("FileSystemEntity does not exist or you are not allowed to see the entity.");
235+
}
236+
237+
boolean allEntitiesAreInRoot = checkedEntities.stream().allMatch(entity -> !entity.isFile() && entity.getPath().equals("/"));
238+
boolean singleEntity = checkedEntities.size() == 1;
239+
240+
List<FileSystemItem> returnList = new ArrayList<>();
241+
String zipName;
242+
243+
if (singleEntity) {
244+
FileSystemEntity currentEntity = checkedEntities.get(0);
245+
zipName = fileSystemHelperService.getNameOfZipWhenOnlyOneEntityNeedsToBeDownloaded(currentEntity, allEntitiesAreInRoot);
246+
fileSystemHelperService.getContentsOfFolderRecursivly(returnList, currentEntity, authenticatedUser, "", false);
247+
248+
} else {
249+
zipName = fileSystemHelperService.getNameOfZipWhenMultipleEntitiesNeedToBeDownloaded(checkedEntities, allEntitiesAreInRoot);
250+
if (!allEntitiesAreInRoot) {
251+
long countOfDifferentParents = checkedEntities.stream()
252+
.map(entity -> fileSystemHelperService.getParentNameEntity().apply(entity))
253+
.distinct()
254+
.count();
255+
256+
if (countOfDifferentParents != 1)
257+
throw new FileSystemItemCouldNotBeDownloadedException("FileSystemEntity need to have a common parent entity.");
258+
}
259+
checkedEntities.forEach(entity -> fileSystemHelperService.getContentsOfFolderRecursivly(returnList, entity, authenticatedUser, "", true));
260+
}
261+
return new Pair<>(returnList, zipName);
216262
}
217263
}

0 commit comments

Comments
 (0)