diff --git a/.github/workflows/build-test-dev-master.yml b/.github/workflows/build-test-dev-master.yml index 92cffc8..cc3d763 100644 --- a/.github/workflows/build-test-dev-master.yml +++ b/.github/workflows/build-test-dev-master.yml @@ -21,5 +21,13 @@ jobs: key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - - name: Run docker - run: docker-compose build + - name: Set up JDK 15 + uses: actions/setup-java@v2 + with: + java-version: '15' + distribution: 'adopt' + - name: Test code + run: | + cd scripts + ls + bash test.sh diff --git a/.github/workflows/docker-publish-develop.yml b/.github/workflows/docker-publish-develop.yml index 2aa6f5c..6bffb46 100644 --- a/.github/workflows/docker-publish-develop.yml +++ b/.github/workflows/docker-publish-develop.yml @@ -20,6 +20,11 @@ jobs: env: CR_PAT: ${{ secrets.CR_PAT }} + - name: Run tests + run: | + cd scripts + bash test.sh + - name: Build image run: docker build . --file Dockerfile --tag $IMAGE_NAME diff --git a/.github/workflows/docker-publish-master.yml b/.github/workflows/docker-publish-master.yml index 690eac4..728861a 100644 --- a/.github/workflows/docker-publish-master.yml +++ b/.github/workflows/docker-publish-master.yml @@ -18,6 +18,11 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Run tests + run: | + cd scripts + bash test.sh + - name: Build image run: docker build . --file Dockerfile --tag $IMAGE_NAME diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml new file mode 100644 index 0000000..b84f07d --- /dev/null +++ b/.github/workflows/sonarcloud.yml @@ -0,0 +1,38 @@ +name: SonarCloud +on: + push: + branches: [ master, develop ] + pull_request: + types: [opened, synchronize, reopened] +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Set up JDK 14 + uses: actions/setup-java@v1 + with: + java-version: 14 + - name: Cache SonarCloud packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache Maven packages + uses: actions/cache@v1 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: | + cd scripts + bash sonarcloud.sh + diff --git a/Dockerfile b/Dockerfile index 205ad6c..df01f86 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,14 @@ -FROM maven:3.6.3-openjdk-14 as build_step +FROM maven:3.8.1-openjdk-15 as build_step ADD ./pom.xml ./pom.xml RUN mvn dependency:go-offline -B ADD . /home/vaultionizer/project +ADD scripts/test.sh test.sh ADD scripts/build_project_docker.sh build_project.sh RUN bash build_project.sh -FROM openjdk:14-alpine +FROM openjdk:15-alpine COPY --from=build_step /home/vaultionizer/vaultionizer_server.jar /home/vaultionizer/vaultionizer_server.jar -EXPOSE 443 +EXPOSE 8080 ENTRYPOINT ["java", "-jar", "/home/vaultionizer/vaultionizer_server.jar"] diff --git a/README.md b/README.md index d41aa44..fc6b201 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ Hey, welcome to the Github repository for the backend for the [Vaultionizer Android application](https://github.com/Vaultionizer/vault-android-app). +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=Vaultionizer_vault-server&metric=ncloc)](https://sonarcloud.io/dashboard?id=Vaultionizer_vault-server)[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Vaultionizer_vault-server&metric=alert_status)](https://sonarcloud.io/dashboard?id=Vaultionizer_vault-server) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Vaultionizer_vault-server&metric=coverage)](https://sonarcloud.io/dashboard?id=Vaultionizer_vault-server)[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=Vaultionizer_vault-server&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=Vaultionizer_vault-server) + The application aims at enabling a user to securely store data online without having to fear the data being analyzed, breached or anything similar. Our goal is to put zero trust into server instances and instead rely solely on the user knowing what to do. Before reading any further, note that this application is explorative and should not be used in practice (since for debugging purposes, the data is not encrypted yet)! @@ -16,4 +19,4 @@ All semantic data (like filenames, contents etc.) is stored in a custom (JSON-ba The backend can easily be deployed using Docker and then used by the Android application. ## Swagger-API -For the current API, see [here](https://v2202006123966120989.bestsrv.de/swagger-ui.html#/). +For the current API, see [here](https://api.vault.jatsqi/swagger-ui.html#/). diff --git a/docker-compose.yml b/docker-compose.yml index 1d7fa7e..f2753fc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ services: image: vaultionizer_app build: ./ ports: - - "443:443" + - "8080:8080" depends_on: - postgres_vault environment: diff --git a/pom.xml b/pom.xml index f653360..7b1b22c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.0-M3 + 2.5.3 com.vaultionizer @@ -16,6 +16,9 @@ 14 + Vaultionizer_vault-server + vaultionizer + https://sonarcloud.io @@ -50,18 +53,23 @@ io.springfox springfox-swagger2 - 2.9.2 + 3.0.0 + + + org.springdoc + springdoc-openapi-ui + 1.5.10 io.springfox springfox-core - 2.9.2 + 3.0.0 io.springfox springfox-swagger-ui - 2.9.2 + 3.0.0 org.postgresql @@ -86,7 +94,12 @@ org.json json - 20171018 + 20231013 + + + org.springframework + spring-tx + 5.3.9 @@ -97,40 +110,46 @@ org.webjars sockjs-client - 1.0.2 + 1.5.1 org.webjars stomp-websocket - 2.3.3 + 2.3.4 org.webjars bootstrap - 3.3.7 + 5.0.2 org.webjars jquery - 3.1.1-1 + 3.6.0 io.cucumber cucumber-java - 6.8.0 + 6.10.4 test io.cucumber cucumber-junit - 6.8.0 + 6.10.4 test io.cucumber cucumber-spring - 6.8.0 + 6.10.4 + test + + + org.junit.vintage + junit-vintage-engine + 5.7.2 test @@ -141,6 +160,26 @@ org.springframework.boot spring-boot-maven-plugin + + + org.jacoco + jacoco-maven-plugin + 0.8.7 + + + + prepare-agent + + + + report + test + + report + + + + diff --git a/scripts/build_project_docker.sh b/scripts/build_project_docker.sh index 239a3dd..6e928f2 100644 --- a/scripts/build_project_docker.sh +++ b/scripts/build_project_docker.sh @@ -3,12 +3,8 @@ cd /home/vaultionizer/project rm target/vaultserver* export MAVEN_OPTS="-Xmx1024m" -echo "installing dependencies..." -mvn clean install -o > /home/vaultionizer/log_install.txt -echo "Finished installing dependencies." - echo "Packaging project" -mvn package +mvn package -Dmaven.test.skip=true mv target/*jar ../vaultionizer_server.jar cd .. diff --git a/scripts/docker-compose.yml b/scripts/docker-compose.yml new file mode 100644 index 0000000..07be7d2 --- /dev/null +++ b/scripts/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3.1' +services: + postgres_vault_test: + image: postgres + ports: + - "5432:5432" + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=password + - POSTGRES_DB=mydb \ No newline at end of file diff --git a/scripts/sonarcloud.sh b/scripts/sonarcloud.sh new file mode 100644 index 0000000..2cca30b --- /dev/null +++ b/scripts/sonarcloud.sh @@ -0,0 +1,15 @@ +# start test vaultionizer postgres + +export VAULT_ENABLE_SSL=false +export VAULT_DB_USER=postgres +export VAULT_DB_PASSWORD=password +export VAULT_DB_DATABASE=mydb +export VAULT_DB_HOST=localhost:5432 + +docker-compose up --build -d + +cd .. +mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar +docker stop scripts_postgres_vault_test_1 +docker rm scripts_postgres_vault_test_1 + diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100644 index 0000000..e00908e --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,15 @@ +# start test vaultionizer postgres + +export VAULT_ENABLE_SSL=false +export VAULT_DB_USER=postgres +export VAULT_DB_PASSWORD=password +export VAULT_DB_DATABASE=mydb +export VAULT_DB_HOST=localhost:5432 + +docker-compose up --build -d + +cd .. +mvn test +docker stop scripts_postgres_vault_test_1 +docker rm scripts_postgres_vault_test_1 + diff --git a/src/main/java/com/vaultionizer/vaultserver/VaultserverApplication.java b/src/main/java/com/vaultionizer/vaultserver/VaultserverApplication.java index a0d4fb8..b610182 100644 --- a/src/main/java/com/vaultionizer/vaultserver/VaultserverApplication.java +++ b/src/main/java/com/vaultionizer/vaultserver/VaultserverApplication.java @@ -5,14 +5,14 @@ import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; -@SpringBootApplication(exclude = {SecurityAutoConfiguration.class }) +@SpringBootApplication(exclude = {SecurityAutoConfiguration.class}) @EnableJpaAuditing public class VaultserverApplication { - public static void main(String[] args) { - SpringApplication app = new SpringApplication(VaultserverApplication.class); - app.setAdditionalProfiles("ssl"); - app.run(args); - } + public static void main(String[] args) { + var app = new SpringApplication(VaultserverApplication.class); + app.setAdditionalProfiles("ssl"); + app.run(args); + } } diff --git a/src/main/java/com/vaultionizer/vaultserver/config/SecurityConfig.java b/src/main/java/com/vaultionizer/vaultserver/config/SecurityConfig.java index 9af6869..ecc1da3 100644 --- a/src/main/java/com/vaultionizer/vaultserver/config/SecurityConfig.java +++ b/src/main/java/com/vaultionizer/vaultserver/config/SecurityConfig.java @@ -24,13 +24,15 @@ protected void configure(HttpSecurity http) throws Exception { @Bean public CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOrigins(Arrays.asList("https://www.vault.gottwuerfeltnicht.de")); + var configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Arrays.asList("https://www.api.vault.jatsqi.com")); + // for testing: + // configuration.setAllowedOrigins(Arrays.asList("http://localhost:63342")); configuration.setAllowedMethods(Arrays.asList("POST", "PUT", "GET", "OPTIONS", "DELETE")); configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", "x-auth-token")); configuration.setExposedHeaders(Arrays.asList("x-auth-token")); configuration.setAllowCredentials(true); - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + var source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } diff --git a/src/main/java/com/vaultionizer/vaultserver/config/SwaggerConfig.java b/src/main/java/com/vaultionizer/vaultserver/config/SwaggerConfig.java index a8893f2..1be48b2 100644 --- a/src/main/java/com/vaultionizer/vaultserver/config/SwaggerConfig.java +++ b/src/main/java/com/vaultionizer/vaultserver/config/SwaggerConfig.java @@ -41,14 +41,14 @@ public LinkDiscoverers discoverers() { } private static final ApiInfo apiInfo = new ApiInfo( // TODO: work on that - "Vaultionizer API", - "A safe space for everybody that seeks after privacy.", - "1.0.0", - "https://www.vault.gottwuerfeltnicht.de", - new Contact("Team Vaultionizer", "https://vaultionizer.com/", "contact@vaultionizer.com"), - "ODC DbCL v1.0 License", - "https://opendatacommons.org/licenses/dbcl/1.0/", - new ArrayList<>() + "Vaultionizer API", + "A safe space for everybody that seeks after privacy.", + "1.0.0", + "https://www.vault.gottwuerfeltnicht.de", + new Contact("Team Vaultionizer", "https://vaultionizer.com/", "contact@vaultionizer.com"), + "ODC DbCL v1.0 License", + "https://opendatacommons.org/licenses/dbcl/1.0/", + new ArrayList<>() ); } diff --git a/src/main/java/com/vaultionizer/vaultserver/config/WebSocketChannelFilter.java b/src/main/java/com/vaultionizer/vaultserver/config/WebSocketChannelFilter.java index 9ca1346..8efe02e 100644 --- a/src/main/java/com/vaultionizer/vaultserver/config/WebSocketChannelFilter.java +++ b/src/main/java/com/vaultionizer/vaultserver/config/WebSocketChannelFilter.java @@ -8,8 +8,7 @@ import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.messaging.support.MessageHeaderAccessor; -import static com.vaultionizer.vaultserver.helpers.Config.WEBSOCKET_DOWNLOAD; -import static com.vaultionizer.vaultserver.helpers.Config.WEBSOCKET_UPLOAD; +import static com.vaultionizer.vaultserver.helpers.Config.*; public class WebSocketChannelFilter implements ChannelInterceptor { private final SessionService sessionService; @@ -21,28 +20,31 @@ public WebSocketChannelFilter(SessionService sessionService) { @Override public Message preSend(Message message, MessageChannel channel) { StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); - if (StompCommand.SUBSCRIBE.equals(accessor.getCommand())){ + if (StompCommand.SUBSCRIBE.equals(accessor.getCommand())) { // TODO: check whether user has rights to subscribe String dest = accessor.getDestination(); if (dest == null) return null; - if (!dest.startsWith(WEBSOCKET_DOWNLOAD)){ - return null; // TODO: send error + if (!dest.startsWith(WEBSOCKET_DOWNLOAD) && !dest.startsWith(WEBSOCKET_ERROR)) { + return null; } - String websocketToken = dest.substring(WEBSOCKET_DOWNLOAD.length()); + + String[] token = dest.split("/"); + if (token.length == 0) return null; + + String websocketToken = token[token.length - 1]; String sessionKey = accessor.getFirstNativeHeader("sessionKey"); String userID = accessor.getFirstNativeHeader("userID"); - if (userID == null || sessionKey == null || websocketToken.length() == 0 || userID.length() == 0) return null; + if (userID == null || sessionKey == null || websocketToken.length() == 0 || userID.length() == 0) + return null; if (!sessionService.checkValidWebsocketToken(Long.parseLong(userID), websocketToken, sessionKey)) { return null; } // subscription is valid - } - - else if (StompCommand.SEND.equals(accessor.getCommand())){ + } else if (StompCommand.SEND.equals(accessor.getCommand())) { // check whether to upload endpoint (only one that is legitimate) String dest = accessor.getDestination(); if (dest == null) return null; - if (!dest.startsWith(WEBSOCKET_UPLOAD)){ + if (!dest.startsWith(WEBSOCKET_UPLOAD)) { return null; // TODO: send error } } diff --git a/src/main/java/com/vaultionizer/vaultserver/config/WebSocketConfig.java b/src/main/java/com/vaultionizer/vaultserver/config/WebSocketConfig.java index d853b79..56d9233 100644 --- a/src/main/java/com/vaultionizer/vaultserver/config/WebSocketConfig.java +++ b/src/main/java/com/vaultionizer/vaultserver/config/WebSocketConfig.java @@ -6,7 +6,10 @@ import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; -import org.springframework.web.socket.config.annotation.*; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; +import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration; import static com.vaultionizer.vaultserver.helpers.Config.WEBSOCKET_PREFIX; @@ -30,6 +33,9 @@ public void configureMessageBroker(MessageBrokerRegistry config) { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint(Config.WEBSOCKET_CONNECT).withSockJS(); + + // for testing: + // registry.addEndpoint(Config.WEBSOCKET_CONNECT).setAllowedOrigins("http://localhost:63342").withSockJS(); } @Override diff --git a/src/main/java/com/vaultionizer/vaultserver/config/WebSocketConfigTomcat.java b/src/main/java/com/vaultionizer/vaultserver/config/WebSocketConfigTomcat.java index aeda5de..f90c721 100644 --- a/src/main/java/com/vaultionizer/vaultserver/config/WebSocketConfigTomcat.java +++ b/src/main/java/com/vaultionizer/vaultserver/config/WebSocketConfigTomcat.java @@ -12,7 +12,7 @@ public class WebSocketConfigTomcat implements WebSocketConfigurer { @Bean public ServletServerContainerFactoryBean createWebSocketContainer() { - ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); + var container = new ServletServerContainerFactoryBean(); container.setMaxTextMessageBufferSize(Config.MSG_SIZE_LIMITS); container.setMaxBinaryMessageBufferSize(Config.MSG_SIZE_LIMITS); return container; diff --git a/src/main/java/com/vaultionizer/vaultserver/controllers/FileController.java b/src/main/java/com/vaultionizer/vaultserver/controllers/FileController.java index f94128b..73b6c74 100644 --- a/src/main/java/com/vaultionizer/vaultserver/controllers/FileController.java +++ b/src/main/java/com/vaultionizer/vaultserver/controllers/FileController.java @@ -1,10 +1,10 @@ package com.vaultionizer.vaultserver.controllers; +import com.vaultionizer.vaultserver.helpers.AccessCheckerUtil; import com.vaultionizer.vaultserver.helpers.FileStatus; -import com.vaultionizer.vaultserver.model.dto.DeleteFileDto; -import com.vaultionizer.vaultserver.model.dto.FileDownloadDto; import com.vaultionizer.vaultserver.model.dto.FileUploadDto; +import com.vaultionizer.vaultserver.model.dto.GenericAuthDto; import com.vaultionizer.vaultserver.service.*; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -25,6 +25,7 @@ public class FileController { private final PendingUploadService pendingUploadService; private final FileService fileService; private final WebsocketController websocketController; + private final AccessCheckerUtil accessCheckerUtil; @Autowired @@ -39,12 +40,10 @@ public FileController(SessionService sessionService, SpaceService spaceService, this.pendingUploadService = pendingUploadService; this.fileService = fileService; this.websocketController = websocketController; + accessCheckerUtil = new AccessCheckerUtil(sessionService, userAccessService, spaceService); } - - - - @RequestMapping(value = "/api/file/upload", method = RequestMethod.POST) + @PostMapping(value = "/api/file/{spaceID}/upload") @ApiOperation(value = "Requests to upload a variable amount of files.", response = Long.class ) @@ -53,34 +52,39 @@ public FileController(SessionService sessionService, SpaceService spaceService, @ApiResponse(code = 400, message = "SpaceID is invalid (< 0) or amount of files to be uploaded is invalid (<= 0)."), @ApiResponse(code = 401, message = "The user either does not exist or the sessionKey is wrong. User is thus not authorized."), @ApiResponse(code = 403, message = "The user has no rights for the requested space."), + @ApiResponse(code = 406, message = "The user has no write access."), @ApiResponse(code = 404, message = "A consistency error occurred.") }) - public @ResponseBody ResponseEntity - uploadFiles(@RequestBody FileUploadDto req){ - Long sessionID = sessionService.getSessionID(req.getAuth().getUserID(), req.getAuth().getSessionKey()); - if (sessionID == -1){ + public @ResponseBody + ResponseEntity + uploadFiles(@RequestBody FileUploadDto req, @RequestHeader("xAuth") GenericAuthDto auth, @PathVariable("spaceID") Long spaceID) { + if (auth == null) return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); + Long sessionID = sessionService.getSessionID(auth.getUserID(), auth.getSessionKey()); + if (sessionID == -1) { return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED); } - if (req.getAmountFiles() <= 0 || req.getSpaceID() < 0){ + if (req.getAmountFiles() <= 0 || spaceID < 0) { return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); } - - if (userAccessService.userHasAccess(req.getAuth().getUserID(), req.getSpaceID())){ - Long refFileID = spaceService.getRefFileID(req.getSpaceID()); - if (refFileID == -1){ + if (userAccessService.userHasAccess(auth.getUserID(), spaceID)) { + if (!spaceService.userHasWriteAccess(spaceID, auth.getUserID())) { + return new ResponseEntity<>(null, HttpStatus.NOT_ACCEPTABLE); + } + Long refFileID = spaceService.getRefFileID(spaceID); + if (refFileID == -1) { return new ResponseEntity<>(null, HttpStatus.NOT_FOUND); } // retrieving the index the files will be saved as Long saveIndex = refFileService.requestUploadFiles(refFileID, (long) req.getAmountFiles()); - if (saveIndex == -1){ + if (saveIndex == -1) { return new ResponseEntity<>(null, HttpStatus.NOT_FOUND); } // add files to pending upload table (with appropriate sessionID) - pendingUploadService.addFilesToUpload(req.getSpaceID(), sessionID, (long) req.getAmountFiles(), saveIndex); + pendingUploadService.addFilesToUpload(spaceID, sessionID, (long) req.getAmountFiles(), saveIndex); return new ResponseEntity<>(saveIndex, HttpStatus.ACCEPTED); } @@ -88,7 +92,7 @@ public FileController(SessionService sessionService, SpaceService spaceService, } - @RequestMapping(value = "/api/file/download", method = RequestMethod.PUT) + @PutMapping(value = "/api/file/{spaceID}/{saveIndex}/download") @ApiOperation(value = "Requests to download a specific file.") @ApiResponses(value = { @ApiResponse(code = 200, message = "The file will be send via websocket to respective location (taking the websocketToken into account)."), @@ -98,65 +102,81 @@ public FileController(SessionService sessionService, SpaceService spaceService, @ApiResponse(code = 423, message = "The requested file is currently either being uploaded or modified. Thus, the file is locked."), @ApiResponse(code = 500, message = "A consistency error occurred. Should never be the case. Bug the developer!") }) - public @ResponseBody ResponseEntity - downloadFile(@RequestBody FileDownloadDto req){ + public @ResponseBody + ResponseEntity + downloadFile(@RequestHeader("xAuth") GenericAuthDto auth, @PathVariable Long spaceID, @PathVariable Long saveIndex) { + if (auth == null) return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); String websocketToken = sessionService. - getSessionWebsocketToken(req.getAuth().getUserID(), req.getAuth().getSessionKey()); - if (websocketToken == null){ + getSessionWebsocketToken(auth.getUserID(), auth.getSessionKey()); + if (websocketToken == null) { return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED); } - if (!userAccessService.userHasAccess(req.getAuth().getUserID(), req.getSpaceID())){ - return new ResponseEntity<>(null, HttpStatus.FORBIDDEN); - } + var httpStatus = accessCheckerUtil.checkAccess(auth, spaceID); + if (httpStatus != null) return new ResponseEntity<>(null, httpStatus); - FileStatus status = fileService.setDownloadFile(req.getSpaceID(), req.getSaveIndex()); - if (status == null){ + FileStatus status = fileService.setDownloadFile(spaceID, saveIndex); + if (status == null) { return new ResponseEntity<>(null, HttpStatus.NOT_FOUND); } - switch (status){ + switch (status) { case READ_FROM: // read file and send to websocket endpoint - Runnable runnable = new Runnable() { - @Override - public void run() { - websocketController.download(websocketToken, req.getSpaceID(), req.getSaveIndex()); - } - }; + Runnable runnable = () -> websocketController.download(websocketToken, spaceID, saveIndex); (new Thread(runnable)).start(); return new ResponseEntity<>(null, HttpStatus.OK); - case MODIFYING: - case UPLOADING: + case MODIFYING, UPLOADING: return new ResponseEntity<>(null, HttpStatus.LOCKED); default: return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); } } - @RequestMapping(value = "/api/file/delete", method = RequestMethod.DELETE) + @DeleteMapping(value = "/api/file/{spaceID}/{saveIndex}") @ApiOperation(value = "Requests to delete a specific file.") @ApiResponses(value = { @ApiResponse(code = 200, message = "File has successfully been deleted."), @ApiResponse(code = 401, message = "The user either does not exist or the sessionKey is wrong. User is thus not authorized."), @ApiResponse(code = 403, message = "The user has no rights for the requested space."), + @ApiResponse(code = 406, message = "The user has no write access."), @ApiResponse(code = 423, message = "The requested file is currently either being uploaded or modified. Thus, the file is locked."), }) - public @ResponseBody ResponseEntity - deleteFile(@RequestBody DeleteFileDto req){ - String websocketToken = sessionService. - getSessionWebsocketToken(req.getAuth().getUserID(), req.getAuth().getSessionKey()); - if (!sessionService.getSession(req.getAuth().getUserID(), req.getAuth().getSessionKey())){ - return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED); + public @ResponseBody + ResponseEntity + deleteFile(@RequestHeader("xAuth") GenericAuthDto auth, @PathVariable Long spaceID, @PathVariable Long saveIndex) { + var status = accessCheckerUtil.checkWriteAccess(auth, spaceID); + if (status != null) return new ResponseEntity<>(null, status); + + boolean success = fileService.deleteFile(spaceID, saveIndex); + if (!success) { + return new ResponseEntity<>(null, HttpStatus.LOCKED); } + return new ResponseEntity<>(null, HttpStatus.OK); + } - if (!userAccessService.userHasAccess(req.getAuth().getUserID(), req.getSpaceID())){ - return new ResponseEntity<>(null, HttpStatus.FORBIDDEN); - } - boolean success = fileService.deleteFile(req.getSpaceID(), req.getSaveIndex()); - if (!success){ - return new ResponseEntity<>(null, HttpStatus.LOCKED); + @PutMapping(value = "/api/file/{spaceID}/{saveIndex}/update") + @ApiOperation(value = "Requests to update a specific file.") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "File has successfully been marked for updating."), + @ApiResponse(code = 401, message = "The user either does not exist or the sessionKey is wrong. User is thus not authorized."), + @ApiResponse(code = 403, message = "The user has no rights for the requested space."), + @ApiResponse(code = 406, message = "The user has no write access."), + @ApiResponse(code = 409, message = "Some conflict occurred."), + }) + public @ResponseBody + ResponseEntity + updateFile(@RequestHeader("xAuth") GenericAuthDto auth, @PathVariable Long spaceID, @PathVariable Long saveIndex) { + var status = accessCheckerUtil.checkWriteAccess(auth, spaceID); + if (status != null) return new ResponseEntity<>(null, status); + + boolean granted = pendingUploadService.updateFile(spaceID, + sessionService.getSessionID(auth.getUserID(), + auth.getSessionKey()), saveIndex); + + if (!granted) { + return new ResponseEntity<>(null, HttpStatus.CONFLICT); } - return new ResponseEntity<>(null, HttpStatus.OK); + return new ResponseEntity<>(null, HttpStatus.ACCEPTED); } } diff --git a/src/main/java/com/vaultionizer/vaultserver/controllers/MiscController.java b/src/main/java/com/vaultionizer/vaultserver/controllers/MiscController.java index 66d6a66..5fec538 100644 --- a/src/main/java/com/vaultionizer/vaultserver/controllers/MiscController.java +++ b/src/main/java/com/vaultionizer/vaultserver/controllers/MiscController.java @@ -21,22 +21,24 @@ public class MiscController { @ApiResponses(value = { @ApiResponse(code = 200, message = "The server's version is returned.") }) - @ResponseBody ResponseEntity - getVersion(){ + public @ResponseBody + ResponseEntity + getVersion() { return new ResponseEntity<>(Config.VERSION, HttpStatus.OK); } - @RequestMapping(value = "/api/misc/checkAuthenticated", method = RequestMethod.POST) + @PostMapping(value = "/api/misc/checkAuthenticated") @ApiOperation(value = "Returns whether the authentication for the server is correct.") @ApiResponses(value = { @ApiResponse(code = 202, message = "The authentication is correct."), @ApiResponse(code = 403, message = "The authentication failed.") }) - @ResponseBody ResponseEntity - checkAuthenticated(@RequestBody CheckAuthenticatedDto req){ + public @ResponseBody + ResponseEntity + checkAuthenticated(@RequestBody CheckAuthenticatedDto req) { return new ResponseEntity<>((!Config.VERSION.isHasAuthKey() || (Config.SERVER_USER.equals(req.getServerUser()) && Config.SERVER_AUTH.equals(req.getServerAuthKey()))) - ? HttpStatus.ACCEPTED : HttpStatus.FORBIDDEN); + ? HttpStatus.ACCEPTED : HttpStatus.FORBIDDEN); } } diff --git a/src/main/java/com/vaultionizer/vaultserver/controllers/RefFileController.java b/src/main/java/com/vaultionizer/vaultserver/controllers/RefFileController.java index f0c57fe..676bd94 100644 --- a/src/main/java/com/vaultionizer/vaultserver/controllers/RefFileController.java +++ b/src/main/java/com/vaultionizer/vaultserver/controllers/RefFileController.java @@ -1,6 +1,8 @@ package com.vaultionizer.vaultserver.controllers; +import com.vaultionizer.vaultserver.helpers.AccessCheckerUtil; +import com.vaultionizer.vaultserver.model.dto.GenericAuthDto; import com.vaultionizer.vaultserver.model.dto.ReadRefFileDto; import com.vaultionizer.vaultserver.model.dto.UpdateRefFileDto; import com.vaultionizer.vaultserver.service.RefFileService; @@ -16,6 +18,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.time.Instant; + @Api(value = "/api/refFile/", description = "Controller that handles the ref file CRUD requests.") @RestController public class RefFileController { @@ -23,6 +27,7 @@ public class RefFileController { private final UserAccessService userAccessService; private final SpaceService spaceService; private final RefFileService refFileService; + private final AccessCheckerUtil accessCheckerUtil; @Autowired public RefFileController(SessionService sessionService, UserAccessService userAccessService, @@ -31,9 +36,10 @@ public RefFileController(SessionService sessionService, UserAccessService userAc this.userAccessService = userAccessService; this.spaceService = spaceService; this.refFileService = refFileService; + accessCheckerUtil = new AccessCheckerUtil(sessionService, userAccessService, spaceService); } - @RequestMapping(value = "/api/refFile/read", method = RequestMethod.POST) + @PostMapping(value = "/api/refFile/{spaceID}/read") @ApiOperation(value = "Read the reference file of the specified space or if lastRead is older than last update on reference file, NOT_MODIFIED is sent as status.") @ApiResponses(value = { @ApiResponse(code = 200, message = "The response contains the content of the current ref file."), @@ -42,58 +48,55 @@ public RefFileController(SessionService sessionService, UserAccessService userAc @ApiResponse(code = 403, message = "Either the space with given ID does not exist or the user has no access to that space."), @ApiResponse(code = 500, message = "Inconsistencies on the server side. Should never be the case.") }) - @ResponseBody ResponseEntity - readRefFile(@RequestBody ReadRefFileDto req){ - if (!sessionService.getSession(req.getAuth().getUserID(), req.getAuth().getSessionKey())){ - return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED); - } + public @ResponseBody + ResponseEntity // TODO + readRefFile(@RequestBody ReadRefFileDto req, @RequestHeader("xAuth") GenericAuthDto auth, @PathVariable Long spaceID) { + var status = accessCheckerUtil.checkAccess(auth, spaceID); + if (status != null) return new ResponseEntity<>(null, status); - if (userAccessService.userHasAccess(req.getAuth().getUserID(), req.getSpaceID())){ - Long refFileID = spaceService.getRefFileID(req.getSpaceID()); - if (refFileID == -1L) { - return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); - } + Long refFileID = spaceService.getRefFileID(spaceID); + if (refFileID == -1L) { + return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); + } - // if the last fetched version is latest, just tell user not modified - if (req.getLastRead() != null && !refFileService.hasNewVersion(refFileID, req.getLastRead())){ - return new ResponseEntity<>(null, HttpStatus.NOT_MODIFIED); - } - String content = refFileService.readRefFile(refFileID); - if (content == null){ - return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); - } - return new ResponseEntity<>(content, HttpStatus.OK); + // if the last fetched version is latest, just tell user not modified + if (req.getLastRead() != null && !refFileService.hasNewVersion(refFileID, Instant.ofEpochMilli(req.getLastRead()))) { + return new ResponseEntity<>(null, HttpStatus.NOT_MODIFIED); + } + String content = refFileService.readRefFile(refFileID); + if (content == null) { + return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); } + return new ResponseEntity<>(content, HttpStatus.OK); + - return new ResponseEntity<>(null, HttpStatus.FORBIDDEN); } - @RequestMapping(value = "/api/refFile/update", method = RequestMethod.POST) + @PutMapping(value = "/api/refFile/{spaceID}/update") @ApiOperation(value = "Update the reference file of the specified space.") @ApiResponses(value = { @ApiResponse(code = 200, message = "The response contains the content of the current ref file."), @ApiResponse(code = 401, message = "The user either does not exist or the sessionKey is wrong. User is thus not authorized."), @ApiResponse(code = 403, message = "Either the space with given ID does not exist or the user has no access to that space."), + @ApiResponse(code = 406, message = "The user has no write access."), @ApiResponse(code = 500, message = "Inconsistencies on the server side. Should never be the case.") }) - @ResponseBody ResponseEntity - updateRefFile(@RequestBody UpdateRefFileDto req){ - if (!sessionService.getSession(req.getAuth().getUserID(), req.getAuth().getSessionKey())){ - return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED); - } + public @ResponseBody + ResponseEntity // TODO + updateRefFile(@RequestBody UpdateRefFileDto req, @RequestHeader("xAuth") GenericAuthDto auth, @PathVariable Long spaceID) { + var status = accessCheckerUtil.checkWriteAccess(auth, spaceID); + if (status != null) return new ResponseEntity<>(null, status); - if (userAccessService.userHasAccess(req.getAuth().getUserID(), req.getSpaceID())){ - Long refFileID = spaceService.getRefFileID(req.getSpaceID()); - if (refFileID == -1L) { - return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); - } - boolean success = refFileService.updateRefFile(refFileID, req.getContent()); - if (!success){ - return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); - } - return new ResponseEntity<>(null, HttpStatus.OK); + Long refFileID = spaceService.getRefFileID(spaceID); + if (refFileID == -1L) { + return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); } + boolean success = refFileService.updateRefFile(refFileID, req.getContent()); + if (!success) { + return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); + } + return new ResponseEntity<>(null, HttpStatus.OK); + - return new ResponseEntity<>(null, HttpStatus.FORBIDDEN); } } diff --git a/src/main/java/com/vaultionizer/vaultserver/controllers/SessionController.java b/src/main/java/com/vaultionizer/vaultserver/controllers/SessionController.java index 89167b3..2f66c1f 100644 --- a/src/main/java/com/vaultionizer/vaultserver/controllers/SessionController.java +++ b/src/main/java/com/vaultionizer/vaultserver/controllers/SessionController.java @@ -1,6 +1,7 @@ package com.vaultionizer.vaultserver.controllers; -import com.vaultionizer.vaultserver.model.dto.AuthWrapperDto; +import com.vaultionizer.vaultserver.helpers.AccessCheckerUtil; +import com.vaultionizer.vaultserver.model.dto.GenericAuthDto; import com.vaultionizer.vaultserver.service.SessionService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -9,30 +10,34 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; @Api(value = "/api/session/", description = "Controller that allows renewing the session.") @RestController public class SessionController { private final SessionService sessionService; + private final AccessCheckerUtil accessCheckerUtil; @Autowired public SessionController(SessionService sessionService) { this.sessionService = sessionService; + accessCheckerUtil = new AccessCheckerUtil(sessionService, null, null); } - @RequestMapping(value = "/api/session/renew", method = RequestMethod.PUT) + @PutMapping(value = "/api/session/renew") @ApiOperation(value = "Renews the session with specified key.") @ApiResponses(value = { @ApiResponse(code = 200, message = "The session has been renewed successfully."), @ApiResponse(code = 403, message = "The session either does not exist or has become invalid already."), }) - @ResponseBody + public @ResponseBody ResponseEntity - renewSession(@RequestBody AuthWrapperDto req){ - if (!sessionService.getSession(req.getAuth().getUserID(), req.getAuth().getSessionKey())){ - return new ResponseEntity<>(null, HttpStatus.FORBIDDEN); - } // if the session exists, the session has just indirectly been renewed. + renewSession(@RequestHeader("xAuth") GenericAuthDto auth) { + var status = accessCheckerUtil.checkAuthenticated(auth); + if (status != null) return new ResponseEntity<>(null, status); return new ResponseEntity<>(null, HttpStatus.OK); } } diff --git a/src/main/java/com/vaultionizer/vaultserver/controllers/SpaceController.java b/src/main/java/com/vaultionizer/vaultserver/controllers/SpaceController.java index 5681413..1d4d5ec 100644 --- a/src/main/java/com/vaultionizer/vaultserver/controllers/SpaceController.java +++ b/src/main/java/com/vaultionizer/vaultserver/controllers/SpaceController.java @@ -1,8 +1,7 @@ package com.vaultionizer.vaultserver.controllers; -import com.vaultionizer.vaultserver.model.db.SpaceModel; +import com.vaultionizer.vaultserver.helpers.AccessCheckerUtil; import com.vaultionizer.vaultserver.model.dto.*; -import com.vaultionizer.vaultserver.resource.SpaceRepository; import com.vaultionizer.vaultserver.service.*; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -22,6 +21,7 @@ public class SpaceController { private final PendingUploadService pendingUploadService; private final FileService fileService; private final UserAccessService userAccessService; + private final AccessCheckerUtil accessCheckerUtil; @Autowired public SpaceController(SessionService sessionService, SpaceService spaceService, RefFileService refFileService, @@ -32,69 +32,70 @@ public SpaceController(SessionService sessionService, SpaceService spaceService, this.pendingUploadService = pendingUploadService; this.fileService = fileService; this.userAccessService = userAccessService; + accessCheckerUtil = new AccessCheckerUtil(sessionService, userAccessService, spaceService); } - - @RequestMapping(value = "/api/spaces/getAll", method = RequestMethod.POST) + @GetMapping(value = "/api/space") @ApiOperation(value = "Returns all spaces a user has access to.", - response = GetSpacesResponseDto.class, - responseContainer = "List" + response = GetSpacesResponseDto.class, + responseContainer = "List" ) @ApiResponses(value = { @ApiResponse(code = 200, message = "The response contains all spaces the user has access to."), @ApiResponse(code = 401, message = "The user either does not exist or the sessionKey is wrong. User is thus not authorized."), }) - public @ResponseBody ResponseEntity - getAllSpaces(@RequestBody AuthWrapperDto req){ - GenericAuthDto auth = req.getAuth(); - if (!sessionService.getSession(auth.getUserID(), auth.getSessionKey())){ - return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED); - } + public @ResponseBody + ResponseEntity + getAllSpaces(@RequestHeader("xAuth") GenericAuthDto auth) { + var status = accessCheckerUtil.checkAuthenticated(auth); + if (status != null) return new ResponseEntity<>(null, status); return new ResponseEntity<>( spaceService.getSpacesAccessible(auth.getUserID()), HttpStatus.OK); } - @RequestMapping(value = "/api/spaces/create", method = RequestMethod.POST) + @PostMapping(value = "/api/space/create") @ApiOperation(value = "Creates a new space.", - response = Long.class) + response = Long.class) @ApiResponses(value = { @ApiResponse(code = 201, message = "The space was created successfully. The returned value is the space's ID."), @ApiResponse(code = 401, message = "The user either does not exist or the sessionKey is wrong."), }) - public @ResponseBody ResponseEntity - createSpace(@RequestBody CreateSpaceDto req){ - if (!sessionService.getSession(req.getAuth().getUserID(), req.getAuth().getSessionKey())){ - return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED); - } - Long spaceID = spaceService.createSpace(req.getAuth().getUserID(), req.getReferenceFile(), req.isPrivate(), req.getAuthKey()); + public @ResponseBody + ResponseEntity + createSpace(@RequestBody CreateSpaceDto req, @RequestHeader("xAuth") GenericAuthDto auth) { + var status = accessCheckerUtil.checkAuthenticated(auth); + if (status != null) return new ResponseEntity<>(null, status); + + Long spaceID = spaceService.createSpace(auth.getUserID(), req.getReferenceFile(), req.isPrivate(), + req.getUsersWriteAccess(), req.getUsersAuthAccess(), req.getAuthKey()); return new ResponseEntity<>(spaceID, HttpStatus.CREATED); } - @RequestMapping(value = "/api/spaces/join", method = RequestMethod.PUT) + @PutMapping(value = "/api/space/{spaceID}/join") @ApiOperation(value = "Adds the user to the space.") @ApiResponses(value = { @ApiResponse(code = 200, message = "The user was successfully added to the space."), @ApiResponse(code = 401, message = "The user either does not exist or the sessionKey is wrong. User is thus not authorized."), @ApiResponse(code = 403, message = "Either the space with given ID does not exist, it is private or the authorization key is wrong.") }) - public @ResponseBody ResponseEntity - joinSpace(@RequestBody JoinSpaceDto req){ - if (!sessionService.getSession(req.getAuth().getUserID(), req.getAuth().getSessionKey())){ - return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED); - } + public @ResponseBody + ResponseEntity + joinSpace(@RequestBody JoinSpaceDto req, @RequestHeader("xAuth") GenericAuthDto auth, @PathVariable Long spaceID) { + var status = accessCheckerUtil.checkAuthenticated(auth); + if (status != null) return new ResponseEntity<>(null, status); - if (spaceService.checkSpaceCredentials(req.getSpaceID(), req.getAuthKey())){ - userAccessService.addUserAccess(req.getSpaceID(), req.getAuth().getUserID()); + if (spaceService.checkSpaceCredentials(spaceID, req.getAuthKey())) { + userAccessService.addUserAccess(spaceID, auth.getUserID()); return new ResponseEntity<>(null, HttpStatus.OK); } return new ResponseEntity<>(null, HttpStatus.FORBIDDEN); } - @RequestMapping(value = "/api/spaces/quit/{spaceID}", method = RequestMethod.DELETE) + @DeleteMapping(value = "/api/space/{spaceID}/quit") @ApiOperation(value = "Removes the user from the space.") @ApiResponses(value = { @ApiResponse(code = 200, message = "The user successfully quit the space."), @@ -102,45 +103,122 @@ public SpaceController(SessionService sessionService, SpaceService spaceService, @ApiResponse(code = 404, message = "The spaceID does not exist or you do not have access in the first place."), @ApiResponse(code = 406, message = "The user is the creator of the space and thus must delete the space manually.") }) - public @ResponseBody ResponseEntity - quitSpace(@RequestBody AuthWrapperDto req, @PathVariable Long spaceID){ - if (!sessionService.getSession(req.getAuth().getUserID(), req.getAuth().getSessionKey())){ - return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED); - } - if (spaceService.checkCreator(spaceID, req.getAuth().getUserID())){ + public @ResponseBody + ResponseEntity + quitSpace(@PathVariable Long spaceID, @RequestHeader("xAuth") GenericAuthDto auth) { + var status = accessCheckerUtil.checkAuthenticated(auth); + if (status != null) return new ResponseEntity<>(null, status); + if (spaceService.checkCreator(spaceID, auth.getUserID())) { return new ResponseEntity<>(null, HttpStatus.NOT_ACCEPTABLE); } - return new ResponseEntity<>(null, userAccessService.removeAccess(req.getAuth().getUserID(), spaceID) ? + return new ResponseEntity<>(null, userAccessService.removeAccess(auth.getUserID(), spaceID) ? HttpStatus.OK : HttpStatus.NOT_FOUND); } - @RequestMapping(value = "/api/spaces/key", method = RequestMethod.POST) - @ApiOperation( value = "Returns the authentication key of a file.", - response = SpaceAuthKeyResponseDto.class + @GetMapping(value = "/api/space/{spaceID}/authkey") + @ApiOperation(value = "Returns the authentication key of a file.", + response = SpaceAuthKeyResponseDto.class ) @ApiResponses(value = { @ApiResponse(code = 200, message = "The auth key is returned."), @ApiResponse(code = 401, message = "The user either does not exist or the sessionKey is wrong. User is thus not authorized."), - @ApiResponse(code = 403, message = "Either the space with given ID does not exist, it is private or the authorization key is wrong.") + @ApiResponse(code = 403, message = "Either the space with given ID does not exist, it is private or the authorization key is wrong."), + @ApiResponse(code = 406, message = "User is not allowed to get the auth key."), + @ApiResponse(code = 417, message = "Some other error occurred.") }) - public @ResponseBody ResponseEntity - getAuthKey(@RequestBody SpaceAuthKeyDto req){ - if (!sessionService.getSession(req.getAuth().getUserID(), req.getAuth().getSessionKey())){ - return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED); - } + public @ResponseBody + ResponseEntity + getAuthKey(@RequestHeader("xAuth") GenericAuthDto auth, @PathVariable Long spaceID) { + var status = accessCheckerUtil.checkAuthKeyAccess(auth, spaceID); + if (status != null) return new ResponseEntity<>(null, status); - if (spaceService.checkDeleted(req.getSpaceID()) && - userAccessService.userHasAccess(req.getAuth().getUserID(), req.getSpaceID())){ - return new ResponseEntity<>(spaceService.getSpaceAuthKey(req.getSpaceID()), HttpStatus.OK); - } + var authKey = spaceService.getSpaceAuthKey(spaceID); + if (authKey.isEmpty()) return new ResponseEntity<>(null, HttpStatus.EXPECTATION_FAILED); + return new ResponseEntity<>(authKey.get(), HttpStatus.OK); + } - return new ResponseEntity<>(null, HttpStatus.FORBIDDEN); + @PutMapping(value = "/api/space/{spaceID}/config") + @ApiOperation(value = "Returns the authentication key of a file.", + response = ConfigureSpaceDto.class + ) + @ApiResponses(value = { + @ApiResponse(code = 202, message = "The auth key is returned."), + @ApiResponse(code = 401, message = "The user either does not exist or the sessionKey is wrong. User is thus not authorized."), + @ApiResponse(code = 403, message = "Either the space with given ID does not exist, it is private or the authorization key is wrong."), + @ApiResponse(code = 406, message = "User is not the creator.") + }) + public @ResponseBody + ResponseEntity + configureSpace(@RequestBody ConfigureSpaceDto req, @PathVariable Long spaceID, @RequestHeader("xAuth") GenericAuthDto auth) { + var status = accessCheckerUtil.checkPrivilegeLevel(auth, spaceID); + if (status != null) return new ResponseEntity<>(null, status); + if (req.getSharedSpace() != null) + spaceService.changeSharedState(spaceID, auth.getUserID(), req.getSharedSpace()); + spaceService.configureSpace(spaceID, req.getUsersWriteAccess(), req.getUsersAuthAccess()); + return new ResponseEntity<>(null, HttpStatus.ACCEPTED); + } + + @DeleteMapping(value = "/api/space/{spaceID}/kickall") + @ApiOperation(value = "Returns the authentication key of a file.", + response = GenericAuthDto.class + ) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "The auth key is returned."), + @ApiResponse(code = 401, message = "The user either does not exist or the sessionKey is wrong. User is thus not authorized."), + @ApiResponse(code = 403, message = "Either the space with given ID does not exist, it is private or the user has no access."), + @ApiResponse(code = 406, message = "User is not the creator.") + }) + public @ResponseBody + ResponseEntity + kickUsers(@PathVariable Long spaceID, @RequestHeader("xAuth") GenericAuthDto auth) { + var status = accessCheckerUtil.checkPrivilegeLevel(auth, spaceID); + if (status != null) return new ResponseEntity<>(null, status); + + userAccessService.kickAll(spaceID, auth.getUserID()); + return new ResponseEntity<>(null, HttpStatus.OK); + } + + @PutMapping(value = "/api/space/{spaceID}/authkey") + @ApiOperation(value = "Changes the authentication key of a space.", + response = ChangeAuthKeyDto.class + ) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "The auth key was replaced."), + @ApiResponse(code = 401, message = "The user either does not exist or the sessionKey is wrong. User is thus not authorized."), + @ApiResponse(code = 403, message = "Either the space with given ID does not exist, it is private or the user has no access."), + @ApiResponse(code = 406, message = "User is not the creator.") + }) + public @ResponseBody + ResponseEntity + changeAuthKey(@RequestBody ChangeAuthKeyDto req, @PathVariable Long spaceID, @RequestHeader("xAuth") GenericAuthDto auth) { + var status = accessCheckerUtil.checkPrivilegeLevel(auth, spaceID); + if (status != null) return new ResponseEntity<>(null, status); + + spaceService.changeAuthKey(spaceID, req.getAuthKey()); + return new ResponseEntity<>(null, HttpStatus.OK); } + @GetMapping(value = "/api/space/{spaceID}/config") + @ApiOperation(value = "Returns the configuration of a space.", + response = GetSpacesResponseDto.class + ) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "The config is returned."), + @ApiResponse(code = 401, message = "The user either does not exist or the sessionKey is wrong. User is thus not authorized."), + @ApiResponse(code = 403, message = "Either the space with given ID does not exist, it is private or the user has no access.") + }) + public @ResponseBody + ResponseEntity + getSpaceConfig(@PathVariable Long spaceID, @RequestHeader("xAuth") GenericAuthDto auth) { + var status = accessCheckerUtil.checkAccess(auth, spaceID); + if (status != null) return new ResponseEntity<>(null, status); + + return new ResponseEntity<>(spaceService.getSpaceConfig(spaceID), HttpStatus.OK); + } - @RequestMapping(value = "/api/spaces/delete/{spaceID}", method = RequestMethod.DELETE) - @ApiOperation( value = "Deletes the specified space if permitted.", + @DeleteMapping(value = "/api/space/{spaceID}") + @ApiOperation(value = "Deletes the specified space if permitted.", response = SpaceAuthKeyResponseDto.class ) @ApiResponses(value = { @@ -149,19 +227,18 @@ public SpaceController(SessionService sessionService, SpaceService spaceService, @ApiResponse(code = 403, message = "Either the space with given ID does not exist."), @ApiResponse(code = 412, message = "Space is probably currently in deletion process.") }) - public @ResponseBody ResponseEntity - deleteSpace(@RequestBody AuthWrapperDto req, @PathVariable Long spaceID){ - GenericAuthDto auth = req.getAuth(); - if (!sessionService.getSession(auth.getUserID(), auth.getSessionKey())){ - return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED); - } + public @ResponseBody + ResponseEntity + deleteSpace(@PathVariable Long spaceID, @RequestHeader("xAuth") GenericAuthDto auth) { + var status = accessCheckerUtil.checkAuthenticated(auth); + if (status != null) return new ResponseEntity<>(null, status); if (!userAccessService.userHasAccess(auth.getUserID(), spaceID) || - !spaceService.checkCreator(spaceID, auth.getUserID())){ + !spaceService.checkCreator(spaceID, auth.getUserID())) { return new ResponseEntity<>(null, HttpStatus.FORBIDDEN); } - if (!spaceService.markSpaceDeleted(spaceID)){ + if (!spaceService.markSpaceDeleted(spaceID)) { return new ResponseEntity<>(null, HttpStatus.PRECONDITION_FAILED); } deleteSpaceRoutine(spaceID); @@ -169,7 +246,7 @@ public SpaceController(SessionService sessionService, SpaceService spaceService, return new ResponseEntity<>(null, HttpStatus.OK); } - public void deleteSpaceRoutine(Long spaceID){ + public void deleteSpaceRoutine(Long spaceID) { userAccessService.deleteAllWithSpace(spaceID); fileService.deleteAllFilesInSpace(spaceID); diff --git a/src/main/java/com/vaultionizer/vaultserver/controllers/UserController.java b/src/main/java/com/vaultionizer/vaultserver/controllers/UserController.java index 6c5c3bc..ee8a790 100644 --- a/src/main/java/com/vaultionizer/vaultserver/controllers/UserController.java +++ b/src/main/java/com/vaultionizer/vaultserver/controllers/UserController.java @@ -1,5 +1,6 @@ package com.vaultionizer.vaultserver.controllers; +import com.vaultionizer.vaultserver.helpers.AccessCheckerUtil; import com.vaultionizer.vaultserver.helpers.Config; import com.vaultionizer.vaultserver.model.dto.*; import com.vaultionizer.vaultserver.service.*; @@ -24,6 +25,7 @@ public class UserController { private final SpaceController spaceController; private final UserAccessService userAccessService; private final PendingUploadService pendingUploadService; + private final AccessCheckerUtil accessCheckerUtil; @Autowired public UserController(UserService userService, SessionService sessionService, @@ -35,11 +37,11 @@ public UserController(UserService userService, SessionService sessionService, this.spaceController = spaceController; this.userAccessService = userAccessService; this.pendingUploadService = pendingUploadService; + accessCheckerUtil = new AccessCheckerUtil(sessionService, userAccessService, spaceService); } - - @RequestMapping(value = "/api/users/create", method = RequestMethod.POST) + @PostMapping(value = "/api/user/create") @ApiOperation(value = "Creates a new user, a new private space and adds a session.", response = LoginUserResponseDto.class) @ApiResponses(value = { @@ -48,19 +50,19 @@ public UserController(UserService userService, SessionService sessionService, @ApiResponse(code = 403, message = "The server credentials are wrong."), @ApiResponse(code = 409, message = "The username is in use.") }) - public @ResponseBody ResponseEntity - createUser(@RequestBody RegisterUserDto req){ + public @ResponseBody + ResponseEntity + createUser(@RequestBody RegisterUserDto req) { if (Config.VERSION.isHasAuthKey() && - ( req.getServerUser() == null || req.getServerAuthKey() == null || - !req.getServerUser().equals(Config.SERVER_USER) || !req.getServerAuthKey().equals(Config.SERVER_AUTH))) - { + (req.getServerUser() == null || req.getServerAuthKey() == null || + !req.getServerUser().equals(Config.SERVER_USER) || !req.getServerAuthKey().equals(Config.SERVER_AUTH))) { return new ResponseEntity<>(null, HttpStatus.FORBIDDEN); } if (req.getKey() == null || req.getRefFile() == null || req.getKey().length() < Config.MIN_USER_KEY_LENGTH || req.getRefFile().length() == 0 || req.getUsername() == null || req.getUsername().length() < Config.MIN_USERNAME_LENGTH - ){ + ) { return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); } @@ -68,21 +70,22 @@ public UserController(UserService userService, SessionService sessionService, if (userID == null) { return new ResponseEntity<>(null, HttpStatus.CONFLICT); } - spaceService.createSpace(userID, req.getRefFile(), true, null); + spaceService.createSpace(userID, req.getRefFile(), true, false, false, null); return new ResponseEntity<>(sessionService.addSession(userID), HttpStatus.CREATED); } - @RequestMapping(value = "/api/users/login", method = RequestMethod.POST) + @PostMapping(value = "/api/user/login") @ApiOperation(value = "Logs the user in and returns a session.", response = LoginUserResponseDto.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "The user was signed in successfully. The response is a session key."), @ApiResponse(code = 401, message = "The user authorization failed.") }) - public @ResponseBody ResponseEntity - loginUser(@RequestBody LoginUserDto req){ + public @ResponseBody + ResponseEntity + loginUser(@RequestBody LoginUserDto req) { Long userID = userService.getUserIDCheckCredentials(req.getUsername(), req.getKey()); - if (userID == -1){ + if (userID == -1) { // no user has that id in combination with the key return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED); } @@ -90,30 +93,31 @@ public UserController(UserService userService, SessionService sessionService, return new ResponseEntity<>(sessionService.addSession(userID), HttpStatus.OK); } - @RequestMapping(value = "/api/users/logout", method = RequestMethod.PUT) + @PutMapping(value = "/api/user/logout") @ApiOperation(value = "Logs the user out.") @ApiResponses(value = { @ApiResponse(code = 200, message = "The user was logged out successfully.") }) - public @ResponseBody ResponseEntity - logoutUser(@RequestBody AuthWrapperDto req){ - GenericAuthDto auth = req.getAuth(); + public @ResponseBody + ResponseEntity + logoutUser(@RequestHeader("xAuth") GenericAuthDto auth) { + var status = accessCheckerUtil.checkAuthenticated(auth); + if (status != null) return new ResponseEntity<>(null, status); sessionService.deleteSession(auth.getUserID(), auth.getSessionKey()); return new ResponseEntity<>(null, HttpStatus.OK); } - @RequestMapping(value = "/api/users/delete", method = RequestMethod.DELETE) + @DeleteMapping(value = "/api/user") @ApiOperation(value = "Deletes the specified user and all spaces the user created.") @ApiResponses(value = { @ApiResponse(code = 200, message = "The deletion process was successful."), @ApiResponse(code = 401, message = "The user authorization failed.") }) - public @ResponseBody ResponseEntity - deleteUser(@RequestBody AuthWrapperDto req){ - GenericAuthDto auth = req.getAuth(); - if (!sessionService.getSession(auth.getUserID(), auth.getSessionKey())){ - return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED); - } + public @ResponseBody + ResponseEntity + deleteUser(@RequestHeader("xAuth") GenericAuthDto auth) { + var status = accessCheckerUtil.checkAuthenticated(auth); + if (status != null) return new ResponseEntity<>(null, status); userService.setDeleted(auth.getUserID()); pendingUploadService.deletePendingUploadsByUser(auth.getUserID()); sessionService.deleteAllSessionsWithUser(auth.getUserID()); diff --git a/src/main/java/com/vaultionizer/vaultserver/controllers/WebsocketController.java b/src/main/java/com/vaultionizer/vaultserver/controllers/WebsocketController.java index 46d8e51..13ed69d 100644 --- a/src/main/java/com/vaultionizer/vaultserver/controllers/WebsocketController.java +++ b/src/main/java/com/vaultionizer/vaultserver/controllers/WebsocketController.java @@ -1,7 +1,11 @@ package com.vaultionizer.vaultserver.controllers; + import com.vaultionizer.vaultserver.helpers.Config; import com.vaultionizer.vaultserver.model.dto.WebsocketFileDto; +import com.vaultionizer.vaultserver.model.dto.wserrors.GenericWSError; +import com.vaultionizer.vaultserver.model.dto.wserrors.UploadData; +import com.vaultionizer.vaultserver.model.dto.wserrors.WS_ERROR; import com.vaultionizer.vaultserver.service.FileService; import com.vaultionizer.vaultserver.service.PendingUploadService; import com.vaultionizer.vaultserver.service.SessionService; @@ -12,9 +16,7 @@ import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Controller; import org.springframework.util.LinkedMultiValueMap; -import org.springframework.web.bind.annotation.CrossOrigin; -@CrossOrigin(maxAge = 3600) @Controller public class WebsocketController { private final SessionService sessionService; @@ -32,49 +34,75 @@ public WebsocketController(SessionService sessionService, PendingUploadService p } @MessageMapping("/upload") - public void upload(@Payload WebsocketFileDto content, Message file){ - // TODO: check how to send errors + public void upload(@Payload WebsocketFileDto content, Message file) { if (content == null || content.getContent() == null) return; LinkedMultiValueMap nativeHeaders = parseNativeHeaders(file.getHeaders().get("nativeHeaders")); if (nativeHeaders == null) return; // parse headers - Long userID = parseLongFromHeader(nativeHeaders, "userID"); - Long spaceID = parseLongFromHeader(nativeHeaders, "spaceID"); - Long saveIndex = parseLongFromHeader(nativeHeaders, "saveIndex"); + var userID = parseLongFromHeader(nativeHeaders, "userID"); + var spaceID = parseLongFromHeader(nativeHeaders, "spaceID"); + var saveIndex = parseLongFromHeader(nativeHeaders, "saveIndex"); String sessionKey = nativeHeaders.getFirst("sessionKey"); + String wsToken = nativeHeaders.getFirst("websocketToken"); + + if (sessionKey == null || wsToken == null) return; - if (userID == null || spaceID == null || saveIndex == null || sessionKey == null) return; Long sessID = sessionService.getSessionID(userID, sessionKey); - if (sessID == -1) return; - boolean granted = pendingUploadService.uploadFile(spaceID, sessID, saveIndex); - if (!granted) return; + if (sessID == -1) { + return; + } - fileService.setUploadFile(spaceID, saveIndex); + if (userID == null || spaceID == null || saveIndex == null) { + sendError(wsToken, new GenericWSError(WS_ERROR.MISSHAPEN_UPLOAD, + new UploadData(userID, spaceID, saveIndex, sessionKey) + )); + return; + } - boolean success = fileService.writeToFile(content.getContent(), spaceID, saveIndex); - if (!success) { reportError(userID, sessionKey, 500); } - } + int granted = pendingUploadService.uploadFile(spaceID, sessID, saveIndex); + if (granted == 0) { + sendError(wsToken, new GenericWSError(WS_ERROR.UPLOAD_NOT_GRANTED, + new UploadData(userID, spaceID, saveIndex, sessionKey) + )); + return; + } - private void reportError(Long userID, String sessionKey, int status){ - System.out.println("Error"); + boolean success; + if (granted == 1) { + // usual upload + fileService.setUploadFile(spaceID, saveIndex); + success = fileService.writeToFile(content.getContent(), spaceID, saveIndex); + } else { + // updating requested + success = fileService.tryUpdating(content.getContent(), spaceID, saveIndex); + } + + if (!success) { + sendError(wsToken, new GenericWSError(WS_ERROR.UPLOAD_UNSUCCESSFUL, + new UploadData(userID, spaceID, saveIndex, sessionKey))); + } } - public synchronized void download(String websocketToken, Long spaceID, Long saveIndex){ + public synchronized void download(String websocketToken, Long spaceID, Long saveIndex) { // TODO: check how to set headers (namely: spaceID and saveIndex) - simpMessagingTemplate.convertAndSend( Config.WEBSOCKET_DOWNLOAD + websocketToken, + simpMessagingTemplate.convertAndSend(Config.WEBSOCKET_DOWNLOAD + websocketToken, fileService.makeDownload(spaceID, saveIndex)); } - private Long parseLongFromHeader(LinkedMultiValueMap map, String key){ + public void sendError(String websocketToken, GenericWSError error) { + simpMessagingTemplate.convertAndSend(Config.WEBSOCKET_ERROR + websocketToken, error); + } + + private Long parseLongFromHeader(LinkedMultiValueMap map, String key) { if (map.getFirst(key) == null) return null; return Long.parseLong(map.getFirst(key)); } - private LinkedMultiValueMap parseNativeHeaders(Object o){ + private LinkedMultiValueMap parseNativeHeaders(Object o) { if (o == null) return null; - if (o instanceof LinkedMultiValueMap){ - return (LinkedMultiValueMap)o; + if (o instanceof LinkedMultiValueMap) { + return (LinkedMultiValueMap) o; } return null; } diff --git a/src/main/java/com/vaultionizer/vaultserver/helpers/AccessCheckerUtil.java b/src/main/java/com/vaultionizer/vaultserver/helpers/AccessCheckerUtil.java new file mode 100644 index 0000000..7f3cebc --- /dev/null +++ b/src/main/java/com/vaultionizer/vaultserver/helpers/AccessCheckerUtil.java @@ -0,0 +1,64 @@ +package com.vaultionizer.vaultserver.helpers; + +import com.vaultionizer.vaultserver.model.dto.GenericAuthDto; +import com.vaultionizer.vaultserver.service.SessionService; +import com.vaultionizer.vaultserver.service.SpaceService; +import com.vaultionizer.vaultserver.service.UserAccessService; +import org.springframework.http.HttpStatus; + +public class AccessCheckerUtil { + private SessionService sessionService; + private UserAccessService userAccessService; + private SpaceService spaceService; + + public AccessCheckerUtil(SessionService sessionService, UserAccessService userAccessService, SpaceService spaceService) { + this.sessionService = sessionService; + this.userAccessService = userAccessService; + this.spaceService = spaceService; + } + + public HttpStatus checkAuthenticated(GenericAuthDto auth) { + if (auth == null) return HttpStatus.BAD_REQUEST; + if (!sessionService.getSession(auth.getUserID(), auth.getSessionKey())) { + return HttpStatus.UNAUTHORIZED; + } + return null; + } + + public HttpStatus checkAccess(GenericAuthDto auth, Long spaceID) { + var status = checkAuthenticated(auth); + if (status != null) return status; + if (spaceService.checkDeleted(spaceID) || + !userAccessService.userHasAccess(auth.getUserID(), spaceID)) { + return HttpStatus.FORBIDDEN; + } + return null; + } + + // check whether user is logged in, has access and whether user is creator. If so, returns null + public HttpStatus checkPrivilegeLevel(GenericAuthDto auth, Long spaceID) { + HttpStatus accessStatus = checkAccess(auth, spaceID); + if (accessStatus != null) return accessStatus; + if (!spaceService.checkCreator(spaceID, auth.getUserID())) { + return HttpStatus.NOT_ACCEPTABLE; + } + return null; + } + + public HttpStatus checkAuthKeyAccess(GenericAuthDto auth, Long spaceID) { + var status = checkAccess(auth, spaceID); + if (status != null) return status; + if (!spaceService.userHasAuthKeyAccess(spaceID, auth.getUserID())) { + return HttpStatus.NOT_ACCEPTABLE; + } + return null; + } + + public HttpStatus checkWriteAccess(GenericAuthDto auth, Long spaceID) { + var status = checkAccess(auth, spaceID); + if (status != null) return status; + if (!spaceService.userHasWriteAccess(spaceID, auth.getUserID())) + return HttpStatus.NOT_ACCEPTABLE; + return null; + } +} diff --git a/src/main/java/com/vaultionizer/vaultserver/helpers/Config.java b/src/main/java/com/vaultionizer/vaultserver/helpers/Config.java index c75e8fb..7291850 100644 --- a/src/main/java/com/vaultionizer/vaultserver/helpers/Config.java +++ b/src/main/java/com/vaultionizer/vaultserver/helpers/Config.java @@ -23,15 +23,18 @@ public class Config { public static final String WEBSOCKET_PREFIX = "/api/ws"; public static final String WEBSOCKET_RES = "/api/wsres"; public static final String WEBSOCKET_DOWNLOAD = WEBSOCKET_RES + "/download/"; + public static final String WEBSOCKET_ERROR = WEBSOCKET_RES + "/error/"; public static final String WEBSOCKET_UPLOAD = WEBSOCKET_PREFIX + "/upload"; // is adjusted in the tests (thus not final) public static String SPACE_PATH = "/home/vaultionizer/spaces/"; - public static final int SESSION_JOB_DELAY = 3600 * 1000; public static final int PENDING_UPLOAD_JOB_DELAY = 86400 * 1000; public static final String RANDOM_ALGO = "SHA1PRNG"; + + private Config() { + } } diff --git a/src/main/java/com/vaultionizer/vaultserver/helpers/GenericAuthConverter.java b/src/main/java/com/vaultionizer/vaultserver/helpers/GenericAuthConverter.java new file mode 100644 index 0000000..a28ebb0 --- /dev/null +++ b/src/main/java/com/vaultionizer/vaultserver/helpers/GenericAuthConverter.java @@ -0,0 +1,28 @@ +package com.vaultionizer.vaultserver.helpers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.vaultionizer.vaultserver.model.dto.GenericAuthDto; +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; + +import java.io.IOException; + + +@Component +public class GenericAuthConverter implements Converter { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public GenericAuthDto convert(String json) { + try { + var auth = objectMapper.readValue(json, GenericAuthDto.class); + if (auth == null || auth.getUserID() == null || auth.getSessionKey() == null || auth.getSessionKey().isBlank()) + return null; + return auth; + } catch (IOException ex) { + ex.printStackTrace(); + return null; + } + } +} diff --git a/src/main/java/com/vaultionizer/vaultserver/helpers/Hashing.java b/src/main/java/com/vaultionizer/vaultserver/helpers/Hashing.java index 8024e9a..7c61714 100644 --- a/src/main/java/com/vaultionizer/vaultserver/helpers/Hashing.java +++ b/src/main/java/com/vaultionizer/vaultserver/helpers/Hashing.java @@ -3,13 +3,16 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; public class Hashing { - public static String hashBcrypt(String s){ - BCryptPasswordEncoder bcrypt = new BCryptPasswordEncoder(); + private Hashing() { + } + + public static String hashBcrypt(String s) { + var bcrypt = new BCryptPasswordEncoder(); return bcrypt.encode(s); } - public static boolean checkMatchingHash(String hashed, String plain){ - BCryptPasswordEncoder bcrypt = new BCryptPasswordEncoder(); + public static boolean checkMatchingHash(String hashed, String plain) { + var bcrypt = new BCryptPasswordEncoder(); return bcrypt.matches(plain, hashed); } } diff --git a/src/main/java/com/vaultionizer/vaultserver/helpers/SessionTokenGen.java b/src/main/java/com/vaultionizer/vaultserver/helpers/SessionTokenGen.java index 039c980..de88512 100644 --- a/src/main/java/com/vaultionizer/vaultserver/helpers/SessionTokenGen.java +++ b/src/main/java/com/vaultionizer/vaultserver/helpers/SessionTokenGen.java @@ -6,13 +6,16 @@ import java.util.UUID; public class SessionTokenGen { + private SessionTokenGen() { + } + public static String generateToken() throws NoSuchAlgorithmException { - byte[] content = new byte[64]; + var content = new byte[64]; SecureRandom.getInstance(Config.RANDOM_ALGO).nextBytes(content); return Base64.getEncoder().encodeToString(content); } - public static String generateUUID(){ + public static String generateUUID() { return UUID.randomUUID().toString(); } } diff --git a/src/main/java/com/vaultionizer/vaultserver/jobs/PendingUploadJob.java b/src/main/java/com/vaultionizer/vaultserver/jobs/PendingUploadJob.java index d1de21e..2486ee6 100644 --- a/src/main/java/com/vaultionizer/vaultserver/jobs/PendingUploadJob.java +++ b/src/main/java/com/vaultionizer/vaultserver/jobs/PendingUploadJob.java @@ -16,7 +16,7 @@ public PendingUploadJob(PendingUploadService pendingUploadService) { } @Scheduled(fixedDelay = Config.PENDING_UPLOAD_JOB_DELAY) - public void cleanOldPendingUploads(){ + public void cleanOldPendingUploads() { pendingUploadService.deleteOldPendingUploads(); } } diff --git a/src/main/java/com/vaultionizer/vaultserver/jobs/SessionJob.java b/src/main/java/com/vaultionizer/vaultserver/jobs/SessionJob.java index be95f3f..c2f9eea 100644 --- a/src/main/java/com/vaultionizer/vaultserver/jobs/SessionJob.java +++ b/src/main/java/com/vaultionizer/vaultserver/jobs/SessionJob.java @@ -16,7 +16,7 @@ public SessionJob(SessionService sessionService) { } @Scheduled(fixedDelay = Config.SESSION_JOB_DELAY) - public void cleanSessionsExpired(){ + public void cleanSessionsExpired() { sessionService.cleanAllSessionsExpired(); } } diff --git a/src/main/java/com/vaultionizer/vaultserver/model/db/FileModel.java b/src/main/java/com/vaultionizer/vaultserver/model/db/FileModel.java index bfc5892..39e2b8f 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/db/FileModel.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/db/FileModel.java @@ -45,7 +45,7 @@ public FileStatus getStatus() { } - public void setStatus(FileStatus status){ + public void setStatus(FileStatus status) { this.status = status; } diff --git a/src/main/java/com/vaultionizer/vaultserver/model/db/PendingUploadModel.java b/src/main/java/com/vaultionizer/vaultserver/model/db/PendingUploadModel.java index 2a5e633..859e383 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/db/PendingUploadModel.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/db/PendingUploadModel.java @@ -27,17 +27,21 @@ public class PendingUploadModel { @Min(value = 0, message = "PermittedSessionID cannot be below zero...") private Long permittedSessionID; // not the token but the id of the session + @NotNull(message = "IsUpdate must either be true or false!") + private Boolean isUpdate; // not the token but the id of the session + @PastOrPresent(message = "The upload cannot possibly have been requested in the future!") private Instant requested; public PendingUploadModel() { } - public PendingUploadModel(Long spaceID, Long saveIndex, Long permittedSessionID) { + public PendingUploadModel(Long spaceID, Long saveIndex, Long permittedSessionID, boolean isUpdate) { this.spaceID = spaceID; this.saveIndex = saveIndex; this.permittedSessionID = permittedSessionID; this.requested = Instant.now(); + this.isUpdate = isUpdate; } public Long getUploadID() { @@ -59,4 +63,8 @@ public Long getPermittedSessionID() { public Instant getRequested() { return requested; } + + public Boolean getUpdate() { + return isUpdate; + } } diff --git a/src/main/java/com/vaultionizer/vaultserver/model/db/RefFilesModel.java b/src/main/java/com/vaultionizer/vaultserver/model/db/RefFilesModel.java index ef35431..15275bf 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/db/RefFilesModel.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/db/RefFilesModel.java @@ -26,7 +26,6 @@ public class RefFilesModel { private String content; - public RefFilesModel() { } diff --git a/src/main/java/com/vaultionizer/vaultserver/model/db/SessionModel.java b/src/main/java/com/vaultionizer/vaultserver/model/db/SessionModel.java index ad7b1ed..9c85d39 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/db/SessionModel.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/db/SessionModel.java @@ -70,7 +70,7 @@ public String getWebSocketToken() { return webSocketToken; } - public void update(){ + public void update() { this.lastQuery = Instant.now(); } diff --git a/src/main/java/com/vaultionizer/vaultserver/model/db/SpaceModel.java b/src/main/java/com/vaultionizer/vaultserver/model/db/SpaceModel.java index 7553523..8a84179 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/db/SpaceModel.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/db/SpaceModel.java @@ -22,24 +22,36 @@ public class SpaceModel { @NotNull(message = "Boolean whether it is a private space cannot be null!") private boolean isPrivateSpace; + @NotNull(message = "Boolean whether normal users have write access cannot be null!") + private boolean usersHaveWriteAccess; + + @NotNull(message = "Boolean whether normal users can obtain auth key cannot be null!") + private boolean usersCanGetAuthKey; + private String authKey; public SpaceModel() { } - public SpaceModel(Long spaceID, Long creatorID, Long refFileID, boolean isPrivateSpace, String authKey) { + public SpaceModel(Long spaceID, Long creatorID, Long refFileID, boolean isPrivateSpace, boolean usersHaveWriteAccess, + boolean usersCanGetAuthKey, String authKey) { this.spaceID = spaceID; this.creatorID = creatorID; this.refFileID = refFileID; this.isPrivateSpace = isPrivateSpace; this.authKey = authKey; + this.usersHaveWriteAccess = usersHaveWriteAccess; + this.usersCanGetAuthKey = usersCanGetAuthKey; } - public SpaceModel(Long creatorID, Long refFileID, boolean isPrivateSpace, String authKey) { + public SpaceModel(Long creatorID, Long refFileID, boolean isPrivateSpace, boolean usersHaveWriteAccess, + boolean usersCanGetAuthKey, String authKey) { this.creatorID = creatorID; this.refFileID = refFileID; this.isPrivateSpace = isPrivateSpace; this.authKey = authKey; + this.usersHaveWriteAccess = usersHaveWriteAccess; + this.usersCanGetAuthKey = usersCanGetAuthKey; } public SpaceModel(Long creatorID, Long refFileID) { @@ -67,4 +79,16 @@ public boolean isPrivateSpace() { public String getAuthKey() { return authKey; } + + public boolean getUsersHaveWriteAccess() { + return usersHaveWriteAccess; + } + + public boolean getUsersCanGetAuthKey() { + return usersCanGetAuthKey; + } + + public void setPrivateSpace(boolean privateSpace) { + isPrivateSpace = privateSpace; + } } diff --git a/src/main/java/com/vaultionizer/vaultserver/model/db/UserModel.java b/src/main/java/com/vaultionizer/vaultserver/model/db/UserModel.java index 3b6bc30..875fb3b 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/db/UserModel.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/db/UserModel.java @@ -2,7 +2,6 @@ import com.vaultionizer.vaultserver.helpers.Config; import org.hibernate.validator.constraints.Length; -import org.hibernate.validator.constraints.UniqueElements; import javax.persistence.*; import javax.validation.constraints.NotNull; @@ -19,9 +18,9 @@ public class UserModel { private String username; @NotNull(message = "Key cannot be null!") - @Length(min = Config.MIN_USER_KEY_LENGTH, message = "Hashed key must be at least "+Config.MIN_USER_KEY_LENGTH+" characters long!") + @Length(min = Config.MIN_USER_KEY_LENGTH, message = "Hashed key must be at least " + Config.MIN_USER_KEY_LENGTH + " characters long!") private String key; // Note: key can be blank because a cryptographic key is randomly distributed. - // The length must have a minimum size. + // The length must have a minimum size. public UserModel() { } diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/ChangeAuthKeyDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/ChangeAuthKeyDto.java new file mode 100644 index 0000000..98037d9 --- /dev/null +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/ChangeAuthKeyDto.java @@ -0,0 +1,16 @@ +package com.vaultionizer.vaultserver.model.dto; + +public class ChangeAuthKeyDto { + private String authKey; + + public ChangeAuthKeyDto() { + } + + public ChangeAuthKeyDto(String authKey) { + this.authKey = authKey; + } + + public String getAuthKey() { + return authKey; + } +} diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/ConfigureSpaceDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/ConfigureSpaceDto.java new file mode 100644 index 0000000..66f23df --- /dev/null +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/ConfigureSpaceDto.java @@ -0,0 +1,28 @@ +package com.vaultionizer.vaultserver.model.dto; + +public class ConfigureSpaceDto { + private boolean usersWriteAccess; + private boolean usersAuthAccess; + private Boolean sharedSpace; + + public ConfigureSpaceDto() { + } + + public ConfigureSpaceDto(boolean usersWriteAccess, boolean usersAuthAccess, Boolean sharedSpace) { + this.usersWriteAccess = usersWriteAccess; + this.usersAuthAccess = usersAuthAccess; + this.sharedSpace = sharedSpace; + } + + public Boolean getSharedSpace() { + return sharedSpace; + } + + public boolean getUsersWriteAccess() { + return usersWriteAccess; + } + + public boolean getUsersAuthAccess() { + return usersAuthAccess; + } +} diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/CreateSpaceDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/CreateSpaceDto.java index 4743aac..0cb26ac 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/dto/CreateSpaceDto.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/CreateSpaceDto.java @@ -3,13 +3,21 @@ import com.fasterxml.jackson.annotation.JsonProperty; public class CreateSpaceDto { - private GenericAuthDto auth; private boolean isPrivate; + private boolean usersWriteAccess; + private boolean usersAuthAccess; private String authKey; private String referenceFile; - public GenericAuthDto getAuth() { - return auth; + public CreateSpaceDto() { + } + + public boolean getUsersWriteAccess() { + return usersWriteAccess; + } + + public boolean getUsersAuthAccess() { + return usersAuthAccess; } @JsonProperty("isPrivate") @@ -25,8 +33,7 @@ public String getReferenceFile() { return referenceFile; } - public CreateSpaceDto(GenericAuthDto auth, boolean isPrivate, String authKey, String referenceFile) { - this.auth = auth; + public CreateSpaceDto(boolean isPrivate, String authKey, String referenceFile) { this.isPrivate = isPrivate; this.authKey = authKey; this.referenceFile = referenceFile; diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/DeleteFileDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/DeleteFileDto.java deleted file mode 100644 index d25231d..0000000 --- a/src/main/java/com/vaultionizer/vaultserver/model/dto/DeleteFileDto.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.vaultionizer.vaultserver.model.dto; - -public class DeleteFileDto { - private GenericAuthDto auth; - private Long spaceID; - private Long saveIndex; - - public GenericAuthDto getAuth() { - return auth; - } - - public Long getSpaceID() { - return spaceID; - } - - public Long getSaveIndex() { - return saveIndex; - } -} diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/FileDownloadDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/FileDownloadDto.java deleted file mode 100644 index 9b58f3c..0000000 --- a/src/main/java/com/vaultionizer/vaultserver/model/dto/FileDownloadDto.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.vaultionizer.vaultserver.model.dto; - -public class FileDownloadDto { - private GenericAuthDto auth; - private Long spaceID; - private Long saveIndex; - - public FileDownloadDto() { - } - - public FileDownloadDto(GenericAuthDto auth, Long spaceID, Long saveIndex) { - this.auth = auth; - this.spaceID = spaceID; - this.saveIndex = saveIndex; - } - - public GenericAuthDto getAuth() { - return auth; - } - - public Long getSpaceID() { - return spaceID; - } - - public Long getSaveIndex() { - return saveIndex; - } -} diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/FileUploadDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/FileUploadDto.java index de63519..9ce27cc 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/dto/FileUploadDto.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/FileUploadDto.java @@ -1,22 +1,13 @@ package com.vaultionizer.vaultserver.model.dto; public class FileUploadDto { - private GenericAuthDto auth; - private Long spaceID; private int amountFiles; - public FileUploadDto(GenericAuthDto auth, Long spaceID, int amountFiles) { - this.auth = auth; - this.spaceID = spaceID; - this.amountFiles = amountFiles; - } - - public GenericAuthDto getAuth() { - return auth; + public FileUploadDto() { } - public Long getSpaceID() { - return spaceID; + public FileUploadDto(int amountFiles) { + this.amountFiles = amountFiles; } public int getAmountFiles() { diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/GenericAuthDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/GenericAuthDto.java index 039423e..3d9d758 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/dto/GenericAuthDto.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/GenericAuthDto.java @@ -2,8 +2,11 @@ // A lot of requests need a sessionKey and the user's id public class GenericAuthDto { - private final Long userID; - private final String sessionKey; + private Long userID; + private String sessionKey; + + public GenericAuthDto() { + } public GenericAuthDto(Long userID, String sessionKey) { this.userID = userID; diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/GetSpaceConfigResponseDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/GetSpaceConfigResponseDto.java new file mode 100644 index 0000000..06b0e39 --- /dev/null +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/GetSpaceConfigResponseDto.java @@ -0,0 +1,25 @@ +package com.vaultionizer.vaultserver.model.dto; + +public class GetSpaceConfigResponseDto { + private boolean isPrivate; + private boolean usersHaveWriteAccess; + private boolean usersCanInvite; + + public GetSpaceConfigResponseDto(boolean isPrivate, boolean usersHaveWriteAccess, boolean usersCanInvite) { + this.isPrivate = isPrivate; + this.usersHaveWriteAccess = usersHaveWriteAccess; + this.usersCanInvite = usersCanInvite; + } + + public boolean isPrivate() { + return isPrivate; + } + + public boolean isUsersHaveWriteAccess() { + return usersHaveWriteAccess; + } + + public boolean isUsersCanInvite() { + return usersCanInvite; + } +} diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/GetSpacesResponseDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/GetSpacesResponseDto.java index c76beef..0c35e51 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/dto/GetSpacesResponseDto.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/GetSpacesResponseDto.java @@ -4,11 +4,23 @@ public class GetSpacesResponseDto { private Long spaceID; private boolean isPrivate; private boolean isCreator; + private boolean hasWriteAccess; + private boolean hasAuthKeyAccess; - public GetSpacesResponseDto(Long spaceID, boolean isPrivate, boolean isCreator) { + public GetSpacesResponseDto(Long spaceID, boolean isPrivate, boolean isCreator, boolean hasWriteAccess, boolean hasAuthKeyAccess) { this.spaceID = spaceID; this.isPrivate = isPrivate; this.isCreator = isCreator; + this.hasWriteAccess = hasWriteAccess; + this.hasAuthKeyAccess = hasAuthKeyAccess; + } + + public boolean isHasWriteAccess() { + return hasWriteAccess; + } + + public boolean isHasAuthKeyAccess() { + return hasAuthKeyAccess; } public Long getSpaceID() { diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/GetVersionResponseDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/GetVersionResponseDto.java index 7d18e44..dd770d5 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/dto/GetVersionResponseDto.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/GetVersionResponseDto.java @@ -11,6 +11,9 @@ public GetVersionResponseDto(String version, String maintainer, boolean hasAuthK this.hasAuthKey = hasAuthKey; } + public GetVersionResponseDto() { + } + public String getVersion() { return version; } diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/JoinSpaceDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/JoinSpaceDto.java index d05714e..c0d4259 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/dto/JoinSpaceDto.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/JoinSpaceDto.java @@ -1,24 +1,15 @@ package com.vaultionizer.vaultserver.model.dto; public class JoinSpaceDto { - private GenericAuthDto auth; - private Long spaceID; private String authKey; - public GenericAuthDto getAuth() { - return auth; + public JoinSpaceDto() { } - public JoinSpaceDto(GenericAuthDto auth, Long spaceID, String authKey) { - this.auth = auth; - this.spaceID = spaceID; + public JoinSpaceDto(String authKey) { this.authKey = authKey; } - public Long getSpaceID() { - return spaceID; - } - public String getAuthKey() { return authKey; } diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/LoginUserDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/LoginUserDto.java index 1e93925..072ab60 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/dto/LoginUserDto.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/LoginUserDto.java @@ -4,6 +4,9 @@ public class LoginUserDto { private String username; private String key; + public LoginUserDto() { + } + public LoginUserDto(String username, String key) { this.username = username; this.key = key; diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/LoginUserResponseDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/LoginUserResponseDto.java index 6eab7e9..ace0777 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/dto/LoginUserResponseDto.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/LoginUserResponseDto.java @@ -5,6 +5,9 @@ public class LoginUserResponseDto { private String sessionKey; private String websocketToken; + public LoginUserResponseDto() { + } + public LoginUserResponseDto(Long userID, String sessionKey, String websocketToken) { this.userID = userID; this.sessionKey = sessionKey; diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/ReadRefFileDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/ReadRefFileDto.java index 8b29480..478a140 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/dto/ReadRefFileDto.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/ReadRefFileDto.java @@ -4,25 +4,16 @@ import java.time.Instant; public class ReadRefFileDto { - private GenericAuthDto auth; - private Long spaceID; - private Instant lastRead; + private Long lastRead; - public ReadRefFileDto(GenericAuthDto auth, Long spaceID, Instant lastRead) { - this.auth = auth; - this.spaceID = spaceID; - this.lastRead = lastRead; - } - - public GenericAuthDto getAuth() { - return auth; + public ReadRefFileDto() { } - public Long getSpaceID() { - return spaceID; + public ReadRefFileDto(Long lastRead) { + this.lastRead = lastRead; } - public Instant getLastRead() { + public Long getLastRead() { return lastRead; } } diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/SpaceAuthKeyDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/SpaceAuthKeyDto.java index 32736de..275f0a7 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/dto/SpaceAuthKeyDto.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/SpaceAuthKeyDto.java @@ -1,16 +1,13 @@ package com.vaultionizer.vaultserver.model.dto; public class SpaceAuthKeyDto { - private GenericAuthDto auth; private Long spaceID; - public SpaceAuthKeyDto(GenericAuthDto auth, Long spaceID) { - this.auth = auth; - this.spaceID = spaceID; + public SpaceAuthKeyDto() { } - public GenericAuthDto getAuth() { - return auth; + public SpaceAuthKeyDto(Long spaceID) { + this.spaceID = spaceID; } public Long getSpaceID() { diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/SpaceAuthKeyResponseDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/SpaceAuthKeyResponseDto.java index 2dca7da..66dba57 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/dto/SpaceAuthKeyResponseDto.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/SpaceAuthKeyResponseDto.java @@ -4,8 +4,19 @@ public class SpaceAuthKeyResponseDto { private Long spaceID; private String authKey; + public SpaceAuthKeyResponseDto() { + } + public SpaceAuthKeyResponseDto(Long spaceID, String authKey) { this.spaceID = spaceID; this.authKey = authKey; } + + public Long getSpaceID() { + return spaceID; + } + + public String getAuthKey() { + return authKey; + } } diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/UpdateRefFileDto.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/UpdateRefFileDto.java index 86decc3..a2a6096 100644 --- a/src/main/java/com/vaultionizer/vaultserver/model/dto/UpdateRefFileDto.java +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/UpdateRefFileDto.java @@ -1,22 +1,13 @@ package com.vaultionizer.vaultserver.model.dto; public class UpdateRefFileDto { - private GenericAuthDto auth; - private Long spaceID; private String content; - public UpdateRefFileDto(GenericAuthDto auth, Long spaceID, String content) { - this.auth = auth; - this.spaceID = spaceID; - this.content = content; - } - - public GenericAuthDto getAuth() { - return auth; + public UpdateRefFileDto() { } - public Long getSpaceID() { - return spaceID; + public UpdateRefFileDto(String content) { + this.content = content; } public String getContent() { diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/wserrors/GenericWSError.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/wserrors/GenericWSError.java new file mode 100644 index 0000000..6fc07a3 --- /dev/null +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/wserrors/GenericWSError.java @@ -0,0 +1,19 @@ +package com.vaultionizer.vaultserver.model.dto.wserrors; + +public class GenericWSError { + private final WS_ERROR type; + private final WSErrorData data; + + public GenericWSError(WS_ERROR type, WSErrorData data) { + this.type = type; + this.data = data; + } + + public WS_ERROR getType() { + return type; + } + + public WSErrorData getData() { + return data; + } +} diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/wserrors/UploadData.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/wserrors/UploadData.java new file mode 100644 index 0000000..62443fc --- /dev/null +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/wserrors/UploadData.java @@ -0,0 +1,31 @@ +package com.vaultionizer.vaultserver.model.dto.wserrors; + +public class UploadData extends WSErrorData { + private final Long userID; + private final Long spaceID; + private final Long saveIndex; + private final String sessionKey; + + public UploadData(Long userID, Long spaceID, Long saveIndex, String sessionKey) { + this.userID = userID; + this.spaceID = spaceID; + this.saveIndex = saveIndex; + this.sessionKey = sessionKey; + } + + public String getSessionKey() { + return sessionKey; + } + + public Long getUserID() { + return userID; + } + + public Long getSpaceID() { + return spaceID; + } + + public Long getSaveIndex() { + return saveIndex; + } +} diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/wserrors/WSErrorData.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/wserrors/WSErrorData.java new file mode 100644 index 0000000..2d96bd0 --- /dev/null +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/wserrors/WSErrorData.java @@ -0,0 +1,4 @@ +package com.vaultionizer.vaultserver.model.dto.wserrors; + +public class WSErrorData { +} diff --git a/src/main/java/com/vaultionizer/vaultserver/model/dto/wserrors/WS_ERROR.java b/src/main/java/com/vaultionizer/vaultserver/model/dto/wserrors/WS_ERROR.java new file mode 100644 index 0000000..615d963 --- /dev/null +++ b/src/main/java/com/vaultionizer/vaultserver/model/dto/wserrors/WS_ERROR.java @@ -0,0 +1,7 @@ +package com.vaultionizer.vaultserver.model.dto.wserrors; + +public enum WS_ERROR { + MISSHAPEN_UPLOAD, + UPLOAD_NOT_GRANTED, + UPLOAD_UNSUCCESSFUL +} \ No newline at end of file diff --git a/src/main/java/com/vaultionizer/vaultserver/resource/FileRepository.java b/src/main/java/com/vaultionizer/vaultserver/resource/FileRepository.java index a9afd15..b1554b2 100644 --- a/src/main/java/com/vaultionizer/vaultserver/resource/FileRepository.java +++ b/src/main/java/com/vaultionizer/vaultserver/resource/FileRepository.java @@ -1,5 +1,6 @@ package com.vaultionizer.vaultserver.resource; +import com.vaultionizer.vaultserver.helpers.FileStatus; import com.vaultionizer.vaultserver.model.db.FileModel; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; @@ -24,4 +25,10 @@ public interface FileRepository extends JpaRepository { @Query("SELECT it FROM FileModel it WHERE it.spaceID = ?1") Set getAllFiles(Long spaceID); + + + @Transactional + @Modifying + @Query("UPDATE FileModel SET status = ?3 WHERE spaceID = ?1 AND saveIndex = ?2") + void updateFileStatus(Long spaceID, Long saveIndex, FileStatus status); } \ No newline at end of file diff --git a/src/main/java/com/vaultionizer/vaultserver/resource/PendingUploadRepository.java b/src/main/java/com/vaultionizer/vaultserver/resource/PendingUploadRepository.java index a1bef95..c4b0b28 100644 --- a/src/main/java/com/vaultionizer/vaultserver/resource/PendingUploadRepository.java +++ b/src/main/java/com/vaultionizer/vaultserver/resource/PendingUploadRepository.java @@ -17,6 +17,10 @@ public interface PendingUploadRepository extends JpaRepository { int checkUnique(String webSocketToken, String sessionKey); @Query("SELECT COUNT(it) FROM SessionModel it " + - "WHERE it.userID = ?1 AND it.webSocketToken = ?2 AND it.sessionKey = ?3 AND it.lastQuery > ?4") + "WHERE it.userID = ?1 AND it.webSocketToken = ?2 AND it.sessionKey = ?3 AND it.lastQuery >= ?4") int checkValidWebsocketToken(Long userID, String webSocketToken, String sessionKey, Instant now); @Transactional diff --git a/src/main/java/com/vaultionizer/vaultserver/resource/SpaceRepository.java b/src/main/java/com/vaultionizer/vaultserver/resource/SpaceRepository.java index 79924fd..36b9942 100644 --- a/src/main/java/com/vaultionizer/vaultserver/resource/SpaceRepository.java +++ b/src/main/java/com/vaultionizer/vaultserver/resource/SpaceRepository.java @@ -1,6 +1,7 @@ package com.vaultionizer.vaultserver.resource; import com.vaultionizer.vaultserver.model.db.SpaceModel; +import com.vaultionizer.vaultserver.model.dto.GetSpaceConfigResponseDto; import com.vaultionizer.vaultserver.model.dto.SpaceAuthKeyResponseDto; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; @@ -32,4 +33,27 @@ public interface SpaceRepository extends JpaRepository { @Modifying @Query("DELETE FROM SpaceModel it WHERE it.spaceID = ?1") void deleteSpace(Long spaceID); + + @Query("SELECT COUNT(it) FROM SpaceModel it WHERE it.spaceID = ?1 " + + "AND (it.creatorID = ?2 OR it.usersHaveWriteAccess = true)") + int getUserWriteAccess(Long spaceID, Long userID); // user has write access if creator or normal users have write access + + @Query("SELECT COUNT(it) FROM SpaceModel it WHERE it.spaceID = ?1 " + + "AND (it.creatorID = ?2 OR it.usersCanGetAuthKey = true)") + int getUserAuthKeyAccess(Long spaceID, Long userID); // user has access to auth key if creator or normal users have access to auth key + + @Transactional + @Modifying + @Query("UPDATE SpaceModel it SET it.usersHaveWriteAccess = ?2, it.usersCanGetAuthKey = ?3 WHERE it.spaceID = ?1") + void configureSpace(Long spaceID, boolean writeAccess, boolean authKeyAccess); + + @Query("SELECT new com.vaultionizer.vaultserver.model.dto.GetSpaceConfigResponseDto(it.isPrivateSpace, it.usersHaveWriteAccess, it.usersCanGetAuthKey) " + + "FROM SpaceModel it WHERE it.spaceID = ?1") + GetSpaceConfigResponseDto getSpaceConfig(Long spaceID); + + + @Transactional + @Modifying + @Query("UPDATE SpaceModel it SET it.authKey = ?2 WHERE it.spaceID = ?1") + void updateAuthKey(Long spaceID, String authKey); } diff --git a/src/main/java/com/vaultionizer/vaultserver/resource/UserAccessRepository.java b/src/main/java/com/vaultionizer/vaultserver/resource/UserAccessRepository.java index 87aba6c..81384bf 100644 --- a/src/main/java/com/vaultionizer/vaultserver/resource/UserAccessRepository.java +++ b/src/main/java/com/vaultionizer/vaultserver/resource/UserAccessRepository.java @@ -20,6 +20,11 @@ public interface UserAccessRepository extends JpaRepository ?2") + void kickAllUsers(Long spaceID, Long creatorID); + @Transactional @Modifying @Query("DELETE FROM UserAccessModel it WHERE it.userID = ?1") diff --git a/src/main/java/com/vaultionizer/vaultserver/service/FileService.java b/src/main/java/com/vaultionizer/vaultserver/service/FileService.java index 70af31e..7c16d82 100644 --- a/src/main/java/com/vaultionizer/vaultserver/service/FileService.java +++ b/src/main/java/com/vaultionizer/vaultserver/service/FileService.java @@ -25,11 +25,11 @@ public FileService(FileRepository fileRepository) { this.readMap = new HashMap<>(); } - public long countFilesInSpace(Long spaceID){ + public long countFilesInSpace(Long spaceID) { return fileRepository.countFilesInSpace(spaceID); } - public void deleteAllFilesInSpace(Long spaceID){ + public void deleteAllFilesInSpace(Long spaceID) { var ids = fileRepository.getAllFiles(spaceID); fileRepository.deleteFiles(spaceID); synchronized (readMap) { @@ -39,7 +39,7 @@ public void deleteAllFilesInSpace(Long spaceID){ } - public boolean setUploadFile(Long spaceID, Long saveIndex){ + public boolean setUploadFile(Long spaceID, Long saveIndex) { FileModel model = findFile(spaceID, saveIndex); if (model != null) return false; @@ -48,10 +48,10 @@ public boolean setUploadFile(Long spaceID, Long saveIndex){ return true; } - public FileStatus setDownloadFile(Long spaceID, Long saveIndex){ + public FileStatus setDownloadFile(Long spaceID, Long saveIndex) { FileModel model = findFile(spaceID, saveIndex); if (model == null) return null; - switch (model.getStatus()){ + switch (model.getStatus()) { case ACCESSIBLE: model.setStatus(FileStatus.READ_FROM); fileRepository.save(model); @@ -66,13 +66,12 @@ public FileStatus setDownloadFile(Long spaceID, Long saveIndex){ } } - public boolean writeToFile(String content, Long spaceID, Long saveIndex){ + public boolean writeToFile(String content, Long spaceID, Long saveIndex) { FileModel model = findFile(spaceID, saveIndex); if (model == null || model.getStatus() != FileStatus.UPLOADING) return false; - File f = new File(getFilePath(spaceID, saveIndex)); - if (!f.exists()){ + var f = new File(getFilePath(spaceID, saveIndex)); + if (!f.exists()) { try { - System.out.println(f.getParentFile().toString()); f.getParentFile().mkdirs(); f.createNewFile(); } catch (IOException e) { @@ -80,7 +79,7 @@ public boolean writeToFile(String content, Long spaceID, Long saveIndex){ return false; } } - try (PrintWriter writer = new PrintWriter(new FileWriter(f, false))){ + try (var writer = new PrintWriter(new FileWriter(f, false))) { writer.print(content); writer.flush(); } catch (IOException e) { @@ -92,54 +91,81 @@ public boolean writeToFile(String content, Long spaceID, Long saveIndex){ return true; } - public String makeDownload(Long spaceID, Long saveIndex){ - FileModel fileModel = findFile(spaceID, saveIndex); + public boolean tryUpdating(String content, Long spaceID, Long saveIndex) { + if (!setUpdating(spaceID, saveIndex)) return false; + var file = new File(getFilePath(spaceID, saveIndex)); + if (!file.exists()) return false; + + try (var writer = new PrintWriter(new FileWriter(file, false))) { + writer.print(content); + writer.flush(); + } catch (IOException e) { + e.printStackTrace(); + setDoneUpdating(spaceID, saveIndex); + return false; + } + setDoneUpdating(spaceID, saveIndex); + return true; + } + + public String makeDownload(Long spaceID, Long saveIndex) { + var fileModel = findFile(spaceID, saveIndex); if (fileModel == null) return null; if (readMap.get(fileModel.getFileID()) == 0 || readMap.get(fileModel.getFileID()) == null || fileModel.getStatus() != FileStatus.READ_FROM) return null; String content = readFromFile(spaceID, saveIndex); - if (readMap.get(fileModel.getFileID()) == 1){ + if (readMap.get(fileModel.getFileID()) == 1) { readMap.remove(fileModel.getFileID()); fileModel.setStatus(FileStatus.ACCESSIBLE); - } - else{ + } else { Integer amount = readMap.get(fileModel.getFileID()); - if (amount <= 0 || amount == null) return null; + if (amount <= 0) return null; amount--; readMap.put(fileModel.getFileID(), amount); } return content; } - public String readFromFile(Long spaceID, Long saveIndex){ - File f = new File(getFilePath(spaceID, saveIndex)); + public String readFromFile(Long spaceID, Long saveIndex) { + var f = new File(getFilePath(spaceID, saveIndex)); if (!f.exists()) return null; - StringBuilder builder = new StringBuilder(); - try{ + try { return Files.readString(f.toPath().toAbsolutePath()); } catch (IOException e) { return null; } } - private String getFilePath(Long spaceID, Long saveIndex){ - return Config.SPACE_PATH +spaceID+"/"+saveIndex+".bin"; + private String getFilePath(Long spaceID, Long saveIndex) { + return Config.SPACE_PATH + spaceID + "/" + saveIndex + ".bin"; } - private String getFilePath(Long spaceID){ - return Config.SPACE_PATH +spaceID; + private String getFilePath(Long spaceID) { + return Config.SPACE_PATH + spaceID; } - private synchronized FileModel findFile(Long spaceID, Long saveIndex){ + private synchronized FileModel findFile(Long spaceID, Long saveIndex) { return fileRepository.findFile(spaceID, saveIndex); } - public boolean deleteFile(Long spaceID, Long saveIndex){ + private synchronized boolean setUpdating(Long spaceID, Long saveIndex) { + FileModel file = fileRepository.findFile(spaceID, saveIndex); + if (file.getStatus() == FileStatus.UPLOADING || file.getStatus() == FileStatus.MODIFYING) return false; + file.setStatus(FileStatus.MODIFYING); + fileRepository.save(file); + return true; + } + + private synchronized void setDoneUpdating(Long spaceID, Long saveIndex) { + fileRepository.updateFileStatus(spaceID, saveIndex, FileStatus.ACCESSIBLE); + } + + public boolean deleteFile(Long spaceID, Long saveIndex) { FileModel file = fileRepository.findFile(spaceID, saveIndex); if (file == null) return true; - switch (file.getStatus()){ + switch (file.getStatus()) { case ACCESSIBLE: case READ_FROM: fileRepository.delete(file); @@ -149,24 +175,29 @@ public boolean deleteFile(Long spaceID, Long saveIndex){ } } - private void deleteDirectory(Long spaceID){ - File file = new File(getFilePath(spaceID)); - if (file.isDirectory()){ + private void deleteDirectory(Long spaceID) { + var file = new File(getFilePath(spaceID)); + if (file.isDirectory()) { file.delete(); } } - private boolean removeFileFromDisk(Long spaceID, Long saveIndex){ - File file = new File(getFilePath(spaceID, saveIndex)); + private boolean removeFileFromDisk(Long spaceID, Long saveIndex) { + var file = new File(getFilePath(spaceID, saveIndex)); if (file.exists()) return file.delete(); return true; } // for testing - public void setModified(Long spaceID, Long saveIndex){ - FileModel fileModel = findFile(spaceID, saveIndex); + public void setModified(Long spaceID, Long saveIndex) { + var fileModel = findFile(spaceID, saveIndex); fileModel.setStatus(FileStatus.MODIFYING); fileRepository.save(fileModel); } + + public boolean fileExists(Long spaceID, Long saveIndex) { + var file = new File(getFilePath(spaceID, saveIndex)); + return file.exists() && findFile(spaceID, saveIndex) != null; + } } diff --git a/src/main/java/com/vaultionizer/vaultserver/service/PendingUploadService.java b/src/main/java/com/vaultionizer/vaultserver/service/PendingUploadService.java index 494699c..83958b0 100644 --- a/src/main/java/com/vaultionizer/vaultserver/service/PendingUploadService.java +++ b/src/main/java/com/vaultionizer/vaultserver/service/PendingUploadService.java @@ -11,42 +11,55 @@ @Service public class PendingUploadService { private final PendingUploadRepository pendingUploadRepository; + private final FileService fileService; @Autowired - public PendingUploadService(PendingUploadRepository pendingUploadRepository) { + public PendingUploadService(PendingUploadRepository pendingUploadRepository, FileService fileService) { this.pendingUploadRepository = pendingUploadRepository; + this.fileService = fileService; } - public void addFilesToUpload(Long spaceID, Long sessionID, Long amountValues, Long saveIndex){ + public void addFilesToUpload(Long spaceID, Long sessionID, Long amountValues, Long saveIndex) { PendingUploadModel model; for (long i = 0; i < amountValues; i++) { - model = new PendingUploadModel(spaceID, saveIndex+i, sessionID); + model = new PendingUploadModel(spaceID, saveIndex + i, sessionID, false); this.pendingUploadRepository.save(model); } } - public boolean uploadFile(Long spaceID, Long sessionID, Long saveIndex){ + // returns 0 if not granted, 1 if usual upload and 2 if update + public int uploadFile(Long spaceID, Long sessionID, Long saveIndex) { var model = pendingUploadRepository.findItem(spaceID, sessionID, saveIndex); - if (model != null){ + if (model != null) { pendingUploadRepository.delete(model); - return true; + if (!model.getUpdate()) return 1; + else return 2; } - return false; + return 0; } - public void deleteAllPendingUploads(Long spaceID){ + public boolean updateFile(Long spaceID, Long sessionID, Long saveIndex) { + if (!fileService.fileExists(spaceID, saveIndex)) return false; + if (pendingUploadRepository.isPending(spaceID, saveIndex) > 0) return false; + PendingUploadModel model; + model = new PendingUploadModel(spaceID, saveIndex, sessionID, true); + this.pendingUploadRepository.save(model); + return true; + } + + public void deleteAllPendingUploads(Long spaceID) { pendingUploadRepository.deletePendingUploads(spaceID); } - public void deletePendingUploadsByUser(Long userID){ + public void deletePendingUploadsByUser(Long userID) { pendingUploadRepository.deleteAllByUser(userID); } - public void deleteOldPendingUploads(){ + public void deleteOldPendingUploads() { pendingUploadRepository.deleteOldUploads(Instant.now().minusSeconds(Config.MAX_UPLOAD_AGE)); } - public long countPendingUploadsForSpace(Long spaceID){ + public long countPendingUploadsForSpace(Long spaceID) { return pendingUploadRepository.countBySpace(spaceID); } } diff --git a/src/main/java/com/vaultionizer/vaultserver/service/RefFileService.java b/src/main/java/com/vaultionizer/vaultserver/service/RefFileService.java index 6905a27..60c29b5 100644 --- a/src/main/java/com/vaultionizer/vaultserver/service/RefFileService.java +++ b/src/main/java/com/vaultionizer/vaultserver/service/RefFileService.java @@ -4,6 +4,7 @@ import com.vaultionizer.vaultserver.resource.RefFileRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; + import java.time.Instant; import java.util.Optional; import java.util.Set; @@ -17,11 +18,11 @@ public RefFileService(RefFileRepository refFileRepository) { this.refFileRepository = refFileRepository; } - public Long addNewRefFile(String content){ + public Long addNewRefFile(String content) { return refFileRepository.save(new RefFilesModel(content)).getRefFileId(); } - public Long requestUploadFiles(Long refFileID, Long amountValues){ + public Long requestUploadFiles(Long refFileID, Long amountValues) { Optional model = this.refFileRepository.findById(refFileID); if (model.isEmpty()) return -1L; RefFilesModel m = model.get(); @@ -31,17 +32,17 @@ public Long requestUploadFiles(Long refFileID, Long amountValues){ return saveIndex; } - public String readRefFile(Long refFileID){ + public String readRefFile(Long refFileID) { Set refFileContents = refFileRepository.getRefFileContent(refFileID); - if (refFileContents == null || refFileContents.size() != 1){ + if (refFileContents == null || refFileContents.size() != 1) { return null; } return refFileContents.stream().findFirst().get(); } - public boolean updateRefFile(Long refFileID, String content){ + public boolean updateRefFile(Long refFileID, String content) { Set models = refFileRepository.getRefFile(refFileID); - if (models == null || models.size() != 1){ + if (models == null || models.size() != 1) { return false; } RefFilesModel model = models.stream().findFirst().get(); @@ -50,11 +51,11 @@ public boolean updateRefFile(Long refFileID, String content){ return true; } - public boolean hasNewVersion(Long refFileID, Instant lastFetched){ + public boolean hasNewVersion(Long refFileID, Instant lastFetched) { return refFileRepository.checkNewVersion(refFileID, lastFetched) == 1; } - public void deleteRefFile(Long refFileID){ + public void deleteRefFile(Long refFileID) { this.refFileRepository.deleteRefFile(refFileID); } } diff --git a/src/main/java/com/vaultionizer/vaultserver/service/SessionService.java b/src/main/java/com/vaultionizer/vaultserver/service/SessionService.java index d4e921e..ff076bd 100644 --- a/src/main/java/com/vaultionizer/vaultserver/service/SessionService.java +++ b/src/main/java/com/vaultionizer/vaultserver/service/SessionService.java @@ -20,69 +20,67 @@ public SessionService(SessionRepository sessionRepository) { this.sessionRepository = sessionRepository; } - private void updateSessionTimeStamp(SessionModel model){ + private void updateSessionTimeStamp(SessionModel model) { model.update(); sessionRepository.save(model); } public LoginUserResponseDto addSession(Long userID) { SessionModel session = null; - do - { + do { try { session = new SessionModel(userID); - } - catch(NoSuchAlgorithmException e) { + } catch (NoSuchAlgorithmException e) { e.printStackTrace(); continue; } - } while(sessionRepository.checkUnique(session.getWebSocketToken(), session.getSessionKey()) > 0); + } while (sessionRepository.checkUnique(session.getWebSocketToken(), session.getSessionKey()) > 0); session = sessionRepository.save(session); return new LoginUserResponseDto(session.getUserID(), session.getSessionKey(), session.getWebSocketToken()); } - public boolean getSession(Long userID, String sessionKey){ - SessionModel model = getSessionModel(userID, sessionKey); - return model!=null; + public boolean getSession(Long userID, String sessionKey) { + var model = getSessionModel(userID, sessionKey); + return model != null; } - public Long getSessionID(Long userID, String sessionKey){ - SessionModel model = getSessionModel(userID, sessionKey); + public Long getSessionID(Long userID, String sessionKey) { + var model = getSessionModel(userID, sessionKey); return model == null ? -1L : model.getId(); } - public String getSessionWebsocketToken(Long userID, String sessionKey){ - SessionModel model = getSessionModel(userID, sessionKey); + public String getSessionWebsocketToken(Long userID, String sessionKey) { + var model = getSessionModel(userID, sessionKey); return model == null ? null : model.getWebSocketToken(); } - private SessionModel getSessionModel(Long userID, String sessionKey){ + private SessionModel getSessionModel(Long userID, String sessionKey) { Set sessions = sessionRepository.getSessionModelByKey(userID, sessionKey, getAllowedAge()); - if(sessions.size() == 1) { - SessionModel sessionModel = sessions.stream().findFirst().get(); + if (sessions.size() == 1) { + var sessionModel = sessions.stream().findFirst().get(); updateSessionTimeStamp(sessionModel); return sessionModel; } return null; } - public boolean checkValidWebsocketToken(Long userID, String websocketToken, String sessionKey){ + public boolean checkValidWebsocketToken(Long userID, String websocketToken, String sessionKey) { return sessionRepository.checkValidWebsocketToken(userID, websocketToken, sessionKey, getAllowedAge()) == 1; } - public void deleteSession(Long userID, String sessionKey){ + public void deleteSession(Long userID, String sessionKey) { sessionRepository.deleteSession(userID, sessionKey); } - public void deleteAllSessionsWithUser(Long userID){ + public void deleteAllSessionsWithUser(Long userID) { sessionRepository.logOutUser(userID); } - private Instant getAllowedAge(){ + private Instant getAllowedAge() { return Instant.now().minusSeconds(Config.MAX_SESSION_AGE); } - public void cleanAllSessionsExpired(){ + public void cleanAllSessionsExpired() { this.sessionRepository.deleteAllOldSessions(getAllowedAge()); } } diff --git a/src/main/java/com/vaultionizer/vaultserver/service/SpaceService.java b/src/main/java/com/vaultionizer/vaultserver/service/SpaceService.java index 5318df6..0403ecb 100644 --- a/src/main/java/com/vaultionizer/vaultserver/service/SpaceService.java +++ b/src/main/java/com/vaultionizer/vaultserver/service/SpaceService.java @@ -1,14 +1,17 @@ package com.vaultionizer.vaultserver.service; -import com.vaultionizer.vaultserver.model.db.RefFilesModel; import com.vaultionizer.vaultserver.model.db.SpaceModel; +import com.vaultionizer.vaultserver.model.dto.GetSpaceConfigResponseDto; import com.vaultionizer.vaultserver.model.dto.GetSpacesResponseDto; import com.vaultionizer.vaultserver.model.dto.SpaceAuthKeyResponseDto; import com.vaultionizer.vaultserver.resource.SpaceRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; @Service public class SpaceService { @@ -29,26 +32,28 @@ public SpaceService(SpaceRepository spaceRepository, RefFileService refFileServi deleteLock = new Object(); } - public GetSpacesResponseDto getSpace(Long spaceID, Long userID){ + public GetSpacesResponseDto getSpace(Long spaceID, Long userID) { Optional model = spaceRepository.findById(spaceID); if (model.isEmpty()) return null; - return new GetSpacesResponseDto(spaceID, model.get().isPrivateSpace(), model.get().getCreatorID().equals(userID)); + return new GetSpacesResponseDto(spaceID, model.get().isPrivateSpace(), model.get().getCreatorID().equals(userID), + model.get().getUsersHaveWriteAccess(), model.get().getUsersCanGetAuthKey()); } - public Long createSpace(Long userID, String refFileContent, boolean isPrivate, String authKey){ + public Long createSpace(Long userID, String refFileContent, boolean isPrivate, boolean usersWriteAccess, + boolean usersCanInvite, String authKey) { Long refFileID = refFileService.addNewRefFile(refFileContent); - SpaceModel model = new SpaceModel(userID, refFileID, isPrivate, authKey); + var model = new SpaceModel(userID, refFileID, isPrivate, usersWriteAccess, usersCanInvite, authKey); model = spaceRepository.save(model); userAccessService.addUserAccess(model.getSpaceID(), userID); return model.getSpaceID(); } - public Optional getSpaceAuthKey(Long spaceID){ + public Optional getSpaceAuthKey(Long spaceID) { return spaceRepository.getSpaceAuthKey(spaceID); } // returns the spaces a user has access to - public ArrayList getSpacesAccessible(Long userID){ + public ArrayList getSpacesAccessible(Long userID) { ArrayList spaces = new ArrayList<>(); Set spaceIDs = userAccessService.getAllWithUser(userID); spaceIDs.forEach(spaceID -> { @@ -61,25 +66,25 @@ public ArrayList getSpacesAccessible(Long userID){ } // checks whether a space's credentials equal the given ones. - public boolean checkSpaceCredentials(Long spaceID, String authKey){ + public boolean checkSpaceCredentials(Long spaceID, String authKey) { return spaceRepository.checkJoinableWithCredentials(spaceID, authKey) == 1; } - public Long getRefFileID(Long spaceID){ + public Long getRefFileID(Long spaceID) { var id = this.spaceRepository.getRefFileID(spaceID); if (id.isEmpty()) return -1L; return id.get(); } - public Set getAllOwnedSpaces(Long userID){ + public Set getAllOwnedSpaces(Long userID) { return spaceRepository.getAllOwnedSpaces(userID); } - public boolean checkCreator(Long spaceID, Long userID){ + public boolean checkCreator(Long spaceID, Long userID) { return spaceRepository.checkIsCreator(spaceID, userID) == 1; } - public boolean markSpaceDeleted(Long spaceID){ + public boolean markSpaceDeleted(Long spaceID) { synchronized (deleteLock) { if (this.isDeleted.contains(spaceID)) { return false; @@ -90,14 +95,52 @@ public boolean markSpaceDeleted(Long spaceID){ return true; } - public void deleteSpace(Long spaceID){ + public void deleteSpace(Long spaceID) { spaceRepository.deleteSpace(spaceID); - synchronized (deleteLock){ + synchronized (deleteLock) { this.isDeleted.remove(spaceID); } } - public synchronized boolean checkDeleted(Long spaceID){ + public synchronized boolean checkDeleted(Long spaceID) { return this.isDeleted.contains(spaceID); } + + public boolean userHasWriteAccess(Long spaceID, Long userID) { + return spaceRepository.getUserWriteAccess(spaceID, userID) == 1; + } + + public boolean userHasAuthKeyAccess(Long spaceID, Long userID) { + return spaceRepository.getUserAuthKeyAccess(spaceID, userID) == 1; + } + + public void configureSpace(Long spaceID, boolean writeAccess, boolean authKeyAccess) { + spaceRepository.configureSpace(spaceID, writeAccess, authKeyAccess); + } + + public void changeSharedState(Long spaceID, Long creatorID, boolean shared) { + var spaceModel = spaceRepository.findById(spaceID); + if (spaceModel.isEmpty() || spaceModel.get().isPrivateSpace() == !shared) return; + var model = spaceModel.get(); + model.setPrivateSpace(!shared); + spaceRepository.save(model); + if (!shared) { + // shared -> private: remove all accesses + userAccessService.kickAll(spaceID, creatorID); + } + } + + public Boolean checkShared(Long spaceID) { + var model = spaceRepository.findById(spaceID); + if (model.isEmpty()) return null; + return !model.get().isPrivateSpace(); + } + + public GetSpaceConfigResponseDto getSpaceConfig(Long spaceID) { + return spaceRepository.getSpaceConfig(spaceID); + } + + public void changeAuthKey(Long spaceID, String authKey) { + spaceRepository.updateAuthKey(spaceID, authKey); + } } diff --git a/src/main/java/com/vaultionizer/vaultserver/service/UserAccessService.java b/src/main/java/com/vaultionizer/vaultserver/service/UserAccessService.java index 294e9fd..434ab67 100644 --- a/src/main/java/com/vaultionizer/vaultserver/service/UserAccessService.java +++ b/src/main/java/com/vaultionizer/vaultserver/service/UserAccessService.java @@ -17,32 +17,36 @@ public UserAccessService(UserAccessRepository userAccessRepository) { this.userAccessRepository = userAccessRepository; } - public void addUserAccess(Long spaceID, Long userID){ + public void addUserAccess(Long spaceID, Long userID) { userAccessRepository.save(new UserAccessModel(userID, spaceID)); } - public void deleteAllWithSpace(Long spaceID){ + public void deleteAllWithSpace(Long spaceID) { userAccessRepository.deleteSpace(spaceID); } - public void deleteAllWithUser(Long userID){ + public void deleteAllWithUser(Long userID) { userAccessRepository.removeUser(userID); } // returns all spaceIDs a userID is associated with - public Set getAllWithUser(Long userID){ + public Set getAllWithUser(Long userID) { return userAccessRepository.getSpacesAccessible(userID); } - public boolean userHasAccess(Long userID, Long spaceID){ + public boolean userHasAccess(Long userID, Long spaceID) { return userAccessRepository.hasAccess(userID, spaceID) == 1; } - public boolean removeAccess(Long userID, Long spaceID){ - if (this.userAccessRepository.hasAccess(userID, spaceID) == 1){ + public boolean removeAccess(Long userID, Long spaceID) { + if (this.userAccessRepository.hasAccess(userID, spaceID) == 1) { this.userAccessRepository.removeUserFromSpace(userID, spaceID); return true; } return false; } + + public void kickAll(Long spaceID, Long creatorID) { + userAccessRepository.kickAllUsers(spaceID, creatorID); + } } diff --git a/src/main/java/com/vaultionizer/vaultserver/service/UserService.java b/src/main/java/com/vaultionizer/vaultserver/service/UserService.java index f1cc88d..505452e 100644 --- a/src/main/java/com/vaultionizer/vaultserver/service/UserService.java +++ b/src/main/java/com/vaultionizer/vaultserver/service/UserService.java @@ -3,9 +3,7 @@ import com.vaultionizer.vaultserver.helpers.Hashing; import com.vaultionizer.vaultserver.model.db.UserModel; import com.vaultionizer.vaultserver.resource.UserRepository; -import org.apache.catalina.User; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import java.util.HashSet; @@ -22,9 +20,9 @@ public UserService(UserRepository userRepository) { deletedUsers = new HashSet<>(); } - public Long getUserIDCheckCredentials(String username, String key){ + public Long getUserIDCheckCredentials(String username, String key) { Set users = userRepository.getPwd(username); - if (users.size() != 1){ + if (users.size() != 1) { return -1L; } UserModel model = users.stream().findFirst().get(); @@ -32,20 +30,21 @@ public Long getUserIDCheckCredentials(String username, String key){ return deletedUsers.contains(userID) ? -1L : userID; } - public Long createUser(String username, String key){ - try {return userRepository.save(new UserModel(username, Hashing.hashBcrypt(key))).getId();} - catch (Exception e){ // in case that username exists + public Long createUser(String username, String key) { + try { + return userRepository.save(new UserModel(username, Hashing.hashBcrypt(key))).getId(); + } catch (Exception e) { // in case that username exists return null; } } - public synchronized boolean setDeleted(Long userID){ + public synchronized boolean setDeleted(Long userID) { if (deletedUsers.contains(userID)) return false; deletedUsers.add(userID); return true; } - public synchronized void setDeletionDone(Long userID){ + public synchronized void setDeletionDone(Long userID) { userRepository.deleteUser(userID); deletedUsers.remove(userID); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index c6d291a..0b10a7f 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,5 +1,4 @@ - -server.port=443 +server.port=8080 spring.application.name=vaultserver # set password here (next line)! @@ -15,4 +14,4 @@ spring.datasource.username=${VAULT_DB_USER} spring.datasource.password=${VAULT_DB_PASSWORD} spring.jpa.hibernate.ddl-auto=create - +springdoc.api-docs.path=/api-docs diff --git a/src/test/java/com/vaultionizer/vaultserver/CucumberIntegrationTest.java b/src/test/java/com/vaultionizer/vaultserver/CucumberIntegrationTest.java index 22dfc81..ccc1460 100644 --- a/src/test/java/com/vaultionizer/vaultserver/CucumberIntegrationTest.java +++ b/src/test/java/com/vaultionizer/vaultserver/CucumberIntegrationTest.java @@ -8,9 +8,10 @@ @RunWith(Cucumber.class) @CucumberOptions( - features = "src/test/resources/features") + features = "src/test/resources/features", + publish = true) @CucumberContextConfiguration @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class CucumberIntegrationTest { -} \ No newline at end of file +} diff --git a/src/test/java/com/vaultionizer/vaultserver/TestHelpers.java b/src/test/java/com/vaultionizer/vaultserver/TestHelpers.java index ac46266..d8ef174 100644 --- a/src/test/java/com/vaultionizer/vaultserver/TestHelpers.java +++ b/src/test/java/com/vaultionizer/vaultserver/TestHelpers.java @@ -3,10 +3,10 @@ import com.fasterxml.jackson.databind.ObjectMapper; public class TestHelpers { - public static String convertToJSON(final Object obj){ + public static String convertToJSON(final Object obj) { try { return new ObjectMapper().writeValueAsString(obj); - }catch (Exception e){ + } catch (Exception e) { throw new RuntimeException(e); } } diff --git a/src/test/java/com/vaultionizer/vaultserver/controllers/MiscControllerTest.java b/src/test/java/com/vaultionizer/vaultserver/controllers/MiscControllerTest.java index 24a1980..a4abf5b 100644 --- a/src/test/java/com/vaultionizer/vaultserver/controllers/MiscControllerTest.java +++ b/src/test/java/com/vaultionizer/vaultserver/controllers/MiscControllerTest.java @@ -4,10 +4,10 @@ import org.junit.jupiter.api.Test; import org.springframework.http.ResponseEntity; -public class MiscControllerTest { +class MiscControllerTest { @Test - public void testGetVersion(){ + void testGetVersion() { ResponseEntity res = (new MiscController()).getVersion(); Assertions.assertEquals(200, res.getStatusCodeValue()); Assertions.assertTrue(res.hasBody()); diff --git a/src/test/java/com/vaultionizer/vaultserver/controllers/SpaceControllerTest.java b/src/test/java/com/vaultionizer/vaultserver/controllers/SpaceControllerTest.java index 7e38c0c..33a4d93 100644 --- a/src/test/java/com/vaultionizer/vaultserver/controllers/SpaceControllerTest.java +++ b/src/test/java/com/vaultionizer/vaultserver/controllers/SpaceControllerTest.java @@ -1,21 +1,15 @@ package com.vaultionizer.vaultserver.controllers; -import com.vaultionizer.vaultserver.model.db.RefFilesModel; -import com.vaultionizer.vaultserver.model.db.SpaceModel; -import com.vaultionizer.vaultserver.resource.SpaceRepository; import com.vaultionizer.vaultserver.service.*; import com.vaultionizer.vaultserver.testdata.SpaceTestData; import org.junit.jupiter.api.*; -import org.mockito.Mock; import org.mockito.Mockito; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.ResponseEntity; -import static org.mockito.ArgumentMatchers.any; - @TestInstance(TestInstance.Lifecycle.PER_CLASS) @DisplayName("Space Controller") -public class SpaceControllerTest { +class SpaceControllerTest { @MockBean private SessionService sessionService; @@ -35,11 +29,10 @@ public class SpaceControllerTest { private FileService fileService; - private SpaceController spaceController; @BeforeEach - private void initialize(){ + private void initialize() { spaceService = Mockito.mock(SpaceService.class); sessionService = Mockito.mock(SessionService.class); refFileService = Mockito.mock(RefFileService.class); @@ -49,41 +42,51 @@ private void initialize(){ Mockito.when(sessionService.getSession( - SpaceTestData.createSpace[0].getAuth().getUserID(), - SpaceTestData.createSpace[0].getAuth().getSessionKey()) + SpaceTestData.createSpaceAuths[0].getUserID(), + SpaceTestData.createSpaceAuths[0].getSessionKey()) ).thenReturn(false); Mockito.when(sessionService.getSession( - SpaceTestData.createSpace[1].getAuth().getUserID(), - SpaceTestData.createSpace[1].getAuth().getSessionKey()) + SpaceTestData.createSpaceAuths[1].getUserID(), + SpaceTestData.createSpaceAuths[1].getSessionKey()) ).thenReturn(true); Mockito.when(spaceService.checkSpaceCredentials( - SpaceTestData.joinSpaces[1].getSpaceID(), + SpaceTestData.joinSpacesSpaceIDs[1], SpaceTestData.joinSpaces[1].getAuthKey()) ).thenReturn(false); Mockito.when(spaceService.checkSpaceCredentials( - SpaceTestData.joinSpaces[2].getSpaceID(), + SpaceTestData.joinSpacesSpaceIDs[2], SpaceTestData.joinSpaces[2].getAuthKey()) ).thenReturn(true); - Mockito.when(spaceService.getSpacesAccessible(SpaceTestData.getAllSpaces[0].getAuth().getUserID())) + Mockito.when(spaceService.getSpacesAccessible(SpaceTestData.getAllSpaces[0].getUserID())) .thenReturn(null); + Mockito.when(sessionService.getSession( + SpaceTestData.getAuthKeyCredentials[3].getUserID(), + SpaceTestData.getAuthKeyCredentials[3].getSessionKey()) + ).thenReturn(true); + + Mockito.when(userAccessService.userHasAccess( - SpaceTestData.getAuthKeys[1].getAuth().getUserID(), - SpaceTestData.getAuthKeys[1].getSpaceID()) + SpaceTestData.getAuthKeyCredentials[1].getUserID(), + SpaceTestData.getAuthKeysSpaceIds[1]) ).thenReturn(false); Mockito.when(userAccessService.userHasAccess( - SpaceTestData.getAuthKeys[2].getAuth().getUserID(), - SpaceTestData.getAuthKeys[2].getSpaceID()) + SpaceTestData.getAuthKeyCredentials[2].getUserID(), + SpaceTestData.getAuthKeysSpaceIds[2]) + ).thenReturn(true); + Mockito.when(userAccessService.userHasAccess( + SpaceTestData.getAuthKeyCredentials[3].getUserID(), + SpaceTestData.getAuthKeysSpaceIds[3]) ).thenReturn(true); - Mockito.when(spaceService.createSpace(SpaceTestData.createSpace[1].getAuth().getUserID(), + Mockito.when(spaceService.createSpace(SpaceTestData.createSpaceAuths[1].getUserID(), SpaceTestData.createSpace[1].getReferenceFile(), SpaceTestData.createSpace[1].isPrivate(), - SpaceTestData.createSpace[1].getAuthKey()) + false, false, SpaceTestData.createSpace[1].getAuthKey()) ).thenReturn(1L); @@ -93,52 +96,55 @@ private void initialize(){ // Tests create space api @Test @DisplayName("Tests creating a new space using a wrong session key.") - public void createSpaceWrongSessionKey(){ - ResponseEntity res = spaceController.createSpace(SpaceTestData.createSpace[0]); + void createSpaceWrongSessionKey() { + ResponseEntity res = spaceController.createSpace(SpaceTestData.createSpace[0], SpaceTestData.createSpaceAuths[0]); Assertions.assertEquals(401, res.getStatusCodeValue()); } @Test @DisplayName("Tests creating a new space using a correct session key.") - public void createSpace(){ - ResponseEntity res = spaceController.createSpace(SpaceTestData.createSpace[1]); + void createSpace() { + ResponseEntity res = spaceController.createSpace(SpaceTestData.createSpace[1], SpaceTestData.createSpaceAuths[1]); Assertions.assertEquals(201, res.getStatusCodeValue()); - Assertions.assertEquals(1L, ((Long)(res.getBody()))); + Assertions.assertEquals(1L, ((Long) (res.getBody()))); } // Tests join space api @Test @DisplayName("Tests joining a space using a wrong session key.") - public void joinSpaceWrongSessionKey(){ - ResponseEntity res = spaceController.joinSpace(SpaceTestData.joinSpaces[0]); + void joinSpaceWrongSessionKey() { + ResponseEntity res = spaceController.joinSpace(SpaceTestData.joinSpaces[0], + SpaceTestData.joinSpacesAuth[0], SpaceTestData.joinSpacesSpaceIDs[0]); Assertions.assertEquals(401, res.getStatusCodeValue()); } @Test @DisplayName("Tests joining a space using a correct session key but wrong authkey.") - public void joinSpaceWrongAuthKey(){ - ResponseEntity res = spaceController.joinSpace(SpaceTestData.joinSpaces[1]); + void joinSpaceWrongAuthKey() { + ResponseEntity res = spaceController.joinSpace(SpaceTestData.joinSpaces[1], + SpaceTestData.joinSpacesAuth[1], SpaceTestData.joinSpacesSpaceIDs[1]); Assertions.assertEquals(403, res.getStatusCodeValue()); } @Test @DisplayName("Tests joining a space using a correct session key with correct authkey.") - public void joinSpace(){ - ResponseEntity res = spaceController.joinSpace(SpaceTestData.joinSpaces[2]); + void joinSpace() { + ResponseEntity res = spaceController.joinSpace(SpaceTestData.joinSpaces[2], + SpaceTestData.joinSpacesAuth[2], SpaceTestData.joinSpacesSpaceIDs[2]); Assertions.assertEquals(200, res.getStatusCodeValue()); } // Tests get all spaces @Test @DisplayName("Tests getting all space a user is part of using a wrong session key.") - public void getAllSpacesWrongSessionKey(){ + void getAllSpacesWrongSessionKey() { ResponseEntity res = spaceController.getAllSpaces(SpaceTestData.getAllSpaces[0]); Assertions.assertEquals(401, res.getStatusCodeValue()); } @Test @DisplayName("Tests getting all space a user is part of using a wrong session key.") - public void getAllSpaces(){ + void getAllSpaces() { ResponseEntity res = spaceController.getAllSpaces(SpaceTestData.getAllSpaces[1]); Assertions.assertEquals(200, res.getStatusCodeValue()); Assertions.assertNull(res.getBody()); @@ -147,23 +153,23 @@ public void getAllSpaces(){ // Tests get authentication key of a specified space @Test @DisplayName("Tests getting the authentication key of a space using a wrong session key.") - public void getAuthKeyWrongSessionKey(){ - ResponseEntity res = spaceController.getAuthKey(SpaceTestData.getAuthKeys[0]); + void getAuthKeyWrongSessionKey() { + ResponseEntity res = spaceController.getAuthKey(SpaceTestData.getAuthKeyCredentials[0], SpaceTestData.getAuthKeysSpaceIds[0]); Assertions.assertEquals(401, res.getStatusCodeValue()); } @Test @DisplayName("Tests getting the authentication key of a space the user has no permission for.") - public void getAuthKeyWithoutPermission(){ - ResponseEntity res = spaceController.getAuthKey(SpaceTestData.getAuthKeys[1]); + void getAuthKeyWithoutPermission() { + ResponseEntity res = spaceController.getAuthKey(SpaceTestData.getAuthKeyCredentials[1], SpaceTestData.getAuthKeysSpaceIds[1]); Assertions.assertEquals(403, res.getStatusCodeValue()); } @Test @DisplayName("Tests getting the authentication key of a space the user access to.") - public void getAuthKey(){ - ResponseEntity res = spaceController.getAuthKey(SpaceTestData.getAuthKeys[1]); - Assertions.assertEquals(403, res.getStatusCodeValue()); + void getAuthKey() { + ResponseEntity res = spaceController.getAuthKey(SpaceTestData.getAuthKeyCredentials[3], SpaceTestData.getAuthKeysSpaceIds[3]); + Assertions.assertEquals(406, res.getStatusCodeValue()); Assertions.assertNull(res.getBody()); } } diff --git a/src/test/java/com/vaultionizer/vaultserver/controllers/UserControllerTest.java b/src/test/java/com/vaultionizer/vaultserver/controllers/UserControllerTest.java index c8e7f58..7c6a4e1 100644 --- a/src/test/java/com/vaultionizer/vaultserver/controllers/UserControllerTest.java +++ b/src/test/java/com/vaultionizer/vaultserver/controllers/UserControllerTest.java @@ -10,12 +10,10 @@ import java.util.Objects; -import static org.mockito.ArgumentMatchers.any; - @TestInstance(TestInstance.Lifecycle.PER_CLASS) @DisplayName("User Controller") -public class UserControllerTest { +class UserControllerTest { @MockBean private UserService userService; @@ -39,7 +37,7 @@ public class UserControllerTest { private UserController userController; @BeforeEach - private void initialize(){ + private void initialize() { userService = Mockito.mock(UserService.class); spaceService = Mockito.mock(SpaceService.class); sessionService = Mockito.mock(SessionService.class); @@ -68,51 +66,51 @@ private void initialize(){ // testing register controller @Test @DisplayName("Tests create user with key and ref file being null.") - public void createUserKeyRefFileNull(){ + void createUserKeyRefFileNull() { ResponseEntity res = userController.createUser(UserTestData.registerData[0]); - Assertions.assertEquals(res.getStatusCodeValue(), 400); + Assertions.assertEquals(400, res.getStatusCodeValue()); } @Test @DisplayName("Tests create user with key and ref file being empty.") - public void createUserKeyRefFileEmpty(){ + void createUserKeyRefFileEmpty() { ResponseEntity res = userController.createUser(UserTestData.registerData[1]); - Assertions.assertEquals(res.getStatusCodeValue(), 400); + Assertions.assertEquals(400, res.getStatusCodeValue()); } @Test @DisplayName("Tests create user with key and ref file being empty.") - public void createUserKeyTooShort(){ + void createUserKeyTooShort() { ResponseEntity res = userController.createUser(UserTestData.registerData[2]); - Assertions.assertEquals(res.getStatusCodeValue(), 400); + Assertions.assertEquals(400, res.getStatusCodeValue()); } @Test @DisplayName("Tests create user with key matching the constraints.") - public void createUser(){ + void createUser() { ResponseEntity res = userController.createUser(UserTestData.registerData[3]); - Assertions.assertEquals(res.getStatusCodeValue(), 201); + Assertions.assertEquals(201, res.getStatusCodeValue()); Assertions.assertTrue(res.hasBody()); - Assertions.assertEquals(((LoginUserResponseDto)(Objects.requireNonNull(res.getBody()))).getUserID(), 0); - Assertions.assertEquals(((LoginUserResponseDto)(Objects.requireNonNull(res.getBody()))).getSessionKey(), "testSessionKey"); + Assertions.assertEquals(0, ((LoginUserResponseDto) (Objects.requireNonNull(res.getBody()))).getUserID()); + Assertions.assertEquals("testSessionKey", ((LoginUserResponseDto) (Objects.requireNonNull(res.getBody()))).getSessionKey()); } // testing login method @Test @DisplayName("Tests login with wrong key") - public void loginUserWrongKey(){ + void loginUserWrongKey() { ResponseEntity res = userController.loginUser(UserTestData.loginUser[0]); System.out.println(res); - Assertions.assertEquals(res.getStatusCodeValue(), 401); + Assertions.assertEquals(401, res.getStatusCodeValue()); } @Test @DisplayName("Tests login with correct key") - public void loginUser(){ + void loginUser() { ResponseEntity res = userController.loginUser(UserTestData.loginUser[1]); - Assertions.assertEquals(res.getStatusCodeValue(), 200); + Assertions.assertEquals(200, res.getStatusCodeValue()); Assertions.assertTrue(res.hasBody()); - Assertions.assertEquals(((LoginUserResponseDto)(Objects.requireNonNull(res.getBody()))).getSessionKey(), "testSession"); + Assertions.assertEquals("testSession", ((LoginUserResponseDto) (Objects.requireNonNull(res.getBody()))).getSessionKey()); } } diff --git a/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/CreateSpaceSteps.java b/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/CreateSpaceSteps.java index 3c0297b..d9e4279 100644 --- a/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/CreateSpaceSteps.java +++ b/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/CreateSpaceSteps.java @@ -4,10 +4,8 @@ import com.vaultionizer.vaultserver.controllers.SessionController; import com.vaultionizer.vaultserver.controllers.SpaceController; import com.vaultionizer.vaultserver.controllers.UserController; -import com.vaultionizer.vaultserver.cucumber.steps.Services; import com.vaultionizer.vaultserver.model.dto.CreateSpaceDto; import com.vaultionizer.vaultserver.model.dto.GenericAuthDto; -import com.vaultionizer.vaultserver.model.dto.LoginUserResponseDto; import com.vaultionizer.vaultserver.service.*; import com.vaultionizer.vaultserver.testdata.UserTestData; import io.cucumber.java.en.And; @@ -50,28 +48,28 @@ public void theSpaceShouldBePrivateTrue(String isPrivate) { @When("the user wants to create a space") public void theUserWantsToCreateASpace() { - res = spaceController.createSpace(new CreateSpaceDto(new GenericAuthDto(userID, sessionKey), isPrivate, authKey, "")); + res = spaceController.createSpace(new CreateSpaceDto(isPrivate, authKey, ""), new GenericAuthDto(userID, sessionKey)); } @Then("the status code of create space is {int}") - public void theStatusCodeOfCreateSpaceIs(int status) throws Throwable{ + public void theStatusCodeOfCreateSpaceIs(int status) throws Throwable { if (res.getStatusCode().value() != status) throw new Throwable(String.valueOf(res.getStatusCodeValue())); } @And("the returned ID is legitimate") - public void theReturnedIDIsLegitimate() throws Throwable{ - spaceID = (Long)res.getBody(); + public void theReturnedIDIsLegitimate() throws Throwable { + spaceID = (Long) res.getBody(); if (spaceID == null) throw new Throwable("No space created"); } @And("the space is private") - public void theSpaceIsPrivate() throws Throwable{ + public void theSpaceIsPrivate() throws Throwable { if (!spaceService.getSpace(spaceID, userID).isPrivate()) throw new Throwable("Not private"); } @And("the user has access") - public void theUserHasAccess() throws Throwable{ - if(!userAccessService.userHasAccess(userID, spaceID)) throw new Throwable("Has no access"); + public void theUserHasAccess() throws Throwable { + if (!userAccessService.userHasAccess(userID, spaceID)) throw new Throwable("Has no access"); } @And("the space is shared") diff --git a/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/CreateUserSteps.java b/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/CreateUserSteps.java index ceae882..2945603 100644 --- a/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/CreateUserSteps.java +++ b/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/CreateUserSteps.java @@ -4,7 +4,6 @@ import com.vaultionizer.vaultserver.controllers.SessionController; import com.vaultionizer.vaultserver.controllers.SpaceController; import com.vaultionizer.vaultserver.controllers.UserController; -import com.vaultionizer.vaultserver.cucumber.steps.Services; import com.vaultionizer.vaultserver.model.dto.LoginUserResponseDto; import com.vaultionizer.vaultserver.model.dto.RegisterUserDto; import com.vaultionizer.vaultserver.service.*; @@ -48,12 +47,12 @@ public void theClientWantsToRegister() { @And("the user has a sessionKey") - public void theUserHasASessionKey() throws Throwable{ + public void theUserHasASessionKey() throws Throwable { if (responseDto.getSessionKey() == null) throw new Throwable("No sessionKey"); } @And("the user has a websocketToken") - public void theUserHasAWebsocketToken() throws Throwable{ + public void theUserHasAWebsocketToken() throws Throwable { if (responseDto.getWebsocketToken() == null) throw new Throwable("No webSocketToken..."); } @@ -61,11 +60,11 @@ public void theUserHasAWebsocketToken() throws Throwable{ @Then("the status code of register is {int}") public void theStatusCodeOfRegisterIs(int status) throws Throwable { if (res.getStatusCode().value() != status) throw new Throwable(String.valueOf(res.getStatusCode().value())); - responseDto = (LoginUserResponseDto)res.getBody(); + responseDto = (LoginUserResponseDto) res.getBody(); } @And("the user has a userID") - public void theUserHasAUserID() throws Throwable{ + public void theUserHasAUserID() throws Throwable { if (responseDto.getUserID() == null) throw new Throwable("No userID"); } @@ -82,8 +81,8 @@ public void theKeyIs(String key) { @And("the key is long enough") public void theKeyIsLongEnough() { this.key = "------------------------------------" + - "------------------------------------------------------------"+ - "------------------------------------------------------------"+ + "------------------------------------------------------------" + + "------------------------------------------------------------" + "------------------------------------------------------------"; } diff --git a/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/DeleteSpaceSteps.java b/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/DeleteSpaceSteps.java index e1a32c1..fa2c70f 100644 --- a/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/DeleteSpaceSteps.java +++ b/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/DeleteSpaceSteps.java @@ -4,8 +4,6 @@ import com.vaultionizer.vaultserver.controllers.SessionController; import com.vaultionizer.vaultserver.controllers.SpaceController; import com.vaultionizer.vaultserver.controllers.UserController; -import com.vaultionizer.vaultserver.cucumber.steps.Services; -import com.vaultionizer.vaultserver.model.dto.AuthWrapperDto; import com.vaultionizer.vaultserver.model.dto.GenericAuthDto; import com.vaultionizer.vaultserver.service.*; import com.vaultionizer.vaultserver.testdata.UserTestData; @@ -41,7 +39,7 @@ public DeleteSpaceSteps(SpaceService spaceService, UserService userService, @Given("the user is logged in properly") public void theUserIsLoggedInProperly() { userID = userService.getUserIDCheckCredentials("luigi", UserTestData.registerData[3].getKey()); - if ( userID == null){ + if (userID == null) { userID = userService.createUser("luigi", UserTestData.registerData[3].getKey()); } sessionKey = sessionService.addSession(userID).getSessionKey(); @@ -49,48 +47,49 @@ public void theUserIsLoggedInProperly() { @And("the user created the space") public void theUserCreatedTheSpace() { - spaceID = spaceService.createSpace(userID, "", false, "authKey"); + spaceID = spaceService.createSpace(userID, "", false, false, false, "authKey"); } @When("the user wants to delete the space") public void theUserWantsToDeleteTheSpace() { refFileID = spaceService.getRefFileID(spaceID); - res = spaceController.deleteSpace(new AuthWrapperDto(new GenericAuthDto(userID, sessionKey)), spaceID); + res = spaceController.deleteSpace(spaceID, new GenericAuthDto(userID, sessionKey)); } @Then("the response is {int}") - public void theResponseIs(int status) throws Throwable{ + public void theResponseIs(int status) throws Throwable { if (res.getStatusCodeValue() != status) throw new Throwable(String.valueOf(res.getStatusCodeValue())); } @And("the user has no access") - public void theUserHasNoAccess() throws Throwable{ + public void theUserHasNoAccess() throws Throwable { if (userAccessService.userHasAccess(userID, spaceID)) throw new Throwable("Has access"); } @And("there is no file in that space") - public void thereIsNoFileInThatSpace() throws Throwable{ + public void thereIsNoFileInThatSpace() throws Throwable { if (fileService.countFilesInSpace(spaceID) != 0) throw new Throwable("Files were not deleted"); } @And("all pending uploads are deleted") - public void allPendingUploadsAreDeleted() throws Throwable{ - if (pendingUploadService.countPendingUploadsForSpace(spaceID) != 0) throw new Throwable("Not all uploads were deleted"); + public void allPendingUploadsAreDeleted() throws Throwable { + if (pendingUploadService.countPendingUploadsForSpace(spaceID) != 0) + throw new Throwable("Not all uploads were deleted"); } @And("the refFile was deleted") - public void theRefFileWasDeleted() throws Throwable{ + public void theRefFileWasDeleted() throws Throwable { if (refFileService.readRefFile(refFileID) != null) throw new Throwable("Ref file was not deleted"); } @And("the space was deleted") - public void theSpaceWasDeleted() throws Throwable{ + public void theSpaceWasDeleted() throws Throwable { if (spaceService.getSpace(spaceID, userID) != null) throw new Throwable("Space was not deleted"); } @And("another user created the space") public void anotherUserCreatedTheSpace() { - spaceID = spaceService.createSpace(10000000L, "", false, "authKey"); + spaceID = spaceService.createSpace(10000000L, "", false, false, false, "authKey"); } @And("the user has access to the space") diff --git a/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/DeleteUserStep.java b/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/DeleteUserStep.java index 2916c8f..c7bab8e 100644 --- a/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/DeleteUserStep.java +++ b/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/DeleteUserStep.java @@ -4,7 +4,6 @@ import com.vaultionizer.vaultserver.controllers.SessionController; import com.vaultionizer.vaultserver.controllers.SpaceController; import com.vaultionizer.vaultserver.controllers.UserController; -import com.vaultionizer.vaultserver.model.dto.AuthWrapperDto; import com.vaultionizer.vaultserver.model.dto.GenericAuthDto; import com.vaultionizer.vaultserver.service.*; import com.vaultionizer.vaultserver.testdata.UserTestData; @@ -29,11 +28,11 @@ public class DeleteUserStep extends Services { @Autowired public DeleteUserStep(SpaceService spaceService, UserService userService, - UserAccessService userAccessService, SessionService sessionService, - RefFileService refFileService, PendingUploadService pendingUploadService, - FileService fileService, UserController userController, - SpaceController spaceController, SessionController sessionController, - FileController fileController) { + UserAccessService userAccessService, SessionService sessionService, + RefFileService refFileService, PendingUploadService pendingUploadService, + FileService fileService, UserController userController, + SpaceController spaceController, SessionController sessionController, + FileController fileController) { super(spaceService, userService, userAccessService, sessionService, refFileService, pendingUploadService, fileService, userController, spaceController, sessionController, fileController); @@ -47,23 +46,25 @@ public void theUserCreatedAnAccountWithName(String username) { @When("the user requests to delete the user") public void theUserRequestsToDeleteTheUser() { - res = userController.deleteUser(new AuthWrapperDto(new GenericAuthDto(userID, sessionKey))); + res = userController.deleteUser(new GenericAuthDto(userID, sessionKey)); } @Then("the status code delete user is {int}") - public void theStatusCodeDeleteUserIs(int status) throws Throwable{ - if (res.getStatusCodeValue() != status) throw new Throwable("Status code wrong: "+res.getStatusCodeValue()); + public void theStatusCodeDeleteUserIs(int status) throws Throwable { + if (res.getStatusCodeValue() != status) throw new Throwable("Status code wrong: " + res.getStatusCodeValue()); } @And("the user's spaces are deleted") - public void theUserSSpacesAreDeleted() throws Throwable{ + public void theUserSSpacesAreDeleted() throws Throwable { var spaces = spaceService.getAllOwnedSpaces(userID); - if (spaces.size() != 0) throw new Throwable("Not all own spaces deleted!: "+ Arrays.toString(spaces.toArray())); + if (spaces.size() != 0) + throw new Throwable("Not all own spaces deleted!: " + Arrays.toString(spaces.toArray())); } @And("the user has no access to any spaces") - public void theUserHasNoAccessToAnySpaces() throws Throwable{ + public void theUserHasNoAccessToAnySpaces() throws Throwable { Set accessible = userAccessService.getAllWithUser(userID); - if (accessible.size() != 0) throw new Throwable("Not all accesses deleted!: "+ Arrays.toString(accessible.toArray())); + if (accessible.size() != 0) + throw new Throwable("Not all accesses deleted!: " + Arrays.toString(accessible.toArray())); } } diff --git a/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/DownloadFileSteps.java b/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/DownloadFileSteps.java index c6ef586..1938c6c 100644 --- a/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/DownloadFileSteps.java +++ b/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/DownloadFileSteps.java @@ -5,7 +5,6 @@ import com.vaultionizer.vaultserver.controllers.SpaceController; import com.vaultionizer.vaultserver.controllers.UserController; import com.vaultionizer.vaultserver.helpers.Config; -import com.vaultionizer.vaultserver.model.dto.FileDownloadDto; import com.vaultionizer.vaultserver.model.dto.GenericAuthDto; import com.vaultionizer.vaultserver.service.*; import com.vaultionizer.vaultserver.testdata.UserTestData; @@ -28,11 +27,11 @@ public class DownloadFileSteps extends Services { @Autowired public DownloadFileSteps(SpaceService spaceService, UserService userService, - UserAccessService userAccessService, SessionService sessionService, - RefFileService refFileService, PendingUploadService pendingUploadService, - FileService fileService, UserController userController, - SpaceController spaceController, SessionController sessionController, - FileController fileController) { + UserAccessService userAccessService, SessionService sessionService, + RefFileService refFileService, PendingUploadService pendingUploadService, + FileService fileService, UserController userController, + SpaceController spaceController, SessionController sessionController, + FileController fileController) { super(spaceService, userService, userAccessService, sessionService, refFileService, pendingUploadService, fileService, userController, spaceController, sessionController, fileController); @@ -41,7 +40,7 @@ public DownloadFileSteps(SpaceService spaceService, UserService userService, @Given("the user has successfully created an account with username {string}") public void theUserHasSuccessfullyCreatedAnAccountWithUsername(String username) { userID = this.userService.createUser(username, UserTestData.registerData[3].getKey()); - spaceID = spaceService.createSpace(userID, "Genki-DAMA", false, "broly"); + spaceID = spaceService.createSpace(userID, "Genki-DAMA", false, false, false, "broly"); sessionKey = sessionService.addSession(userID).getSessionKey(); } @@ -49,19 +48,19 @@ public void theUserHasSuccessfullyCreatedAnAccountWithUsername(String username) public void theFileWithSaveIndexWasUploaded(Long saveIndex) { File dir = new File("trash/cucumberTestAssets/"); dir.mkdirs(); - Config.SPACE_PATH = (dir.getAbsolutePath()+"/"); + Config.SPACE_PATH = (dir.getAbsolutePath() + "/"); fileService.setUploadFile(spaceID, saveIndex); fileService.writeToFile("Kame-hame-HAAAA", spaceID, saveIndex); } @When("the user requests to download the file with saveIndex {long}") public void theUserRequestsToDownloadTheFileWithSaveIndex(Long saveIndex) { - res = fileController.downloadFile(new FileDownloadDto(new GenericAuthDto(userID, sessionKey), spaceID, saveIndex)); + res = fileController.downloadFile(new GenericAuthDto(userID, sessionKey), spaceID, saveIndex); } @Then("the status code of download is {int}") - public void theStatusCodeOfDownloadIs(int status) throws Throwable{ - if (res.getStatusCodeValue() != status) throw new Throwable("Wrong status code: "+res.getStatusCodeValue()); + public void theStatusCodeOfDownloadIs(int status) throws Throwable { + if (res.getStatusCodeValue() != status) throw new Throwable("Wrong status code: " + res.getStatusCodeValue()); } @And("the space id is set inappropriately") diff --git a/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/ManageSpaceSteps.java b/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/ManageSpaceSteps.java new file mode 100644 index 0000000..d8423b8 --- /dev/null +++ b/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/ManageSpaceSteps.java @@ -0,0 +1,182 @@ +package com.vaultionizer.vaultserver.cucumber.steps; + +import com.vaultionizer.vaultserver.controllers.FileController; +import com.vaultionizer.vaultserver.controllers.SessionController; +import com.vaultionizer.vaultserver.controllers.SpaceController; +import com.vaultionizer.vaultserver.controllers.UserController; +import com.vaultionizer.vaultserver.model.dto.*; +import com.vaultionizer.vaultserver.service.*; +import com.vaultionizer.vaultserver.testdata.UserTestData; +import io.cucumber.java.en.And; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; + +public class ManageSpaceSteps extends Services { + private Long userID; + private Long spaceID; + private Long otherUserID; + private ResponseEntity res; + private String DEFAULT_AUTH = "abc"; + + @Autowired + public ManageSpaceSteps(SpaceService spaceService, UserService userService, + UserAccessService userAccessService, SessionService sessionService, + RefFileService refFileService, PendingUploadService pendingUploadService, + FileService fileService, UserController userController, + SpaceController spaceController, SessionController sessionController, + FileController fileController) { + + super(spaceService, userService, userAccessService, sessionService, refFileService, + pendingUploadService, fileService, userController, spaceController, sessionController, fileController); + } + + @Then("the return code is {int}") + public void theReturnCodeIs(int status) throws Exception { + if (res.getStatusCode().value() != status) throw new Exception("Wrong status code."); + } + + @Given("the user has created an account with name {string}") + public void theUserHasCreatedAnAccountWithName(String name) { + userID = userService.createUser(name, UserTestData.registerData[3].getKey()); + spaceID = spaceService.createSpace(userID, "sd", false, true, true, DEFAULT_AUTH); + } + + @And("another user has an account with name {string}") + public void anotherUserHasAnAccountWithName(String name) { + otherUserID = userService.createUser(name, UserTestData.registerData[3].getKey()); + } + + @And("the other user has access") + public void theOtherUserHasAccess() { + userAccessService.addUserAccess(spaceID, otherUserID); + } + + @When("the user kicks all users") + public void theUserKicksAllUsers() { + res = spaceController.kickUsers(spaceID, getUserAuth(userID)); + } + + @And("the other user has no access") + public void theOtherUserHasNoAccess() { + if (userAccessService.userHasAccess(otherUserID, spaceID)) userAccessService.removeAccess(otherUserID, spaceID); + } + + @And("the user still has access") + public void theUserStillHasAccess() throws Exception { + if (!userAccessService.userHasAccess(userID, spaceID)) throw new Exception("User has no more access"); + } + + @When("the other user kicks all users") + public void theOtherUserKicksAllUsers() { + res = spaceController.kickUsers(spaceID, getUserAuth(otherUserID)); + } + + @And("the other user still has access") + public void theOtherUserStillHasAccess() throws Exception { + if (!userAccessService.userHasAccess(otherUserID, spaceID)) + throw new Exception("Other user has no more access"); + } + + @And("the user creates a space that is {string}") + public void theUserCreatesASpaceThatIs(String sharedState) { + spaceID = spaceService.createSpace(userID, "sd", !parseSharedState(sharedState), true, true, DEFAULT_AUTH); + + } + + @When("the user sets the space {string}") + public void theUserSetsTheSpace(String newSharedState) { + res = spaceController.configureSpace( + new ConfigureSpaceDto(true, true, !parseSharedState(newSharedState)), spaceID, getUserAuth(userID)); + } + + @When("the other user configures space") + public void theOtherUserConfiguresSpace() { + res = spaceController.configureSpace( + new ConfigureSpaceDto(true, true, false), spaceID, getUserAuth(otherUserID)); + + } + + private boolean parseSharedState(String state) { + return state.equals("shared"); + } + + @And("the space is configured as {string}") + public void theSpaceIsConfiguredAs(String sharedState) throws Exception { + Boolean shared = spaceService.checkShared(spaceID); + if (shared == null || shared == parseSharedState(sharedState)) + throw new Exception("Configuration failed. State is now " + shared); + } + + @When("the user queries the config") + public void theUserQueriesTheConfig() { + res = spaceController.getSpaceConfig(spaceID, getUserAuth(userID)); + } + + @And("the config is correct.") + public void theConfigIsCorrect() { + } + + @When("the other user queries the config") + public void theOtherUserQueriesTheConfig() { + res = spaceController.getSpaceConfig(spaceID, getUserAuth(otherUserID)); + } + + @When("the user configures the space to write access {string} and invite {string}") + public void theUserConfiguresTheSpaceToWriteAccessAndInvite(String writeAccess, String inviteAccess) { + res = spaceController.configureSpace(new ConfigureSpaceDto( + Boolean.parseBoolean(writeAccess), Boolean.parseBoolean(inviteAccess), true), + spaceID, getUserAuth(userID)); + } + + @And("the config has write access {string} and invite {string}") + public void theConfigHasWriteAccessAndInvite(String writeAccess, String inviteAccess) throws Exception { + res = spaceController.getSpaceConfig(spaceID, getUserAuth(userID)); + if (res == null || !res.hasBody() || res.getBody() == null) throw new Exception("Querying config failed"); + var body = (GetSpaceConfigResponseDto) res.getBody(); + + if (body.isUsersHaveWriteAccess() != Boolean.parseBoolean(writeAccess) || body.isUsersCanInvite() != Boolean.parseBoolean(inviteAccess)) + throw new Exception("Error. Wrong config. Write access: " + body.isUsersHaveWriteAccess() + " auth key access: " + body.isUsersCanInvite()); + } + + @When("the other user configures the space") + public void theOtherUserConfiguresTheSpace() { + res = spaceController.configureSpace(new ConfigureSpaceDto(false, false, false), spaceID, getUserAuth(otherUserID)); + } + + private GenericAuthDto getUserAuth(Long _userID) { + var session = sessionService.addSession(_userID); + return new GenericAuthDto(_userID, session.getSessionKey()); + } + + @When("the user changes the auth key to {string}") + public void theUserChangesTheAuthKeyTo(String authKey) { + res = spaceController.changeAuthKey(new ChangeAuthKeyDto(authKey), spaceID, getUserAuth(userID)); + } + + @And("the auth key is {string}") + public void theAuthKeyIs(String authKey) throws Exception { + checkAuthKey(authKey); + } + + @When("the other user changes the auth key") + public void theOtherUserChangesTheAuthKey() { + res = spaceController.changeAuthKey(new ChangeAuthKeyDto(""), spaceID, getUserAuth(otherUserID)); + } + + @And("the auth key remains unchanged") + public void theAuthKeyRemainsUnchanged() throws Exception { + checkAuthKey(DEFAULT_AUTH); + } + + public void checkAuthKey(String expected) throws Exception { + var result = spaceController.getAuthKey(getUserAuth(userID), spaceID); + if (!result.hasBody() || result.getBody() == null) + throw new Exception("Getting auth key failed. " + result.getStatusCode().value() + " -> " + result.getStatusCode().name()); + var auth = (SpaceAuthKeyResponseDto) result.getBody(); + if (auth == null || !auth.getAuthKey().equals(expected)) + throw new Exception("Auth key expected: " + expected + " is not actual: " + auth.getAuthKey()); + } +} diff --git a/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/UploadFileSteps.java b/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/UploadFileSteps.java index 7a64b09..3758f79 100644 --- a/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/UploadFileSteps.java +++ b/src/test/java/com/vaultionizer/vaultserver/cucumber/steps/UploadFileSteps.java @@ -6,7 +6,6 @@ import com.vaultionizer.vaultserver.controllers.UserController; import com.vaultionizer.vaultserver.model.dto.FileUploadDto; import com.vaultionizer.vaultserver.model.dto.GenericAuthDto; -import com.vaultionizer.vaultserver.model.dto.LoginUserResponseDto; import com.vaultionizer.vaultserver.service.*; import com.vaultionizer.vaultserver.testdata.UserTestData; import io.cucumber.java.en.And; @@ -39,22 +38,22 @@ public UploadFileSteps(SpaceService spaceService, UserService userService, public void theUserHasAnAccountWithName(String username) { userID = this.userService.createUser(username, UserTestData.registerData[3].getKey()); sessionKey = sessionService.addSession(userID).getSessionKey(); - spaceID = spaceService.createSpace(userID, "NANI", false, "dbz"); + spaceID = spaceService.createSpace(userID, "NANI", false, false, false, "dbz"); } @When("the user requests to upload {int} files") public void theUserRequestsToUploadFiles(int amount) { - res = fileController.uploadFiles(new FileUploadDto(new GenericAuthDto(userID, sessionKey), spaceID, amount)); + res = fileController.uploadFiles(new FileUploadDto(amount), new GenericAuthDto(userID, sessionKey), spaceID); } @Then("the status code of upload is {int}") - public void theStatusCodeOfUploadIs(int status) throws Throwable{ - if (res.getStatusCodeValue() != status) throw new Throwable("Status code wrong: "+ res.getStatusCodeValue()); + public void theStatusCodeOfUploadIs(int status) throws Throwable { + if (res.getStatusCodeValue() != status) throw new Throwable("Status code wrong: " + res.getStatusCodeValue()); } @And("the saveIndex is {long}") - public void theSaveIndexIs(Long saveIndex) throws Throwable{ - if (res.getBody() != saveIndex) throw new Throwable("Wrong body (saveIndex): "+ res.getBody()); + public void theSaveIndexIs(Long saveIndex) throws Throwable { + if (res.getBody() != saveIndex) throw new Throwable("Wrong body (saveIndex): " + res.getBody()); } @And("the spaceID is {long}") diff --git a/src/test/java/com/vaultionizer/vaultserver/services/SpaceServiceUnitTests.java b/src/test/java/com/vaultionizer/vaultserver/services/SpaceServiceUnitTests.java new file mode 100644 index 0000000..c2454a3 --- /dev/null +++ b/src/test/java/com/vaultionizer/vaultserver/services/SpaceServiceUnitTests.java @@ -0,0 +1,73 @@ +package com.vaultionizer.vaultserver.services; + +import com.vaultionizer.vaultserver.model.db.SpaceModel; +import com.vaultionizer.vaultserver.model.dto.GetSpacesResponseDto; +import com.vaultionizer.vaultserver.resource.SpaceRepository; +import com.vaultionizer.vaultserver.service.RefFileService; +import com.vaultionizer.vaultserver.service.SpaceService; +import com.vaultionizer.vaultserver.service.UserAccessService; +import org.junit.jupiter.api.*; +import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.MockBean; + +import java.util.Optional; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@DisplayName("SpaceService") +class SpaceServiceUnitTests { + @MockBean + private SpaceRepository spaceRepository; + + @MockBean + private RefFileService refFileService; + + @MockBean + private UserAccessService userAccessService; + + private SpaceService spaceService; + + private GetSpacesResponseDto resGetSpace = new GetSpacesResponseDto((long) 2, false, true, true, true); + + @BeforeEach + private void initialize() { + spaceRepository = Mockito.mock(SpaceRepository.class); + refFileService = Mockito.mock(RefFileService.class); + userAccessService = Mockito.mock(UserAccessService.class); + + Mockito.when(spaceRepository.findById((long) 1)).thenReturn(Optional.ofNullable(null)); + Mockito.when(spaceRepository.findById((long) 2)).thenReturn(Optional.of(new SpaceModel((long) 2, (long) 2, false, false, false, ""))); + Mockito.when(spaceRepository.save(Mockito.any())).thenReturn(new SpaceModel((long) 1, (long) 0, (long) 0, false, false, false, "")); + spaceService = new SpaceService(spaceRepository, refFileService, userAccessService); + } + + @Test + @DisplayName("getSpace that does not exist.") + void getSpaceNotExisting() { + GetSpacesResponseDto res = spaceService.getSpace((long) 1, (long) 1); + Assertions.assertNull(res); + } + + @Test + @DisplayName("getSpace.") + void getSpace() { + GetSpacesResponseDto res = spaceService.getSpace((long) 2, (long) 2); + Assertions.assertNotNull(res); + Assertions.assertEquals(resGetSpace.getSpaceID(), res.getSpaceID()); + Assertions.assertEquals(resGetSpace.isCreator(), res.isCreator()); + Assertions.assertEquals(resGetSpace.isPrivate(), res.isPrivate()); + } + + + @Test + @DisplayName("Create space.") + void createSpaceTest() { + Assertions.assertEquals((long) 1, spaceService.createSpace((long) 1, "", true, false, false, "")); + } + + + @Test + @DisplayName("Get spaces accessible.") + void getSpacesAccess() { + Assertions.assertEquals(0, spaceService.getSpacesAccessible((long) 1).size()); + } +} \ No newline at end of file diff --git a/src/test/java/com/vaultionizer/vaultserver/services/UserAccessServiceUnitTests.java b/src/test/java/com/vaultionizer/vaultserver/services/UserAccessServiceUnitTests.java new file mode 100644 index 0000000..73d60fa --- /dev/null +++ b/src/test/java/com/vaultionizer/vaultserver/services/UserAccessServiceUnitTests.java @@ -0,0 +1,55 @@ +package com.vaultionizer.vaultserver.services; + +import com.vaultionizer.vaultserver.resource.UserAccessRepository; +import com.vaultionizer.vaultserver.service.UserAccessService; +import org.junit.jupiter.api.*; +import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.MockBean; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@DisplayName("UserAccessService") +class UserAccessServiceUnitTests { + @MockBean + private UserAccessRepository userAccessRepository; + + private UserAccessService userAccessService; + + @BeforeEach + private void initialize() { + userAccessRepository = Mockito.mock(UserAccessRepository.class); + Mockito.when(userAccessRepository.hasAccess((long) 1, (long) 1)).thenReturn((long) 0); + Mockito.when(userAccessRepository.hasAccess((long) 1, (long) 2)).thenReturn((long) 1); + + userAccessService = new UserAccessService(userAccessRepository); + } + + @Test + @DisplayName("Add user access.") + void getUserIdOneResult() { + userAccessService.addUserAccess((long) 1, (long) 1); + } + + @Test + @DisplayName("Check user access without access.") + void checkUserAccessNoAccess() { + Assertions.assertFalse(userAccessService.userHasAccess((long) 1, (long) 1)); + } + + @Test + @DisplayName("Check user access.") + void checkUserAccess() { + Assertions.assertTrue(userAccessService.userHasAccess((long) 1, (long) 2)); + } + + @Test + @DisplayName("Remove access although user has no access.") + void removeAccessTestNoAccess() { + Assertions.assertFalse(userAccessService.removeAccess((long) 1, (long) 1)); + } + + @Test + @DisplayName("Remove access.") + void removeAccessTest() { + Assertions.assertTrue(userAccessService.removeAccess((long) 1, (long) 2)); + } +} \ No newline at end of file diff --git a/src/test/java/com/vaultionizer/vaultserver/services/UserServiceUnitTests.java b/src/test/java/com/vaultionizer/vaultserver/services/UserServiceUnitTests.java new file mode 100644 index 0000000..b8f3974 --- /dev/null +++ b/src/test/java/com/vaultionizer/vaultserver/services/UserServiceUnitTests.java @@ -0,0 +1,99 @@ +package com.vaultionizer.vaultserver.services; + +import com.vaultionizer.vaultserver.helpers.Hashing; +import com.vaultionizer.vaultserver.model.db.UserModel; +import com.vaultionizer.vaultserver.resource.UserRepository; +import com.vaultionizer.vaultserver.service.UserService; +import org.junit.jupiter.api.*; +import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.MockBean; + +import java.util.HashSet; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@DisplayName("UserService") +class UserServiceUnitTests { + @MockBean + private UserRepository userRepository; + + private UserService userService; + + @BeforeEach + private void initialize() { + userRepository = Mockito.mock(UserRepository.class); + Long id = 1L; + var hashsetExactlyOne = new HashSet(); + hashsetExactlyOne.add(new UserModel(id, "exactlyOne", Hashing.hashBcrypt("pwd"))); + Mockito.when(userRepository.getPwd("exactlyOne")).thenReturn(hashsetExactlyOne); + + + Mockito.when(userRepository.getPwd("none")).thenReturn(new HashSet<>()); + + + var hashsetMultiple = new HashSet(); + hashsetMultiple.add(new UserModel(id, "moreThanOne", Hashing.hashBcrypt("pwd"))); + hashsetMultiple.add(new UserModel(id, "moreThanOne", Hashing.hashBcrypt("pwd"))); + Mockito.when(userRepository.getPwd("moreThanOne")).thenReturn(hashsetMultiple); + + Mockito.when(userRepository.save(new UserModel("create", Mockito.anyString()))) + .thenReturn(new UserModel((long) 1, "create", "pwd")); + Mockito.when(userRepository.save(new UserModel("failCreate", "pwd"))) + .thenReturn(null); + + userService = new UserService(userRepository); + } + + @Test + @DisplayName("getUserIDCheckCredentials with exactly one.") + void getUserIdOneResult() { + Long id = userService.getUserIDCheckCredentials("exactlyOne", "pwd"); + Assertions.assertEquals(1, id); + } + + @Test + @DisplayName("getUserIDCheckCredentials with none.") + void getUserIdNone() { + Long id = userService.getUserIDCheckCredentials("none", "pwd"); + Assertions.assertEquals(-1, id); + } + + @Test + @DisplayName("getUserIDCheckCredentials with more than one.") + void getUserIdMoreThanOne() { + Long id = userService.getUserIDCheckCredentials("moreThanOne", "pwd"); + Assertions.assertEquals(-1, id); + } + + @Test + @DisplayName("Create user success.") + void createUser() { + Long id = userService.createUser("create", "pwd"); + Assertions.assertNull(id); // TODO: Mockito does not like news + } + + @Test + @DisplayName("Create user failing because of null.") + void createUserException() { + Long id = userService.createUser("failCreate", "pwd"); + Assertions.assertNull(id); + } + + @Test + @DisplayName("Delete user while already in deletion process.") + void deleteUserFailing() { + boolean success = userService.setDeleted((long) 2); + Assertions.assertTrue(success); + success = userService.setDeleted((long) 2); + Assertions.assertFalse(success); + } + + @Test + @DisplayName("Normal delete user.") + void deleteUserNormal() { + boolean success = userService.setDeleted((long) 2); + Assertions.assertTrue(success); + userService.setDeletionDone((long) 2); + } + + +} diff --git a/src/test/java/com/vaultionizer/vaultserver/testdata/SpaceTestData.java b/src/test/java/com/vaultionizer/vaultserver/testdata/SpaceTestData.java index e68a91b..0a068c4 100644 --- a/src/test/java/com/vaultionizer/vaultserver/testdata/SpaceTestData.java +++ b/src/test/java/com/vaultionizer/vaultserver/testdata/SpaceTestData.java @@ -1,43 +1,51 @@ package com.vaultionizer.vaultserver.testdata; -import com.vaultionizer.vaultserver.model.dto.*; +import com.vaultionizer.vaultserver.model.dto.CreateSpaceDto; +import com.vaultionizer.vaultserver.model.dto.GenericAuthDto; +import com.vaultionizer.vaultserver.model.dto.JoinSpaceDto; public class SpaceTestData { public static final CreateSpaceDto[] createSpace = { - new CreateSpaceDto(new GenericAuthDto( + new CreateSpaceDto(true, "", ""), + new CreateSpaceDto(true, "0", "000") + }; + public static final GenericAuthDto[] createSpaceAuths = { + new GenericAuthDto( 1L, "testSessionKey" - ), true, "", ""), - new CreateSpaceDto(new GenericAuthDto( - 1L, - "correctTestSessionKey" - ), true, "0", "000") - }; + ), new GenericAuthDto( + 1L, + "correctTestSessionKey" + )}; public static final JoinSpaceDto[] joinSpaces = { - new JoinSpaceDto(new GenericAuthDto( - 1L, - "testSessionKey" - ), 2L, "definitely wrong"), - new JoinSpaceDto(new GenericAuthDto( - 1L, - "correctTestSessionKey" - ), 2L, "definitely wrong"), - new JoinSpaceDto(new GenericAuthDto( - 1L, - "correctTestSessionKey" - ), 2L, "thatWasTheAuthKey!") + new JoinSpaceDto("definitely wrong"), + new JoinSpaceDto("definitely wrong"), + new JoinSpaceDto("thatWasTheAuthKey!") + }; + + public static final GenericAuthDto[] joinSpacesAuth = { + new GenericAuthDto(1L, "testSessionKey"), + new GenericAuthDto(1L, "correctTestSessionKey"), + new GenericAuthDto(1L, "correctTestSessionKey") + }; + + public static final Long[] joinSpacesSpaceIDs = {2L, 2L, 2L}; + + public static final GenericAuthDto[] getAllSpaces = { + new GenericAuthDto(1L, "definitely wrong"), + new GenericAuthDto(1L, "correctTestSessionKey") }; - public static final AuthWrapperDto[] getAllSpaces = { - new AuthWrapperDto(new GenericAuthDto(1L, "definitely wrong")), - new AuthWrapperDto(new GenericAuthDto(1L, "correctTestSessionKey")) + public static final Long[] getAuthKeysSpaceIds = { + 3L, 3L, 4L, 3L }; - public static final SpaceAuthKeyDto[] getAuthKeys = { - new SpaceAuthKeyDto(new GenericAuthDto(1L, "definitely wrong"), 3L), - new SpaceAuthKeyDto(new GenericAuthDto(1L, "correctTestSessionKey"), 3L), - new SpaceAuthKeyDto(new GenericAuthDto(1L, "correctTestSessionKey"), 4L) + public static final GenericAuthDto[] getAuthKeyCredentials = { + new GenericAuthDto(1L, "definitely wrong"), + new GenericAuthDto(1L, "correctTestSessionKey"), + new GenericAuthDto(1L, "correctTestSessionKey"), + new GenericAuthDto(2L, "correctTestSessionKey") }; } diff --git a/src/test/java/com/vaultionizer/vaultserver/testdata/UserTestData.java b/src/test/java/com/vaultionizer/vaultserver/testdata/UserTestData.java index 1534fab..5e68275 100644 --- a/src/test/java/com/vaultionizer/vaultserver/testdata/UserTestData.java +++ b/src/test/java/com/vaultionizer/vaultserver/testdata/UserTestData.java @@ -8,8 +8,8 @@ public class UserTestData { public static final RegisterUserDto[] registerData = new RegisterUserDto[]{ new RegisterUserDto("", null, null, Config.SERVER_USER, Config.SERVER_AUTH), // key and ref file are null - new RegisterUserDto("","", "", Config.SERVER_USER, Config.SERVER_AUTH), // key and ref file are empty - new RegisterUserDto("","-----", "---", Config.SERVER_USER, Config.SERVER_AUTH),// key is too short + new RegisterUserDto("", "", "", Config.SERVER_USER, Config.SERVER_AUTH), // key and ref file are empty + new RegisterUserDto("", "-----", "---", Config.SERVER_USER, Config.SERVER_AUTH),// key is too short new RegisterUserDto("1234", new String("--------|--------|--------|--------|--------|--------|--------|--------"), "test", Config.SERVER_USER, Config.SERVER_AUTH) // legitimate key (correct length) diff --git a/src/test/resources/features/createSpace.feature b/src/test/resources/features/createSpace.feature index 73a29b2..6a0d603 100644 --- a/src/test/resources/features/createSpace.feature +++ b/src/test/resources/features/createSpace.feature @@ -1,4 +1,5 @@ Feature: A space can be created + Scenario: Private space can be created Given the user is logged in with name "shiggy" And the space should be private: "true" diff --git a/src/test/resources/features/createUser.feature b/src/test/resources/features/createUser.feature index f2f0d29..8ec9866 100644 --- a/src/test/resources/features/createUser.feature +++ b/src/test/resources/features/createUser.feature @@ -1,4 +1,5 @@ Feature: One can create a user + Scenario: Key too short creation Given the username is "mario" And the key is "luigiIdiotta!" diff --git a/src/test/resources/features/deleteSpace.feature b/src/test/resources/features/deleteSpace.feature index 4c2be85..cb87f06 100644 --- a/src/test/resources/features/deleteSpace.feature +++ b/src/test/resources/features/deleteSpace.feature @@ -1,4 +1,5 @@ Feature: A space can be deleted + Scenario: Successful deletion Given the user is logged in properly And the user created the space diff --git a/src/test/resources/features/deleteUser.feature b/src/test/resources/features/deleteUser.feature index 792208e..999873c 100644 --- a/src/test/resources/features/deleteUser.feature +++ b/src/test/resources/features/deleteUser.feature @@ -1,4 +1,5 @@ Feature: The user can be deleted + Scenario: The user was deleted successfully Given the user created an account with name "ruffy" When the user requests to delete the user diff --git a/src/test/resources/features/downloadFile.feature b/src/test/resources/features/downloadFile.feature index b2c55fb..59870b2 100644 --- a/src/test/resources/features/downloadFile.feature +++ b/src/test/resources/features/downloadFile.feature @@ -1,4 +1,5 @@ Feature: A file can be downloaded + Scenario: The file can be downloaded successfully Given the user has successfully created an account with username "cell" And the file with saveIndex 420 was uploaded diff --git a/src/test/resources/features/manageSpace.feature b/src/test/resources/features/manageSpace.feature new file mode 100644 index 0000000..6a355df --- /dev/null +++ b/src/test/resources/features/manageSpace.feature @@ -0,0 +1,142 @@ +Feature: Space can be managed + + Scenario: User can kick other users + Given the user has created an account with name "test1" + And another user has an account with name "other1" + And the other user has access + When the user kicks all users + Then the return code is 200 + And the other user has no access + And the user still has access + + Scenario: Only creator can kick users + Given the user has created an account with name "test2" + And another user has an account with name "other2" + And the other user has access + When the other user kicks all users + Then the return code is 406 + And the user still has access + And the other user still has access + + Scenario: Users without access cannot kick + Given the user has created an account with name "test3" + And another user has an account with name "other3" + When the other user kicks all users + Then the return code is 403 + And the user still has access + + Scenario: User can make space private + Given the user has created an account with name "test4" + And the user creates a space that is "shared" + And another user has an account with name "other4" + And the other user has access + When the user sets the space "private" + Then the return code is 202 + And the user still has access + And the space is configured as "private" + And the other user has no access + + Scenario: User can make space private + Given the user has created an account with name "test5" + And the user creates a space that is "private" + And another user has an account with name "other5" + And the other user has access + When the user sets the space "shared" + Then the return code is 202 + And the user still has access + And the space is configured as "shared" + And the other user has no access + + + Scenario: Only creator can configure + Given the user has created an account with name "test6" + And another user has an account with name "other6" + And the other user has access + When the other user configures space + Then the return code is 406 + And the user still has access + And the other user still has access + + + Scenario: Configuration needs access + Given the user has created an account with name "test7" + And another user has an account with name "other7" + When the other user configures space + Then the return code is 403 + And the user still has access + And the other user has no access + + Scenario Outline: User can make space + Given the user has created an account with name "" + And the user creates a space that is "" + And another user has an account with name "" + And the other user has access + When the user sets the space "" + Then the return code is 202 + And the user still has access + And the space is configured as "" + Examples: + | shared_state | username | other_user | + | private | test8 | other8 | + | shared | test9 | other9 | + + + Scenario: User can get config + Given the user has created an account with name "test10" + When the user queries the config + Then the return code is 200 + And the config is correct. + + Scenario: User can only get config with access + Given the user has created an account with name "test11" + And another user has an account with name "other11" + When the other user queries the config + Then the return code is 403 + + Scenario Outline: User can configure space + Given the user has created an account with name "" + When the user configures the space to write access "" and invite "" + Then the return code is 202 + And the config has write access "" and invite "" + Examples: + | username | writeAccess | inviteAllowed | + | test12 | false | false | + | test13 | false | true | + | test14 | true | false | + | test15 | true | true | + + Scenario: User without access cannot configure + Given the user has created an account with name "test16" + And another user has an account with name "other16" + When the other user configures the space + Then the return code is 403 + + Scenario: User without access cannot configure + Given the user has created an account with name "test17" + And another user has an account with name "other17" + And the other user has access + When the other user configures the space + Then the return code is 406 + + Scenario: User can change auth key + Given the user has created an account with name "test18" + When the user changes the auth key to "PizzaPasta" + Then the return code is 200 + And the auth key is "PizzaPasta" + + + Scenario: Users without access cannot change auth key + Given the user has created an account with name "test19" + And another user has an account with name "other19" + When the other user changes the auth key + Then the return code is 403 + And the auth key remains unchanged + + + Scenario: Only creator can change auth key + Given the user has created an account with name "test20" + And another user has an account with name "other20" + And the other user has access + When the other user changes the auth key + Then the return code is 406 + And the auth key remains unchanged \ No newline at end of file diff --git a/src/test/resources/features/uploadFile.feature b/src/test/resources/features/uploadFile.feature index 6ced15a..ac7ae1a 100644 --- a/src/test/resources/features/uploadFile.feature +++ b/src/test/resources/features/uploadFile.feature @@ -1,4 +1,5 @@ Feature: A file can be uploaded + Scenario: The file can be uploaded successfully Given the user has an account with name "goku" When the user requests to upload 10 files