diff --git a/.env.unix.example b/.env.unix.example index b0ad6ff0532fd9d5d74c99b95cee0ea66122cfcd..54986336db71fa576456e6638245f6a36574a34b 100644 --- a/.env.unix.example +++ b/.env.unix.example @@ -12,6 +12,7 @@ METADATA_PASSWORD=dbrepo AUTH_DB=keycloak AUTH_USERNAME=root AUTH_PASSWORD=dbrepo +BROKER_ENDPOINT=http://broker-service:15672/admin/broker BROKER_USERNAME=fda BROKER_PASSWORD=fda KEYCLOAK_ADMIN=fda diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dc68ed7dbc884ebfa1215fc3fdb9207790d1b1f5..60880514a97c759997bf1023a715a8b157498544 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -124,20 +124,26 @@ test-frontend: test-default-deployment: stage: test-deployment + timeout: 5m needs: - test-frontend script: + - "make teardown" - "rm -f .env" - - "docker compose up -d || docker compose down" + - "docker compose up -d && make teardown" + - "make teardown" coverage: '/TOTAL.*?([0-9]{1,3})%/' test-env-deployment: stage: test-deployment + timeout: 5m needs: - test-frontend script: + - "make teardown" - "cp .env.unix.example .env" - - "docker compose up -d || docker compose down" + - "docker compose up -d && make teardown" + - "make teardown" scan-analyse-service: stage: scan-docker diff --git a/Makefile b/Makefile index b6a203169d6e3e44903d5178e4ce8e49a2354fa3..2229c2993727a3b08657dabeaa8b07093da5a1d0 100644 --- a/Makefile +++ b/Makefile @@ -102,7 +102,7 @@ test-backend: test-metadata-service test-analyse-service test-search-sync-agent test-search-sync-agent: build-search-sync-agent mvn -f ./dbrepo-search-sync-agent/pom.xml clean test verify -test-metadata-service: build-metadata-service +test-metadata-service: build-metadata-service teardown mvn -f ./dbrepo-metadata-service/pom.xml clean test verify test-analyse-service: build-analyse-service diff --git a/dbrepo-broker-service/Dockerfile b/dbrepo-broker-service/Dockerfile index caef7401ec51119dd66b94c4ae0a3cf184021fed..78bc9bb462d61dda0def30fbd5ac70b45cde95f1 100644 --- a/dbrepo-broker-service/Dockerfile +++ b/dbrepo-broker-service/Dockerfile @@ -13,5 +13,3 @@ RUN rabbitmq-plugins enable --offline rabbitmq_prometheus rabbitmq_mqtt rabbitmq EXPOSE 5672 EXPOSE 15672 - -HEALTHCHECK --interval=10s --timeout=5s --retries=12 CMD wget --spider http://localhost:15672/broker/ diff --git a/dbrepo-broker-service/rabbitmq.conf b/dbrepo-broker-service/rabbitmq.conf index 23942bcede8a6ab13812d91beea5aac8583c956b..e1883b4b825a9f9a6aa79a050fc01d8f8a1efdf0 100644 --- a/dbrepo-broker-service/rabbitmq.conf +++ b/dbrepo-broker-service/rabbitmq.conf @@ -10,8 +10,8 @@ default_permissions.write = .* # enable http outside localhost listeners.tcp.1 = 0.0.0.0:5672 -# management ui (https://www.rabbitmq.com/management.html#path-prefix) -management.path_prefix = /broker +# management prefix (https://www.rabbitmq.com/management.html#path-prefix) +management.path_prefix = /admin/broker # logging log.console = true diff --git a/dbrepo-gateway-service/dbrepo.conf b/dbrepo-gateway-service/dbrepo.conf index b5f706fced78f9438ef7d9de98f2c321f92bfed2..68abcdeb0fbaaf2ebeadca382e60c2aad6b96056 100644 --- a/dbrepo-gateway-service/dbrepo.conf +++ b/dbrepo-gateway-service/dbrepo.conf @@ -34,8 +34,7 @@ server { listen 80 default_server; server_name _; - location /api/broker { - rewrite /api/broker/(.*) /api/$1 break; + location /admin/broker { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -44,22 +43,22 @@ server { proxy_read_timeout 90; } - location /api/analyse { + location /api/broker { + rewrite /api/broker/(.*) /admin/broker/api/$1 break; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://analyse; + proxy_pass http://broker; proxy_read_timeout 90; } - location /pid { - rewrite /pid/(.*) /api/pid/$1 break; + location /api/analyse { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://metadata; + proxy_pass http://analyse; proxy_read_timeout 90; } @@ -91,17 +90,17 @@ server { proxy_read_timeout 90; } - location /retrieve { - rewrite /retrieve/(.*) /$1 break; + location /api { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://search; + proxy_pass http://metadata; proxy_read_timeout 90; } - location /api { + location /pid { + rewrite /pid/(.*) /api/pid/$1 break; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -110,6 +109,16 @@ server { proxy_read_timeout 90; } + location /retrieve { + rewrite /retrieve/(.*) /$1 break; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://search; + proxy_read_timeout 90; + } + location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; diff --git a/dbrepo-metadata-service/Dockerfile b/dbrepo-metadata-service/Dockerfile index e9587b40feac829f8b1b7d0676779a758c7f29da..f88b37c5ec15bad047103c445298aa48569c4078 100644 --- a/dbrepo-metadata-service/Dockerfile +++ b/dbrepo-metadata-service/Dockerfile @@ -36,7 +36,7 @@ ENV ADMIN_MAIL="noreply@localhost" ENV BASE_URL="http://localhost" ENV GRANT_PRIVILEGES="SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE" ENV BROKER_CONSUMERS=2 -ENV BROKER_ENDPOINT="http://broker-service:15672" +ENV BROKER_ENDPOINT="http://broker-service:15672/admin/broker" ENV BROKER_USERNAME=fda ENV BROKER_PASSWORD=fda ENV DELETED_RECORD=persistent diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/BrokerRemoteException.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/BrokerRemoteException.java new file mode 100644 index 0000000000000000000000000000000000000000..922462d2ee95b4cfea228b64cab7f80e40fa7661 --- /dev/null +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/BrokerRemoteException.java @@ -0,0 +1,21 @@ +package at.tuwien.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(code = HttpStatus.SERVICE_UNAVAILABLE) +public class BrokerRemoteException extends Exception { + + public BrokerRemoteException(String msg) { + super(msg); + } + + public BrokerRemoteException(String msg, Throwable thr) { + super(msg, thr); + } + + public BrokerRemoteException(Throwable thr) { + super(thr); + } + +} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/BrokerVirtualHostCreationException.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/BrokerVirtualHostCreationException.java deleted file mode 100644 index bb3b2690dc7c1348cb33ac0f147d6be320aa7267..0000000000000000000000000000000000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/BrokerVirtualHostCreationException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.NOT_ACCEPTABLE) -public class BrokerVirtualHostCreationException extends Exception { - - public BrokerVirtualHostCreationException(String msg) { - super(msg); - } - - public BrokerVirtualHostCreationException(String msg, Throwable thr) { - super(msg, thr); - } - - public BrokerVirtualHostCreationException(Throwable thr) { - super(thr); - } -} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/BrokerVirtualHostModificationException.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/BrokerVirtualHostModificationException.java new file mode 100644 index 0000000000000000000000000000000000000000..a9fda0c9d43ffc61ff5205c5f4eabf0ebadd32f0 --- /dev/null +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/BrokerVirtualHostModificationException.java @@ -0,0 +1,20 @@ +package at.tuwien.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(code = HttpStatus.NOT_ACCEPTABLE) +public class BrokerVirtualHostModificationException extends Exception { + + public BrokerVirtualHostModificationException(String msg) { + super(msg); + } + + public BrokerVirtualHostModificationException(String msg, Throwable thr) { + super(msg, thr); + } + + public BrokerVirtualHostModificationException(Throwable thr) { + super(thr); + } +} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/KeycloakRemoteException.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/KeycloakRemoteException.java index 594d70340303f18a3504324d75a445d1457158de..6616739278b0b02b8160d09e94c87f304f809628 100644 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/KeycloakRemoteException.java +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/exception/KeycloakRemoteException.java @@ -3,7 +3,7 @@ package at.tuwien.exception; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; -@ResponseStatus(code = HttpStatus.LOCKED) +@ResponseStatus(code = HttpStatus.SERVICE_UNAVAILABLE) public class KeycloakRemoteException extends Exception { public KeycloakRemoteException(String msg) { diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DatabaseMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DatabaseMapper.java index a889b2ee10a01328a477e4acf247f7fcc079bd84..5890c1b0193d7808e82cfa25943e908c771b9d6d 100644 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DatabaseMapper.java +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DatabaseMapper.java @@ -163,9 +163,9 @@ public interface DatabaseMapper { } default PreparedStatement rawGrantCreatorAccessQuery(Connection connection, String databaseName, String username, - String priviliges) throws QueryMalformedException { + String privileges) throws QueryMalformedException { final StringBuilder statement = new StringBuilder("GRANT ") - .append(priviliges) + .append(privileges) .append(" ON ") .append(databaseName) .append(".* TO `") diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java index 4c77a4ac257500f57fb03c03819e6a1c0c14d858..c114506501c05e943bb78298dd0a93d0e72d2533 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java @@ -2,7 +2,6 @@ package at.tuwien.endpoints; import at.tuwien.api.database.*; import at.tuwien.api.error.ApiErrorDto; -import at.tuwien.api.user.UserDto; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; import at.tuwien.entities.user.User; @@ -84,7 +83,7 @@ public class DatabaseEndpoint { } @PostMapping - @Transactional + @Transactional(rollbackFor = Exception.class) @PreAuthorize("hasAuthority('create-database')") @Timed(value = "database.create", description = "Time needed to create a database") @Operation(summary = "Create database", security = @SecurityRequirement(name = "bearerAuth")) @@ -140,8 +139,8 @@ public class DatabaseEndpoint { throws ImageNotSupportedException, ContainerNotFoundException, DatabaseMalformedException, AmqpException, ContainerConnectionException, UserNotFoundException, DatabaseNotFoundException, DatabaseNameExistsException, DatabaseConnectionException, - QueryMalformedException, NotAllowedException, BrokerVirtualHostCreationException, QueryStoreException, - BrokerVirtualHostGrantException, KeycloakRemoteException, AccessDeniedException { + QueryMalformedException, NotAllowedException, BrokerVirtualHostModificationException, QueryStoreException, + BrokerVirtualHostGrantException, KeycloakRemoteException, AccessDeniedException, BrokerRemoteException { log.debug("endpoint create database, createDto={}, principal={}", createDto, principal); final User user = userService.findByUsername(principal.getName()); @@ -272,7 +271,7 @@ public class DatabaseEndpoint { } @DeleteMapping("/{id}") - @Transactional + @Transactional(rollbackFor = Exception.class) @PreAuthorize("hasAuthority('delete-database')") @Timed(value = "database.delete", description = "Time needed to delete a database") @Operation(summary = "Delete some database", security = @SecurityRequirement(name = "bearerAuth")) @@ -321,7 +320,7 @@ public class DatabaseEndpoint { public ResponseEntity<?> delete(@NotNull @PathVariable Long id, Principal principal) throws DatabaseNotFoundException, ImageNotSupportedException, DatabaseMalformedException, AmqpException, QueryMalformedException, UserNotFoundException, BrokerVirtualHostGrantException, - DatabaseConnectionException, KeycloakRemoteException, AccessDeniedException { + DatabaseConnectionException, KeycloakRemoteException, AccessDeniedException, BrokerRemoteException { log.debug("endpoint delete database, id={}, principal={}", id, principal); final Database database = databaseService.findById(id); diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java index c4d587c99ca7cb153d5ebf3617aae58d62806348..e2c6b94814aa6eed26b282fd5c8e8d45281322d6 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java @@ -6,6 +6,7 @@ import at.tuwien.api.user.*; import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.mapper.UserMapper; +import at.tuwien.service.AuthenticationService; import at.tuwien.service.DatabaseService; import at.tuwien.service.MessageQueueService; import at.tuwien.service.UserService; @@ -42,15 +43,16 @@ public class UserEndpoint { private final UserService userService; private final DatabaseService databaseService; private final MessageQueueService messageQueueService; - + private final AuthenticationService authenticationService; @Autowired public UserEndpoint(UserMapper userMapper, UserService userService, DatabaseService databaseService, - MessageQueueService messageQueueService) { + MessageQueueService messageQueueService, AuthenticationService authenticationService) { this.userMapper = userMapper; this.userService = userService; this.databaseService = databaseService; this.messageQueueService = messageQueueService; + this.authenticationService = authenticationService; } @GetMapping @@ -75,7 +77,7 @@ public class UserEndpoint { } @PostMapping - @Transactional + @Transactional(rollbackFor = Exception.class) @PreAuthorize("!isAuthenticated()") @Timed(value = "user.create", description = "Time needed to create a user in the metadata database") @Operation(summary = "Create user") @@ -85,8 +87,11 @@ public class UserEndpoint { content = {@Content( mediaType = "application/json", schema = @Schema(implementation = UserBriefDto.class))}), + @ApiResponse(responseCode = "400", + description = "Parameters are not well-formed (likely email)", + content = {@Content(mediaType = "application/json")}), @ApiResponse(responseCode = "404", - description = "Realm or default role not found", + description = "default role not found", content = {@Content( mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), @@ -102,15 +107,35 @@ public class UserEndpoint { schema = @Schema(implementation = ApiErrorDto.class))}), }) public ResponseEntity<UserBriefDto> create(@NotNull @Valid @RequestBody SignupRequestDto data) - throws RealmNotFoundException, UserAlreadyExistsException, UserEmailAlreadyExistsException, - UserNotFoundException, KeycloakRemoteException, AccessDeniedException, BrokerVirtualHostCreationException { + throws UserAlreadyExistsException, UserEmailAlreadyExistsException, UserNotFoundException, + KeycloakRemoteException, AccessDeniedException, BrokerRemoteException, + BrokerVirtualHostModificationException { log.debug("endpoint create a user, data={}", data); /* check */ userService.validateUsernameNotExists(data.getUsername()); userService.validateEmailNotExists(data.getEmail()); /* create */ - final UserBriefDto dto = userMapper.userToUserBriefDto(userService.create(data)); - messageQueueService.createUser(dto.getUsername()); + authenticationService.create(data); + final at.tuwien.api.keycloak.UserDto keycloakUserDto = authenticationService.findByUsername(data.getUsername()); + try { + messageQueueService.createUser(data.getUsername()); + } catch (BrokerRemoteException e) { + try { + authenticationService.delete(keycloakUserDto.getId()); + } catch (UserNotFoundException e2) { + /* ignore */ + } + throw new BrokerRemoteException(e); + } catch (BrokerVirtualHostModificationException e) { + try { + authenticationService.delete(keycloakUserDto.getId()); + } catch (UserNotFoundException e2) { + /* ignore */ + } + throw new BrokerVirtualHostModificationException(e); + } + final User user = userService.create(data, keycloakUserDto.getId()); + final UserBriefDto dto = userMapper.userToUserBriefDto(user); log.trace("create user resulted in dto {}", dto); return ResponseEntity.status(HttpStatus.CREATED) .body(dto); @@ -275,6 +300,7 @@ public class UserEndpoint { } /* modify password */ userService.updatePassword(id, data); + authenticationService.updatePassword(id, data); return ResponseEntity.accepted() .build(); } diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java index b792981dfc61eea5763f456f51d0fd3749454b43..0e10da264304a879b6dcf72b2716e16b4fbaba09 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java @@ -40,17 +40,29 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler { } @Hidden - @ResponseStatus(HttpStatus.LOCKED) + @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) @ExceptionHandler(KeycloakRemoteException.class) public ResponseEntity<ApiErrorDto> handle(KeycloakRemoteException e, WebRequest request) { final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.LOCKED) + .status(HttpStatus.SERVICE_UNAVAILABLE) .message(e.getLocalizedMessage()) .code("error.metadata.keycloak") .build(); return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); } + @Hidden + @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) + @ExceptionHandler(BrokerRemoteException.class) + public ResponseEntity<ApiErrorDto> handle(BrokerRemoteException e, WebRequest request) { + final ApiErrorDto response = ApiErrorDto.builder() + .status(HttpStatus.SERVICE_UNAVAILABLE) + .message(e.getLocalizedMessage()) + .code("error.metadata.broker") + .build(); + return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); + } + @Hidden @ResponseStatus(HttpStatus.CONFLICT) @ExceptionHandler(ContainerAlreadyExistsException.class) @@ -233,8 +245,8 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler { @Hidden @ResponseStatus(HttpStatus.NOT_ACCEPTABLE) - @ExceptionHandler(BrokerVirtualHostCreationException.class) - public ResponseEntity<ApiErrorDto> handle(BrokerVirtualHostCreationException e, WebRequest request) { + @ExceptionHandler(BrokerVirtualHostModificationException.class) + public ResponseEntity<ApiErrorDto> handle(BrokerVirtualHostModificationException e, WebRequest request) { final ApiErrorDto response = ApiErrorDto.builder() .status(HttpStatus.NOT_ACCEPTABLE) .message(e.getLocalizedMessage()) diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/config/MariaDbContainerConfig.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/config/MariaDbContainerConfig.java index 42e1d3b2bc700df0962a5864c5d6aac99c1a8c3a..1d61a1108d8ceba8c2eaabaee35a6f1efcfd98d3 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/config/MariaDbContainerConfig.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/config/MariaDbContainerConfig.java @@ -32,7 +32,7 @@ public class MariaDbContainerConfig { private boolean started = false; public static synchronized CustomMariaDBContainer getInstance() { - if(instance == null) { + if (instance == null) { instance = new CustomMariaDBContainer(BaseTest.IMAGE_1_NAME + ":" + BaseTest.IMAGE_1_VERSION); instance.withImagePullPolicy(PullPolicy.alwaysPull()); instance.addFixedExposedPort(BaseTest.CONTAINER_1_PORT, BaseTest.IMAGE_1_PORT); @@ -56,7 +56,8 @@ public class MariaDbContainerConfig { @Override public synchronized void start() { - if(!started) { + if (!started) { + super.stop(); super.start(); started = true; } diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java index d9ae17223957d0014fb074fac4c20784bb53d4dc..c13d6ed419a8a72ec078fb0c8da92a32a77ed620 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java @@ -4,7 +4,6 @@ import at.tuwien.BaseUnitTest; import at.tuwien.annotations.MockAmqp; import at.tuwien.annotations.MockOpensearch; import at.tuwien.api.database.*; -import at.tuwien.api.user.UserDto; import at.tuwien.entities.container.Container; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; @@ -116,8 +115,8 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { public void create_succeeds() throws UserNotFoundException, BrokerVirtualHostGrantException, DatabaseNameExistsException, NotAllowedException, ContainerConnectionException, DatabaseMalformedException, QueryStoreException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, - ImageNotSupportedException, AmqpException, BrokerVirtualHostCreationException, ContainerNotFoundException, - KeycloakRemoteException, AccessDeniedException { + ImageNotSupportedException, AmqpException, BrokerVirtualHostModificationException, ContainerNotFoundException, + KeycloakRemoteException, AccessDeniedException, BrokerRemoteException { final DatabaseCreateDto request = DatabaseCreateDto.builder() .cid(CONTAINER_1_ID) .name(DATABASE_1_NAME) @@ -399,7 +398,7 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { @WithMockUser(username = USER_2_USERNAME, authorities = {"delete-database"}) public void delete_hasRole_succeeds() throws UserNotFoundException, BrokerVirtualHostGrantException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, - AmqpException, DatabaseMalformedException, KeycloakRemoteException, AccessDeniedException { + AmqpException, DatabaseMalformedException, KeycloakRemoteException, AccessDeniedException, BrokerRemoteException { /* test */ delete_generic(DATABASE_2_ID, DATABASE_2, USER_2_PRINCIPAL); @@ -429,8 +428,8 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { Principal principal) throws UserNotFoundException, DatabaseNameExistsException, NotAllowedException, ContainerConnectionException, DatabaseMalformedException, QueryStoreException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, - AmqpException, BrokerVirtualHostCreationException, ContainerNotFoundException, - BrokerVirtualHostGrantException, KeycloakRemoteException, AccessDeniedException { + AmqpException, BrokerVirtualHostModificationException, ContainerNotFoundException, + BrokerVirtualHostGrantException, KeycloakRemoteException, AccessDeniedException, BrokerRemoteException { /* mock */ doNothing() @@ -499,7 +498,7 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { public void delete_generic(Long databaseId, Database database, Principal principal) throws DatabaseNotFoundException, UserNotFoundException, DatabaseConnectionException, QueryMalformedException, ImageNotSupportedException, AmqpException, DatabaseMalformedException, - BrokerVirtualHostGrantException, KeycloakRemoteException, AccessDeniedException { + BrokerVirtualHostGrantException, KeycloakRemoteException, AccessDeniedException, BrokerRemoteException { /* mock */ if (database != null) { diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java index 21b81f10e9ee1e4d095b7eba00b6ebae3d4b0470..a7ed7c0c77bf08bceb36bf6f59b0e49a69212e2f 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java @@ -7,6 +7,7 @@ import at.tuwien.api.auth.SignupRequestDto; import at.tuwien.api.user.*; import at.tuwien.entities.user.User; import at.tuwien.exception.*; +import at.tuwien.service.AuthenticationService; import at.tuwien.service.MessageQueueService; import at.tuwien.service.UserService; import lombok.extern.log4j.Log4j2; @@ -44,6 +45,9 @@ public class UserEndpointUnitTest extends BaseUnitTest { @MockBean private MessageQueueService messageQueueService; + @MockBean + private AuthenticationService authenticationService; + @Autowired private UserEndpoint userEndpoint; @@ -66,8 +70,8 @@ public class UserEndpointUnitTest extends BaseUnitTest { @Test @WithAnonymousUser public void create_anonymous_succeeds() throws UserNotFoundException, UserEmailAlreadyExistsException, - RealmNotFoundException, UserAlreadyExistsException, KeycloakRemoteException, - at.tuwien.exception.AccessDeniedException, BrokerVirtualHostCreationException { + UserAlreadyExistsException, KeycloakRemoteException, + at.tuwien.exception.AccessDeniedException, BrokerRemoteException, BrokerVirtualHostModificationException { final SignupRequestDto request = SignupRequestDto.builder() .email(USER_1_EMAIL) .username(USER_1_USERNAME) @@ -75,7 +79,7 @@ public class UserEndpointUnitTest extends BaseUnitTest { .build(); /* test */ - create_generic(request, USER_1); + create_generic(request, USER_1, USER_1_KEYCLOAK_DTO, USER_1_ID); } @Test @@ -89,7 +93,7 @@ public class UserEndpointUnitTest extends BaseUnitTest { /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { - create_generic(request, null); + create_generic(request, null, null, null); }); } @@ -302,16 +306,22 @@ public class UserEndpointUnitTest extends BaseUnitTest { assertEquals(2, body.size()); } - protected void create_generic(SignupRequestDto data, User user) throws UserEmailAlreadyExistsException, - RealmNotFoundException, UserAlreadyExistsException, UserNotFoundException, KeycloakRemoteException, - AccessDeniedException, BrokerVirtualHostCreationException { + protected void create_generic(SignupRequestDto data, User user, at.tuwien.api.keycloak.UserDto userDto, UUID id) + throws UserEmailAlreadyExistsException, UserAlreadyExistsException, UserNotFoundException, + KeycloakRemoteException, AccessDeniedException, BrokerRemoteException, + BrokerVirtualHostModificationException { /* mock */ - when(userService.create(data)) + when(userService.create(data, id)) .thenReturn(user); doNothing() .when(messageQueueService) .createUser(anyString()); + when(authenticationService.findByUsername(data.getUsername())) + .thenReturn(userDto); + doNothing() + .when(authenticationService) + .create(any(SignupRequestDto.class)); /* test */ final ResponseEntity<UserBriefDto> response = userEndpoint.create(data); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/BrokerServiceGatewayTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/BrokerServiceGatewayTest.java index ef9682a26d73a567883962dcc285cbb32fb88de3..7e1fd0accb8827f49ba149a62e8d653467c14be8 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/BrokerServiceGatewayTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/BrokerServiceGatewayTest.java @@ -3,7 +3,8 @@ package at.tuwien.gateway; import at.tuwien.BaseUnitTest; import at.tuwien.annotations.MockAmqp; import at.tuwien.annotations.MockOpensearch; -import at.tuwien.exception.BrokerVirtualHostCreationException; +import at.tuwien.exception.BrokerRemoteException; +import at.tuwien.exception.BrokerVirtualHostModificationException; import at.tuwien.exception.BrokerVirtualHostGrantException; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.Test; @@ -37,7 +38,7 @@ public class BrokerServiceGatewayTest extends BaseUnitTest { private BrokerServiceGateway brokerServiceGateway; @Test - public void createVirtualHost_succeeds() throws BrokerVirtualHostCreationException { + public void createVirtualHost_succeeds() throws BrokerVirtualHostModificationException, BrokerRemoteException { final ResponseEntity<Void> mock = ResponseEntity.status(HttpStatus.CREATED) .build(); @@ -59,13 +60,13 @@ public class BrokerServiceGatewayTest extends BaseUnitTest { .thenReturn(mock); /* test */ - assertThrows(BrokerVirtualHostCreationException.class, () -> { + assertThrows(BrokerVirtualHostModificationException.class, () -> { brokerServiceGateway.createVirtualHost(VIRTUAL_HOST_CREATE_DTO); }); } @Test - public void grantPermission_exchangeNoRightsBefore_succeeds() throws BrokerVirtualHostGrantException { + public void grantPermission_exchangeNoRightsBefore_succeeds() throws BrokerVirtualHostGrantException, BrokerRemoteException { final ResponseEntity<Void> mock = ResponseEntity.status(HttpStatus.CREATED) .build(); @@ -78,7 +79,7 @@ public class BrokerServiceGatewayTest extends BaseUnitTest { } @Test - public void grantPermission_exchangeRightsSame_succeeds() throws BrokerVirtualHostGrantException { + public void grantPermission_exchangeRightsSame_succeeds() throws BrokerVirtualHostGrantException, BrokerRemoteException { final ResponseEntity<Void> mock = ResponseEntity.status(HttpStatus.NO_CONTENT) .build(); @@ -106,7 +107,7 @@ public class BrokerServiceGatewayTest extends BaseUnitTest { } @Test - public void grantPermission_virtualHostNoRightsBefore_succeeds() throws BrokerVirtualHostGrantException { + public void grantPermission_virtualHostNoRightsBefore_succeeds() throws BrokerRemoteException, BrokerVirtualHostGrantException { final ResponseEntity<Void> mock = ResponseEntity.status(HttpStatus.CREATED) .build(); @@ -119,7 +120,7 @@ public class BrokerServiceGatewayTest extends BaseUnitTest { } @Test - public void grantPermission_virtualHostRightsSame_succeeds() throws BrokerVirtualHostGrantException { + public void grantPermission_virtualHostRightsSame_succeeds() throws BrokerRemoteException, BrokerVirtualHostGrantException { final ResponseEntity<Void> mock = ResponseEntity.status(HttpStatus.NO_CONTENT) .build(); @@ -147,7 +148,7 @@ public class BrokerServiceGatewayTest extends BaseUnitTest { } @Test - public void createUser_succeeds() throws BrokerVirtualHostCreationException { + public void createUser_succeeds() throws BrokerRemoteException, BrokerVirtualHostModificationException { final ResponseEntity<Void> mock = ResponseEntity.status(HttpStatus.NO_CONTENT) .build(); @@ -169,7 +170,7 @@ public class BrokerServiceGatewayTest extends BaseUnitTest { .thenReturn(mock); /* test */ - assertThrows(BrokerVirtualHostCreationException.class, () -> { + assertThrows(BrokerVirtualHostModificationException.class, () -> { brokerServiceGateway.createUser(USER_1_USERNAME); }); } diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/SwaggerComponentTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/SwaggerEndpointMvcTest.java similarity index 85% rename from dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/SwaggerComponentTest.java rename to dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/SwaggerEndpointMvcTest.java index c19bd4bc018cb102665690188d3ab575bff44cfb..c07446eee592ebdb8d2b7d7e631fdc9f10eca927 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/SwaggerComponentTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/SwaggerEndpointMvcTest.java @@ -1,16 +1,14 @@ -package at.tuwien.endpoints; +package at.tuwien.mvc; import at.tuwien.BaseUnitTest; import at.tuwien.annotations.MockAmqp; import at.tuwien.annotations.MockOpensearch; -import at.tuwien.repository.sdb.DatabaseIdxRepository; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; @@ -24,7 +22,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest @MockAmqp @MockOpensearch -public class SwaggerComponentTest extends BaseUnitTest { +public class SwaggerEndpointMvcTest extends BaseUnitTest { @Autowired private MockMvc mockMvc; diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/UserEndpointMvcTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/UserEndpointMvcTest.java new file mode 100644 index 0000000000000000000000000000000000000000..44e4c35fcc3cbfa133a258f440a0fa0ef8b604f6 --- /dev/null +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/UserEndpointMvcTest.java @@ -0,0 +1,109 @@ +package at.tuwien.mvc; + +import at.tuwien.BaseUnitTest; +import at.tuwien.annotations.MockAmqp; +import at.tuwien.annotations.MockOpensearch; +import at.tuwien.api.auth.CreateUserDto; +import at.tuwien.api.auth.SignupRequestDto; +import at.tuwien.api.keycloak.UserCreateDto; +import at.tuwien.exception.BrokerRemoteException; +import at.tuwien.exception.KeycloakRemoteException; +import at.tuwien.gateway.BrokerServiceGateway; +import at.tuwien.gateway.KeycloakGateway; +import at.tuwien.gateway.impl.KeycloakGatewayImpl; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; + +import static at.tuwien.test.utils.ObjectUtil.asJsonString; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@Log4j2 +@ExtendWith(SpringExtension.class) +@AutoConfigureMockMvc +@SpringBootTest +@MockAmqp +@MockOpensearch +public class UserEndpointMvcTest extends BaseUnitTest { + + @MockBean + private BrokerServiceGateway brokerServiceGateway; + + @MockBean + private KeycloakGatewayImpl keycloakGateway; + + @Autowired + private MockMvc mockMvc; + + @Test + public void createUser_malformed_fails() throws Exception { + final SignupRequestDto request = SignupRequestDto.builder() + .username(USER_1_USERNAME) + .password(USER_1_PASSWORD) + .email("invalid_email") + .build(); + + /* mock */ + doNothing() + .when(brokerServiceGateway) + .createUser(USER_1_USERNAME); + + /* test */ + this.mockMvc.perform(post("/api/user") + .content(asJsonString(request)) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().is(400)); + } + + @Test + public void createUser_keycloakOffline_503_fails() throws Exception { + + /* mock */ + doThrow(KeycloakRemoteException.class) + .when(keycloakGateway) + .createUser(any(UserCreateDto.class)); + + /* test */ + this.mockMvc.perform(post("/api/user") + .content(asJsonString(USER_1_SIGNUP_REQUEST_DTO)) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().is(503)); + } + + @Test + public void createUser_brokerOffline_503_fails() throws Exception { + + /* mock */ + doNothing() + .when(keycloakGateway) + .createUser(any(UserCreateDto.class)); + when(keycloakGateway.findByUsername(USER_1_USERNAME)) + .thenReturn(USER_1_KEYCLOAK_DTO); + doThrow(BrokerRemoteException.class) + .when(brokerServiceGateway) + .createUser(USER_1_USERNAME); + + /* test */ + this.mockMvc.perform(post("/api/user") + .content(asJsonString(USER_1_SIGNUP_REQUEST_DTO)) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().is(503)); + } + +} diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/AuthenticationServiceIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/AuthenticationServiceIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1f4240a8647e00668c77220a93b155622e4c6fa8 --- /dev/null +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/AuthenticationServiceIntegrationTest.java @@ -0,0 +1,87 @@ +package at.tuwien.service; + +import at.tuwien.BaseUnitTest; +import at.tuwien.annotations.MockAmqp; +import at.tuwien.annotations.MockOpensearch; +import at.tuwien.entities.user.User; +import at.tuwien.exception.*; +import at.tuwien.gateway.KeycloakGateway; +import dasniko.testcontainers.keycloak.KeycloakContainer; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.testcontainers.images.PullPolicy; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Log4j2 +@Testcontainers +@EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class) +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) +@SpringBootTest +@ExtendWith(SpringExtension.class) +@MockAmqp +@MockOpensearch +public class AuthenticationServiceIntegrationTest extends BaseUnitTest { + + @Autowired + private AuthenticationService authenticationService; + + @Autowired + private KeycloakGateway keycloakGateway; + + @Container + private static KeycloakContainer keycloakContainer = new KeycloakContainer("quay.io/keycloak/keycloak:21.0") + .withImagePullPolicy(PullPolicy.alwaysPull()) + .withAdminUsername("fda") + .withAdminPassword("fda") + .withRealmImportFile("./dbrepo-realm.json") + .withEnv("KC_HOSTNAME_STRICT_HTTPS", "false"); + + @DynamicPropertySource + static void keycloakProperties(DynamicPropertyRegistry registry) { + registry.add("fda.keycloak.endpoint", () -> "http://localhost:" + keycloakContainer.getMappedPort(8080)); + } + + @Test + public void delete_succeeds() throws UserNotFoundException, KeycloakRemoteException, AccessDeniedException, + UserEmailAlreadyExistsException, UserAlreadyExistsException { + + /* mock */ + try { + keycloakGateway.deleteUser(keycloakGateway.findByUsername(USER_1_USERNAME).getId()); + } catch (Exception e) { + /* ignore */ + } + keycloakGateway.createUser(USER_1_KEYCLOAK_SIGNUP_REQUEST); + + /* test */ + authenticationService.delete(keycloakGateway.findByUsername(USER_1_USERNAME).getId()); + } + + @Test + public void create_succeeds() throws UserNotFoundException, KeycloakRemoteException, AccessDeniedException, + UserEmailAlreadyExistsException, UserAlreadyExistsException { + + /* mock */ + try { + keycloakGateway.deleteUser(keycloakGateway.findByUsername(USER_1_USERNAME).getId()); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + authenticationService.create(USER_1_SIGNUP_REQUEST_DTO); + } + +} diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java index 67de4364b64bc0dcc5d5f32612a89bbcb5945f03..5fd4fdf02b04b37fe8f913c7e2fa85a7b34fcef2 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java @@ -6,10 +6,12 @@ import at.tuwien.annotations.MockOpensearch; import at.tuwien.api.database.*; import at.tuwien.config.MariaDbConfig; import at.tuwien.config.MariaDbContainerConfig; +import at.tuwien.config.QueryConfig; import at.tuwien.entities.database.Database; import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.gateway.KeycloakGateway; +import at.tuwien.mapper.DatabaseMapper; import at.tuwien.repository.mdb.*; import at.tuwien.repository.sdb.DatabaseIdxRepository; import at.tuwien.service.impl.MariaDbServiceImpl; @@ -17,6 +19,7 @@ import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -26,13 +29,14 @@ import org.testcontainers.containers.MariaDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLInvalidAuthorizationSpecException; import java.util.List; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @Log4j2 @Testcontainers @@ -46,6 +50,9 @@ public class DatabaseServiceIntegrationTest extends BaseUnitTest { @MockBean private DatabaseIdxRepository databaseIdxRepository; + @MockBean + private QueryConfig queryConfig; + @Autowired private UserRepository userRepository; @@ -109,6 +116,8 @@ public class DatabaseServiceIntegrationTest extends BaseUnitTest { .thenReturn(DATABASE_1_DTO); when(databaseIdxRepository.save(any(DatabaseDto.class))) .thenReturn(DATABASE_1_DTO); + when(queryConfig.getGrantPrivileges()) + .thenReturn("SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE"); /* test */ generic_create(DATABASE_1_CREATE, DATABASE_1); @@ -123,6 +132,8 @@ public class DatabaseServiceIntegrationTest extends BaseUnitTest { .thenReturn(DATABASE_1_DTO); when(databaseIdxRepository.save(any(DatabaseDto.class))) .thenReturn(DATABASE_1_DTO); + when(queryConfig.getGrantPrivileges()) + .thenReturn("SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE"); /* test */ generic_create(DATABASE_1_CREATE, DATABASE_1); @@ -139,6 +150,8 @@ public class DatabaseServiceIntegrationTest extends BaseUnitTest { when(databaseIdxRepository.save(any(DatabaseDto.class))) .thenReturn(DATABASE_2_DTO) .thenReturn(DATABASE_3_DTO); + when(queryConfig.getGrantPrivileges()) + .thenReturn("SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE"); /* test */ generic_create(DATABASE_2_CREATE, DATABASE_2); @@ -155,6 +168,8 @@ public class DatabaseServiceIntegrationTest extends BaseUnitTest { when(databaseIdxRepository.save(any(DatabaseDto.class))) .thenReturn(DATABASE_3_DTO) .thenReturn(DATABASE_2_DTO); + when(queryConfig.getGrantPrivileges()) + .thenReturn("SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE"); /* test */ generic_create(DATABASE_3_CREATE, DATABASE_3); @@ -169,11 +184,31 @@ public class DatabaseServiceIntegrationTest extends BaseUnitTest { databaseRepository.deleteAll(); when(databaseIdxRepository.save(any(DatabaseDto.class))) .thenReturn(DATABASE_1_DTO); + when(queryConfig.getGrantPrivileges()) + .thenReturn("SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE"); final Database database = generic_create(DATABASE_1_CREATE, DATABASE_1); /* test */ - MariaDbConfig.getPrivileges(mariaDBContainer.getHost(), mariaDBContainer.getMappedPort(3306), database.getInternalName(), USER_1_USERNAME, USER_1_PASSWORD); + MariaDbConfig.getPrivileges(mariaDBContainer.getHost(), 3308, database.getInternalName(), USER_1_USERNAME, USER_1_PASSWORD); + } + + @Test + public void create_existsRollbackSucceeds_fails() throws Exception { + + /* mock */ + MariaDbConfig.dropDatabase(CONTAINER_1, DATABASE_1_INTERNALNAME); + databaseRepository.deleteAll(); + when(databaseIdxRepository.save(any(DatabaseDto.class))) + .thenReturn(DATABASE_1_DTO); + when(queryConfig.getGrantPrivileges()) + .thenReturn("" /* (1) */, "SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE"/* (2) */); + + /* test */ + assertThrows(DatabaseMalformedException.class, () -> { + databaseService.create(DATABASE_1_CREATE, USER_1_PRINCIPAL); // (1) + }); + generic_create(DATABASE_1_CREATE, DATABASE_1); // (2) } @Test @@ -184,22 +219,28 @@ public class DatabaseServiceIntegrationTest extends BaseUnitTest { databaseAccessRepository.save(DATABASE_1_USER_3_READ_ACCESS); when(databaseIdxRepository.save(any(DatabaseDto.class))) .thenReturn(DATABASE_1_DTO); + when(queryConfig.getGrantPrivileges()) + .thenReturn("SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE"); /* test */ assertThrows(SQLInvalidAuthorizationSpecException.class, () -> { - MariaDbConfig.getPrivileges(mariaDBContainer.getHost(), mariaDBContainer.getMappedPort(3306), USER_3_USERNAME, USER_4_PASSWORD); + MariaDbConfig.getPrivileges(mariaDBContainer.getHost(), 3308, USER_3_USERNAME, USER_4_PASSWORD); }); databaseService.updatePassword(User.builder() - .id(USER_3_ID) - .username(USER_3_USERNAME) - .mariadbPassword(USER_4_DATABASE_PASSWORD) + .id(USER_3_ID) + .username(USER_3_USERNAME) + .mariadbPassword(USER_4_DATABASE_PASSWORD) .build()); - MariaDbConfig.getPrivileges(mariaDBContainer.getHost(), mariaDBContainer.getMappedPort(3306), USER_3_USERNAME, USER_4_PASSWORD); + MariaDbConfig.getPrivileges(mariaDBContainer.getHost(), 3308, USER_3_USERNAME, USER_4_PASSWORD); } @Test public void create_queryStore_succeeds() throws Exception { + /* mock */ + when(queryConfig.getGrantPrivileges()) + .thenReturn("SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE"); + /* test */ generic_insert(QUERY_4_STATEMENT, 1L); } @@ -207,6 +248,10 @@ public class DatabaseServiceIntegrationTest extends BaseUnitTest { @Test public void create_queryStoreSameQueryHash_succeeds() throws Exception { + /* mock */ + when(queryConfig.getGrantPrivileges()) + .thenReturn("SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE"); + /* test */ generic_insert(QUERY_4_STATEMENT, 1L); generic_insert(QUERY_5_STATEMENT, 2L); @@ -216,6 +261,10 @@ public class DatabaseServiceIntegrationTest extends BaseUnitTest { @Test public void create_systemProcedure_succeeds() throws Exception { + /* mock */ + when(queryConfig.getGrantPrivileges()) + .thenReturn("SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE"); + /* test */ generic_system_insert(CONTAINER_1_PRIVILEGED_USERNAME, CONTAINER_1_PRIVILEGED_PASSWORD); } @@ -223,6 +272,10 @@ public class DatabaseServiceIntegrationTest extends BaseUnitTest { @Test public void create_systemProcedure_fails() { + /* mock */ + when(queryConfig.getGrantPrivileges()) + .thenReturn("SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE"); + /* test */ assertThrows(SQLException.class, () -> { generic_system_insert("junit1", "junit1"); @@ -232,6 +285,10 @@ public class DatabaseServiceIntegrationTest extends BaseUnitTest { @Test public void create_userProcedureRoot_succeeds() throws SQLException, QueryMalformedException { + /* mock */ + when(queryConfig.getGrantPrivileges()) + .thenReturn("SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE"); + /* test */ generic_user_insert(CONTAINER_1_PRIVILEGED_USERNAME, CONTAINER_1_PRIVILEGED_PASSWORD); } @@ -244,6 +301,8 @@ public class DatabaseServiceIntegrationTest extends BaseUnitTest { MariaDbConfig.createInitDatabase(CONTAINER_1, DATABASE_3); mariaDbConfig.grantUserPermissions(CONTAINER_1, DATABASE_3, "junit1"); databaseAccessRepository.save(DATABASE_3_USER_1_WRITE_ALL_ACCESS); + when(queryConfig.getGrantPrivileges()) + .thenReturn("SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE"); /* test */ generic_user_insert("junit1", "junit1"); @@ -275,7 +334,7 @@ public class DatabaseServiceIntegrationTest extends BaseUnitTest { } @Test - public void transfer_succeeds() throws DatabaseNotFoundException, UserNotFoundException{ + public void transfer_succeeds() throws DatabaseNotFoundException, UserNotFoundException { final DatabaseTransferDto request = DatabaseTransferDto.builder() .username(USER_2_USERNAME) .build(); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java index 73429969253b1e2ea7761d20d995c4ba95a52716..92236fbb31b2c70db18906792c5c5a0068ef0b1d 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java @@ -100,7 +100,7 @@ public class DatabaseServiceUnitTest extends BaseUnitTest { } @Test - public void create_notFound_fails() throws UserNotFoundException, KeycloakRemoteException, AccessDeniedException { + public void create_notFound_fails() throws UserNotFoundException { final DatabaseCreateDto request = DatabaseCreateDto.builder() .cid(CONTAINER_1_ID) .name(DATABASE_1_NAME) diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/MessageQueueServiceIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/MessageQueueServiceIntegrationTest.java index b68d07b0df8de1599b9c0918b304b26cf72787cb..8e9c2cb7b9609238afdbf9dd0c05b2a301c0d050 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/MessageQueueServiceIntegrationTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/MessageQueueServiceIntegrationTest.java @@ -4,7 +4,8 @@ import at.tuwien.BaseUnitTest; import at.tuwien.annotations.MockOpensearch; import at.tuwien.api.amqp.PermissionDto; import at.tuwien.exception.AmqpException; -import at.tuwien.exception.BrokerVirtualHostCreationException; +import at.tuwien.exception.BrokerRemoteException; +import at.tuwien.exception.BrokerVirtualHostModificationException; import at.tuwien.exception.BrokerVirtualHostGrantException; import at.tuwien.repository.mdb.DatabaseRepository; import at.tuwien.repository.mdb.TableRepository; @@ -85,14 +86,14 @@ public class MessageQueueServiceIntegrationTest extends BaseUnitTest { } @Test - public void createUser_succeeds() throws BrokerVirtualHostCreationException { + public void createUser_succeeds() throws BrokerRemoteException, BrokerVirtualHostModificationException { /* test */ messageQueueService.createUser(USER_1_USERNAME); } @Test - public void updatePermissions_empty_succeeds() throws BrokerVirtualHostGrantException { + public void updatePermissions_empty_succeeds() throws BrokerRemoteException, BrokerVirtualHostGrantException { /* test */ final PermissionDto permissions = updatePermissions_generic(); @@ -104,7 +105,7 @@ public class MessageQueueServiceIntegrationTest extends BaseUnitTest { } @Test - public void updatePermissions_owner_succeeds() throws BrokerVirtualHostGrantException { + public void updatePermissions_owner_succeeds() throws BrokerRemoteException, BrokerVirtualHostGrantException { /* mock */ when(databaseRepository.findConfigureAccess(USER_1_ID)) @@ -124,7 +125,7 @@ public class MessageQueueServiceIntegrationTest extends BaseUnitTest { } @Test - public void updatePermissions_ownerNoAccess_succeeds() throws BrokerVirtualHostGrantException { + public void updatePermissions_ownerNoAccess_succeeds() throws BrokerRemoteException, BrokerVirtualHostGrantException { /* mock */ when(databaseRepository.findConfigureAccess(USER_1_ID)) @@ -163,7 +164,7 @@ public class MessageQueueServiceIntegrationTest extends BaseUnitTest { /* ## GENERIC TEST CASES ## */ /* ################################################################################################### */ - protected PermissionDto updatePermissions_generic() throws BrokerVirtualHostGrantException { + protected PermissionDto updatePermissions_generic() throws BrokerRemoteException, BrokerVirtualHostGrantException { /* mock */ amqpUtils.createUser(USER_1_USERNAME, USER_1_RABBITMQ_CREATE_DTO); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java index 5628f041c988611bb0cdf0a2bda9f3cb16dfe68f..07fa1e92b65492df0e267077430297d6f82f77ab 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java @@ -23,6 +23,8 @@ import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.testcontainers.containers.MariaDBContainer; import org.testcontainers.junit.jupiter.Container; diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java index c5669b5407ea0225db338a63b204364895ebf1c4..4be1fb518bebd3dcc00d03bf092903a2a63f396d 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java @@ -10,6 +10,7 @@ import at.tuwien.config.MariaDbConfig; import at.tuwien.config.MariaDbContainerConfig; import at.tuwien.config.RabbitMqConfig; import at.tuwien.exception.AmqpException; +import at.tuwien.exception.BrokerRemoteException; import at.tuwien.gateway.BrokerServiceGateway; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.repository.mdb.DatabaseRepository; @@ -208,7 +209,7 @@ public class QueueServiceIntegrationTest extends BaseUnitTest { @Test @Disabled("Not testable") - public void restore_succeeds() throws AmqpException, IOException { + public void restore_succeeds() throws AmqpException, IOException, BrokerRemoteException { /* mock */ when(tableRepository.findAll()) diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java index ad229d6aa39d0d6eb4e6d9c8bf8515f8dd2f571d..ba29bea0102b62422c9f725799cf6ec00f21999c 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java @@ -8,7 +8,6 @@ import at.tuwien.api.user.*; import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.repository.mdb.UserRepository; -import dasniko.testcontainers.keycloak.KeycloakContainer; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -18,12 +17,8 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.annotation.Transactional; -import org.testcontainers.images.PullPolicy; -import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import java.util.List; @@ -46,19 +41,6 @@ public class UserServiceIntegrationTest extends BaseUnitTest { @Autowired private UserService userService; - @Container - private static KeycloakContainer keycloakContainer = new KeycloakContainer("quay.io/keycloak/keycloak:21.0") - .withImagePullPolicy(PullPolicy.alwaysPull()) - .withAdminUsername("fda") - .withAdminPassword("fda") - .withRealmImportFile("./dbrepo-realm.json") - .withEnv("KC_HOSTNAME_STRICT_HTTPS", "false"); - - @DynamicPropertySource - static void elasticsearchProperties(DynamicPropertyRegistry registry) { - registry.add("fda.keycloak.endpoint", () -> "http://localhost:" + keycloakContainer.getMappedPort(8080)); - } - @BeforeEach public void beforeEach() { userRepository.save(USER_1); @@ -100,38 +82,10 @@ public class UserServiceIntegrationTest extends BaseUnitTest { .build(); /* test */ - final User response = userService.create(request); + final User response = userService.create(request, USER_2_ID); assertEquals(USER_2_USERNAME, response.getUsername()); } - @Test - public void create_nonUniqueUsername_fails() { - final SignupRequestDto request = SignupRequestDto.builder() - .username(USER_1_USERNAME) - .password(USER_2_PASSWORD) - .email(USER_2_EMAIL) - .build(); - - /* test */ - assertThrows(UserEmailAlreadyExistsException.class, () -> { - userService.create(request); - }); - } - - @Test - public void create_nonUniqueEmail_fails() { - final SignupRequestDto request = SignupRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .email(USER_1_EMAIL) - .build(); - - /* test */ - assertThrows(UserAlreadyExistsException.class, () -> { - userService.create(request); - }); - } - @Test @Transactional public void modify_succeeds() throws UserNotFoundException { @@ -178,7 +132,7 @@ public class UserServiceIntegrationTest extends BaseUnitTest { .username(USER_3_USERNAME) .password(USER_3_PASSWORD) .email(USER_3_EMAIL) - .build()); + .build(), USER_3_ID); /* test */ userService.updatePassword(user.getId(), request); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java index 35fc49b369dd59cff21217a05d54bcf8a07f7096..c5660860ded2c91c65ff500e3a912c1b7d588724 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java @@ -92,7 +92,7 @@ public class UserServiceUnitTest extends BaseUnitTest { .thenReturn(USER_1_KEYCLOAK_DTO); /* test */ - final User response = userService.create(USER_1_SIGNUP_REQUEST_DTO); + final User response = userService.create(USER_1_SIGNUP_REQUEST_DTO, USER_1_ID); assertEquals(USER_1_ID, response.getId()); assertEquals(USER_1_USERNAME, response.getUsername()); } diff --git a/dbrepo-metadata-service/rest-service/src/test/resources/application.properties b/dbrepo-metadata-service/rest-service/src/test/resources/application.properties index 2e2bd0d465c79c57ce5ed3a24071c10789e2c503..a004db1bceab10545f026b3752c6db633ddc80b2 100644 --- a/dbrepo-metadata-service/rest-service/src/test/resources/application.properties +++ b/dbrepo-metadata-service/rest-service/src/test/resources/application.properties @@ -16,7 +16,7 @@ spring.jpa.hibernate.ddl-auto=create # logging logging.level.root=error -logging.level.at.tuwien.=debug +logging.level.at.tuwien.=${LOG_LEVEL:-debug} # rabbitmq fda.broker.endpoint=http://localhost:15672 diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/BrokerServiceGateway.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/BrokerServiceGateway.java index ba38f6118fce9c18829ed8cf5c96d9866a37cb47..7e55074f7e1197aaa1e60c1e83ee31e8e5905531 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/BrokerServiceGateway.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/BrokerServiceGateway.java @@ -4,47 +4,66 @@ import at.tuwien.api.amqp.ConsumerDto; import at.tuwien.api.amqp.CreateVirtualHostDto; import at.tuwien.api.amqp.GrantVirtualHostPermissionsDto; import at.tuwien.api.user.ExchangeUpdatePermissionsDto; -import at.tuwien.exception.BrokerVirtualHostCreationException; +import at.tuwien.exception.BrokerRemoteException; +import at.tuwien.exception.BrokerVirtualHostModificationException; import at.tuwien.exception.BrokerVirtualHostGrantException; import java.util.List; public interface BrokerServiceGateway { - List<ConsumerDto> findAllConsumers(); + /** + * Finds all active consumers on the virtual host "dbrepo". + * + * @return The list of active consumers. + * @throws BrokerRemoteException The Broker Service did not respond within the 3s timeout. + */ + List<ConsumerDto> findAllConsumers() throws BrokerRemoteException; /** * Create virtual host at the queue service. * * @param data The virtual host. - * @throws BrokerVirtualHostCreationException The queue service did not respond within the 3s timeout. + * @throws BrokerVirtualHostModificationException The virtual host could not be created. + * @throws BrokerRemoteException The Broker Service did not respond within the 3s timeout. */ - void createVirtualHost(CreateVirtualHostDto data) throws BrokerVirtualHostCreationException; + void createVirtualHost(CreateVirtualHostDto data) throws BrokerVirtualHostModificationException, BrokerRemoteException; /** * Grants a user permission at a virtual host in the queue service. * * @param username The username of the user. * @param data The grant data. - * @throws BrokerVirtualHostGrantException The queue service did not respond within the 3s timeout. + * @throws BrokerVirtualHostGrantException The permissions could not be granted. + * @throws BrokerRemoteException The Broker Service did not respond within the 3s timeout. + */ + void grantPermission(String username, ExchangeUpdatePermissionsDto data) throws BrokerVirtualHostGrantException, BrokerRemoteException; + + /** + * Create user on the broker service with given username. + * + * @param username The username. + * @throws BrokerRemoteException The Broker Service did not respond within the 3s timeout. + * @throws BrokerVirtualHostModificationException The user could not be created. */ - void grantPermission(String username, ExchangeUpdatePermissionsDto data) - throws BrokerVirtualHostGrantException; + void createUser(String username) throws BrokerRemoteException, BrokerVirtualHostModificationException; /** - * Create user on the broker service + * Deletes a user on the broker service with given username. * - * @param username The new username. - * @throws BrokerVirtualHostCreationException The user could not be created. + * @param username The username. + * @throws BrokerRemoteException The Broker Service did not respond within the 3s timeout. + * @throws BrokerVirtualHostModificationException The user could not be deleted. */ - void createUser(String username) throws BrokerVirtualHostCreationException; + void deleteUser(String username) throws BrokerRemoteException, BrokerVirtualHostModificationException; /** * Grants a user permission at a virtual host in the queue service. * * @param username The username of the user. * @param data The grant data. - * @throws BrokerVirtualHostGrantException The queue service did not respond within the 3s timeout. + * @throws BrokerRemoteException The Broker Service did not respond within the 3s timeout. + * @throws BrokerVirtualHostGrantException The permissions could not be granted. */ - void grantPermission(String username, GrantVirtualHostPermissionsDto data) throws BrokerVirtualHostGrantException; + void grantPermission(String username, GrantVirtualHostPermissionsDto data) throws BrokerRemoteException, BrokerVirtualHostGrantException; } diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/KeycloakGateway.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/KeycloakGateway.java index 612ee2eb372cbee0f0c10067d55eba0d2029f988..3614e43fcbbbc21c8f9808974355b97b0d6750a4 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/KeycloakGateway.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/KeycloakGateway.java @@ -9,11 +9,47 @@ import java.util.UUID; public interface KeycloakGateway { + /** + * Creates a user at the Authentication Service with given credentials. + * + * @param data The user credentials. + * @throws AccessDeniedException The admin token could not be obtained. + * @throws KeycloakRemoteException The Authentication Service was not able to respond within the 3s timeout. + * @throws UserAlreadyExistsException The user already exists at the Authentication Service. + * @throws UserEmailAlreadyExistsException The user email already exists in the metadata database. + */ void createUser(UserCreateDto data) throws AccessDeniedException, KeycloakRemoteException, UserAlreadyExistsException, UserEmailAlreadyExistsException; + /** + * Deletes a user at the Authentication Service with given user id. + * + * @param id The user id. + * @throws KeycloakRemoteException The Authentication Service was not able to respond within the 3s timeout. + * @throws AccessDeniedException The admin token could not be obtained. + * @throws UserNotFoundException The user was not found at the Authentication Service. + */ + void deleteUser(UUID id) throws KeycloakRemoteException, AccessDeniedException, UserNotFoundException; + + /** + * Update the credentials for a given user. + * + * @param id The user id. + * @param password The user credential. + * @throws AccessDeniedException The admin token could not be obtained. + * @throws KeycloakRemoteException The Authentication Service was not able to respond within the 3s timeout. + */ void updateUserCredentials(UUID id, UserPasswordDto password) throws AccessDeniedException, KeycloakRemoteException; + /** + * Finds a user in the metadata database by given username. + * + * @param username The user username. + * @return The updated user. + * @throws AccessDeniedException The admin token could not be obtained. + * @throws UserNotFoundException The user was not found, + * @throws KeycloakRemoteException The Authentication Service was not able to respond within the 3s timeout. + */ UserDto findByUsername(String username) throws AccessDeniedException, UserNotFoundException, KeycloakRemoteException; } diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java index 52de16e2f364e3ea6f4896763dc0514cc0331a64..a13de9f29ccc761a16aaa4dd5cf18ab11283acf1 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java @@ -5,19 +5,16 @@ import at.tuwien.api.amqp.CreateUserDto; import at.tuwien.api.amqp.CreateVirtualHostDto; import at.tuwien.api.amqp.GrantVirtualHostPermissionsDto; import at.tuwien.api.user.ExchangeUpdatePermissionsDto; -import at.tuwien.config.AmqpConfig; import at.tuwien.config.GatewayConfig; -import at.tuwien.exception.BrokerVirtualHostCreationException; +import at.tuwien.exception.BrokerRemoteException; +import at.tuwien.exception.BrokerVirtualHostModificationException; import at.tuwien.exception.BrokerVirtualHostGrantException; import at.tuwien.gateway.BrokerServiceGateway; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.codec.binary.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.core.ParameterizedTypeReference; -import org.springframework.core.env.Environment; import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -25,50 +22,54 @@ import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.net.URI; -import java.nio.charset.Charset; -import java.util.Arrays; import java.util.List; @Slf4j @Service public class BrokerServiceGatewayImpl implements BrokerServiceGateway { - private final Environment environment; private final RestTemplate restTemplate; private final GatewayConfig gatewayConfig; private final static String VIRTUAL_SERVER = "dbrepo"; @Autowired - public BrokerServiceGatewayImpl(Environment environment, GatewayConfig gatewayConfig, + public BrokerServiceGatewayImpl(GatewayConfig gatewayConfig, @Qualifier("brokerRestTemplate") RestTemplate restTemplate) { - this.environment = environment; this.restTemplate = restTemplate; this.gatewayConfig = gatewayConfig; } - private String parseUrl(String path) { - final String url = "/api" + path; - log.debug("parse url: {}", url); - return url; - } - @Override - public void createVirtualHost(CreateVirtualHostDto data) throws BrokerVirtualHostCreationException { - final ResponseEntity<Void> response = restTemplate.exchange(parseUrl("/vhost"), HttpMethod.POST, - new HttpEntity<>(data), Void.class); + public void createVirtualHost(CreateVirtualHostDto data) throws BrokerVirtualHostModificationException, BrokerRemoteException { + final String url = "/api/vhost"; + log.trace("POST {}{}", gatewayConfig.getBrokerEndpoint(), url); + final ResponseEntity<Void> response; + try { + response = restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(data), Void.class); + } catch (Exception e) { + log.error("Failed to create virtual host: remote host answered unexpected: {}", e.getMessage()); + throw new BrokerRemoteException("Failed to create virtual host: remote host answered unexpected", e); + } if (!response.getStatusCode().equals(HttpStatus.CREATED)) { log.error("Failed to create virtual host: {}", response.getStatusCode()); - throw new BrokerVirtualHostCreationException("Failed to create virtual host"); + throw new BrokerVirtualHostModificationException("Failed to create virtual host"); } log.info("Create virtual host with name {}", data.getName()); } @Override public void grantPermission(String username, ExchangeUpdatePermissionsDto data) - throws BrokerVirtualHostGrantException { - final ResponseEntity<Void> response = restTemplate.exchange(parseUrl("/topic-permissions/dbrepo/" + username), HttpMethod.PUT, - new HttpEntity<>(data), Void.class); + throws BrokerVirtualHostGrantException, BrokerRemoteException { + final String url = "/api/topic-permissions/dbrepo/" + username; + log.trace("PUT {}{}", gatewayConfig.getBrokerEndpoint(), url); + final ResponseEntity<Void> response; + try { + response = restTemplate.exchange(url, HttpMethod.PUT, new HttpEntity<>(data), Void.class); + } catch (Exception e) { + log.error("Failed to grant permissions: remote host answered unexpected: {}", e.getMessage()); + throw new BrokerRemoteException("Failed to grant permissions: remote host answered unexpected", e); + } if (!response.getStatusCode().equals(HttpStatus.CREATED) && !response.getStatusCode().equals(HttpStatus.NO_CONTENT)) { log.error("Failed to grant exchange: {}", response.getStatusCode()); throw new BrokerVirtualHostGrantException("Failed to grant exchange"); @@ -77,25 +78,57 @@ public class BrokerServiceGatewayImpl implements BrokerServiceGateway { } @Override - public void createUser(String username) throws BrokerVirtualHostCreationException { + public void createUser(String username) throws BrokerRemoteException, BrokerVirtualHostModificationException { final CreateUserDto data = CreateUserDto.builder() .passwordHash("") .tags("") .build(); - final ResponseEntity<Void> response = restTemplate.exchange(parseUrl("/users/" + username), HttpMethod.PUT, - new HttpEntity<>(data), Void.class); + final String url = "/api/users/" + username; + log.trace("PUT {}{}", gatewayConfig.getBrokerEndpoint(), url); + final ResponseEntity<Void> response; + try { + response = restTemplate.exchange(url, HttpMethod.PUT, new HttpEntity<>(data), Void.class); + } catch (Exception e) { + log.error("Failed to create user: remote host answered unexpected: {}", e.getMessage()); + throw new BrokerRemoteException("Failed to create user: remote host answered unexpected", e); + } if (!response.getStatusCode().equals(HttpStatus.CREATED) && !response.getStatusCode().equals(HttpStatus.NO_CONTENT)) { log.error("Failed to create user: {}", response.getStatusCode()); - throw new BrokerVirtualHostCreationException("Failed to create user"); + throw new BrokerVirtualHostModificationException("Failed to create user"); } log.info("Created user with username {}", username); } @Override - public void grantPermission(String username, GrantVirtualHostPermissionsDto data) - throws BrokerVirtualHostGrantException { - final ResponseEntity<Void> response = restTemplate.exchange(parseUrl("/permissions/dbrepo/" + username), HttpMethod.PUT, - new HttpEntity<>(data), Void.class); + public void deleteUser(String username) throws BrokerRemoteException, BrokerVirtualHostModificationException { + final String url = "/api/users/" + username; + log.trace("DELETE {}{}", gatewayConfig.getBrokerEndpoint(), url); + final ResponseEntity<Void> response; + try { + response = restTemplate.exchange(url, HttpMethod.DELETE, new HttpEntity<>(null), Void.class); + } catch (Exception e) { + log.error("Failed to delete user: remote host answered unexpected: {}", e.getMessage()); + throw new BrokerRemoteException("Failed to delete user: remote host answered unexpected", e); + } + if (!response.getStatusCode().equals(HttpStatus.NO_CONTENT)) { + log.error("Failed to delete user: {}", response.getStatusCode()); + throw new BrokerVirtualHostModificationException("Failed to create user"); + } + log.info("Deleted user with username {}", username); + } + + @Override + public void grantPermission(String username, GrantVirtualHostPermissionsDto data) throws BrokerRemoteException, + BrokerVirtualHostGrantException { + final String url = "/api/permissions/dbrepo/" + username; + log.trace("PUT {}{}", gatewayConfig.getBrokerEndpoint(), url); + final ResponseEntity<Void> response; + try { + response = restTemplate.exchange(url, HttpMethod.PUT, new HttpEntity<>(data), Void.class); + } catch (Exception e) { + log.error("Failed to create permissions: remote host answered unexpected: {}", e.getMessage()); + throw new BrokerRemoteException("Failed to create permissions: remote host answered unexpected", e); + } if (!response.getStatusCode().equals(HttpStatus.CREATED) && !response.getStatusCode().equals(HttpStatus.NO_CONTENT)) { log.error("Failed to grant virtual host: {}", response.getStatusCode()); throw new BrokerVirtualHostGrantException("Failed to grant virtual host"); @@ -104,19 +137,19 @@ public class BrokerServiceGatewayImpl implements BrokerServiceGateway { } @Override - public List<ConsumerDto> findAllConsumers() { - final StringBuilder urlBuilder = new StringBuilder(gatewayConfig.getBrokerEndpoint()) - .append("/api"); - if (Arrays.stream(environment.getActiveProfiles()).noneMatch(p -> p.equals("junit"))) { - urlBuilder.append("/broker"); - } - urlBuilder.append("/consumers/") - .append(VIRTUAL_SERVER); + public List<ConsumerDto> findAllConsumers() throws BrokerRemoteException { + final String url = "/api/consumers/" + VIRTUAL_SERVER; log.trace("gateway broker find all consumers, virtual server={}", VIRTUAL_SERVER); - final URI findUri = URI.create(urlBuilder.toString()); - final ResponseEntity<List<ConsumerDto>> response = restTemplate.exchange(findUri, HttpMethod.GET, - HttpEntity.EMPTY, new ParameterizedTypeReference<>() { - }); + log.trace("GET {}{}", gatewayConfig.getBrokerEndpoint(), url); + final ResponseEntity<List<ConsumerDto>> response; + try { + response = restTemplate.exchange(URI.create(url), HttpMethod.GET, HttpEntity.EMPTY, + new ParameterizedTypeReference<>() { + }); + } catch (Exception e) { + log.error("Failed to find consumers: remote host answered unexpected: {}", e.getMessage()); + throw new BrokerRemoteException("Failed to find consumers: remote host answered unexpected", e); + } return response.getBody(); } diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java index 91f33c336c3bb80322d573a2ad6737c0f32b40a7..6f29d138ffb858c526e1e18b0f76c0d27cb5d99c 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java @@ -33,7 +33,7 @@ public class KeycloakGatewayImpl implements KeycloakGateway { this.keycloakConfig = keycloakConfig; } - public TokenDto obtainToken() throws AccessDeniedException { + public TokenDto obtainToken() throws AccessDeniedException, KeycloakRemoteException { final HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); final MultiValueMap<String, String> payload = new LinkedMultiValueMap<>(); @@ -49,6 +49,9 @@ public class KeycloakGatewayImpl implements KeycloakGateway { } catch (ResourceAccessException | HttpServerErrorException.ServiceUnavailable e) { log.error("Failed to obtain admin token: {}", e.getMessage()); throw new AccessDeniedException("Failed to obtain admin token: " + e.getMessage()); + } catch (Exception e) { + log.error("Failed to create user: remote host answered unexpected: {}", e.getMessage()); + throw new KeycloakRemoteException("Failed to create user: remote host answered unexpected", e); } return response.getBody(); } @@ -76,11 +79,43 @@ public class KeycloakGatewayImpl implements KeycloakGateway { log.error("Conflict when creating user: {}", e.getMessage()); throw new UserAlreadyExistsException("Conflict when creating user: " + e.getMessage()); } + } catch (Exception e) { + log.error("Failed to create user: remote host answered unexpected: {}", e.getMessage()); + throw new KeycloakRemoteException("Failed to create user: remote host answered unexpected", e); } if (!response.getStatusCode().equals(HttpStatus.CREATED)) { log.error("Failed to create user: status {} was not expected", response.getStatusCode().value()); throw new KeycloakRemoteException("Failed to create user: status " + response.getStatusCode().value() + "was not expected"); } + log.info("Created user {} at authentication service", data.getUsername()); + } + + @Override + public void deleteUser(UUID id) throws KeycloakRemoteException, AccessDeniedException, UserNotFoundException { + /* obtain admin token */ + final HttpHeaders headers = new HttpHeaders(); + headers.set("Accept", "application/json"); + headers.set("Authorization", "Bearer " + obtainToken().getAccessToken()); + final String url = keycloakConfig.getKeycloakEndpoint() + "/admin/realms/dbrepo/users/" + id; + log.debug("delete user at url {}", url); + final ResponseEntity<Void> response; + try { + response = restTemplate.exchange(url, HttpMethod.DELETE, new HttpEntity<>(null, headers), Void.class); + } catch (ResourceAccessException | HttpServerErrorException.ServiceUnavailable e) { + log.error("Failed to delete user: {}", e.getMessage()); + throw new KeycloakRemoteException("Failed to delete user: " + e.getMessage()); + } catch (HttpClientErrorException.NotFound e) { + log.error("User does not exist: {}", e.getMessage()); + throw new UserNotFoundException("User does not exist: " + e.getMessage()); + } catch (Exception e) { + log.error("Failed to delete user: remote host answered unexpected: {}", e.getMessage()); + throw new KeycloakRemoteException("Failed to delete user: remote host answered unexpected", e); + } + if (!response.getStatusCode().equals(HttpStatus.NO_CONTENT)) { + log.error("Failed to delete user: status {} was not expected", response.getStatusCode().value()); + throw new KeycloakRemoteException("Failed to delete user: status " + response.getStatusCode().value() + "was not expected"); + } + log.info("Deleted user {} at authentication service", id); } @Override @@ -99,11 +134,15 @@ public class KeycloakGatewayImpl implements KeycloakGateway { } catch (ResourceAccessException | HttpServerErrorException.ServiceUnavailable e) { log.error("Failed to update user credentials: {}", e.getMessage()); throw new KeycloakRemoteException("Failed to update user credentials: " + e.getMessage()); + } catch (Exception e) { + log.error("Failed to create user: remote host answered unexpected: {}", e.getMessage()); + throw new KeycloakRemoteException("Failed to create user: remote host answered unexpected", e); } if (!response.getStatusCode().equals(HttpStatus.NO_CONTENT)) { log.error("Failed to update user credentials: status {} was not expected", response.getStatusCode().value()); throw new KeycloakRemoteException("Failed to update user credentials: status " + response.getStatusCode().value() + "was not expected"); } + log.info("Updated user {} password at authentication service", id); } @Override @@ -121,6 +160,9 @@ public class KeycloakGatewayImpl implements KeycloakGateway { } catch (ResourceAccessException | HttpServerErrorException.ServiceUnavailable e) { log.error("Failed to find user: {}", e.getMessage()); throw new KeycloakRemoteException("Failed to find user: " + e.getMessage()); + } catch (Exception e) { + log.error("Failed to create user: remote host answered unexpected: {}", e.getMessage()); + throw new KeycloakRemoteException("Failed to create user: remote host answered unexpected", e); } final UserDto[] body = response.getBody(); if (body == null || body.length != 1) { diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/listener/MessageQueueListener.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/listener/MessageQueueListener.java index f6c0d703b0783e57ca580a58436ddd48a8abc468..7b5c1ffa2cf16f1996372a085f7a0958f36cf931 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/listener/MessageQueueListener.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/listener/MessageQueueListener.java @@ -1,6 +1,7 @@ package at.tuwien.listener; import at.tuwien.exception.AmqpException; +import at.tuwien.exception.BrokerRemoteException; import org.springframework.scheduling.annotation.Scheduled; import java.util.concurrent.TimeUnit; @@ -13,5 +14,5 @@ public interface MessageQueueListener { * @throws AmqpException The consumer could not be created. */ @Scheduled(fixedDelay = 5, initialDelay = 300, timeUnit = TimeUnit.SECONDS) - void updateConsumers() throws AmqpException; + void updateConsumers() throws AmqpException, BrokerRemoteException; } diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/listener/impl/RabbitMqListenerImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/listener/impl/RabbitMqListenerImpl.java index cb6c74658562238f4d17d0d86b12ef104d4d849c..cc454351e616943f6179ab5a2bce9b43a91b9972 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/listener/impl/RabbitMqListenerImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/listener/impl/RabbitMqListenerImpl.java @@ -1,6 +1,7 @@ package at.tuwien.listener.impl; import at.tuwien.exception.AmqpException; +import at.tuwien.exception.BrokerRemoteException; import at.tuwien.listener.MessageQueueListener; import at.tuwien.service.MessageQueueService; import lombok.extern.log4j.Log4j2; @@ -23,7 +24,7 @@ public class RabbitMqListenerImpl implements MessageQueueListener { @Override @Scheduled(fixedDelay = 5, initialDelay = 300, timeUnit = TimeUnit.SECONDS) @Transactional(readOnly = true) - public void updateConsumers() throws AmqpException { + public void updateConsumers() throws AmqpException, BrokerRemoteException { messageQueueService.restore(); } diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/AuthenticationService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/AuthenticationService.java new file mode 100644 index 0000000000000000000000000000000000000000..a39e0d66c20348a35d5d2fcafbbd7ab1a364fd85 --- /dev/null +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/AuthenticationService.java @@ -0,0 +1,47 @@ +package at.tuwien.service; + +import at.tuwien.api.auth.SignupRequestDto; +import at.tuwien.api.keycloak.UserDto; +import at.tuwien.api.user.UserPasswordDto; +import at.tuwien.exception.*; + +import java.util.UUID; + +public interface AuthenticationService { + + /** + * Create a user at the Authentication Service with given credentials. + * + * @param data The credentials. + * @throws AccessDeniedException The admin token could not be obtained. + * @throws KeycloakRemoteException The Authentication Service was not able to respond within the 3s timeout. + * @throws UserAlreadyExistsException The user already exists at the Authentication Service. + * @throws UserEmailAlreadyExistsException The user email already exists in the metadata database. + */ + void create(SignupRequestDto data) throws KeycloakRemoteException, AccessDeniedException, + UserEmailAlreadyExistsException, UserAlreadyExistsException; + + /** + * Deletes a user at the Authentication Service with given user id. + * + * @param userId The user id. + * @throws KeycloakRemoteException The Authentication Service was not able to respond within the 3s timeout. + * @throws AccessDeniedException The admin token could not be obtained. + * @throws UserNotFoundException The user was not found at the Authentication Service. + */ + void delete(UUID userId) throws UserNotFoundException, KeycloakRemoteException, AccessDeniedException; + + /** + * Finds a user with given username. + * + * @param username The username. + * @return The user, if successful. + * @throws UserNotFoundException The user was not found at the Authentication Service. + * @throws KeycloakRemoteException The Authentication Service was not able to respond within the 3s timeout. + * @throws AccessDeniedException The admin token could not be obtained. + */ + UserDto findByUsername(String username) throws UserNotFoundException, KeycloakRemoteException, + AccessDeniedException; + + void updatePassword(UUID id, UserPasswordDto data) throws KeycloakRemoteException, AccessDeniedException; +} diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/MessageQueueService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/MessageQueueService.java index bfd0d4a1d32f1c2817f67ec57a977fe94b6016af..3d21c91a521e6e581f1ac7886f5d98bd44fdfc82 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/MessageQueueService.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/MessageQueueService.java @@ -1,11 +1,11 @@ package at.tuwien.service; -import at.tuwien.api.user.UserDto; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.table.Table; import at.tuwien.entities.user.User; import at.tuwien.exception.AmqpException; -import at.tuwien.exception.BrokerVirtualHostCreationException; +import at.tuwien.exception.BrokerRemoteException; +import at.tuwien.exception.BrokerVirtualHostModificationException; import at.tuwien.exception.BrokerVirtualHostGrantException; import jakarta.annotation.PostConstruct; @@ -43,13 +43,22 @@ public interface MessageQueueService { void create(Table table) throws AmqpException; /** - * Create user on the broker service + * Create user on the broker service with given username. * * @param username The username. - * @throws BrokerVirtualHostCreationException The user could not be created. + * @throws BrokerRemoteException The user could not be created. + * @throws BrokerVirtualHostModificationException The Broker Service did not respond within the 3s timeout. */ - void createUser(String username) throws BrokerVirtualHostCreationException; + void createUser(String username) throws BrokerRemoteException, BrokerVirtualHostModificationException; + /** + * Delete a user on the broker service with given username. + * + * @param username The username. + * @throws BrokerRemoteException The user could not be deleted. + * @throws BrokerVirtualHostModificationException The Broker Service did not respond within the 3s timeout. + */ + void deleteUser(String username) throws BrokerRemoteException, BrokerVirtualHostModificationException; /** * Updates the virtual host permissions in the Broker Service for a user with given principal. @@ -57,7 +66,7 @@ public interface MessageQueueService { * @param user The user. * @throws BrokerVirtualHostGrantException The Broker Service refused to grant the permissions. */ - void updatePermissions(User user) throws BrokerVirtualHostGrantException; + void updatePermissions(User user) throws BrokerVirtualHostGrantException, BrokerRemoteException; /** * Deletes an exchange for a database. @@ -82,5 +91,5 @@ public interface MessageQueueService { * * @throws AmqpException The consumer could not be created. */ - void restore() throws AmqpException; + void restore() throws AmqpException, BrokerRemoteException; } diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java index 3b94ad5d903eb52a0d17a5c869c2af897f205a3f..a5edcc519c9643afc2915217eaebd14df20550ac 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java @@ -42,7 +42,7 @@ public interface UserService { * @return The user, if successful. False otherwise. * @throws UserAlreadyExistsException The user already exists in the metadata database. */ - User create(SignupRequestDto data) throws UserAlreadyExistsException, AccessDeniedException, + User create(SignupRequestDto data, UUID id) throws UserAlreadyExistsException, AccessDeniedException, KeycloakRemoteException, UserNotFoundException, UserEmailAlreadyExistsException; /** @@ -61,7 +61,7 @@ public interface UserService { * @param id The user id. * @param data The new password. */ - void updatePassword(UUID id, UserPasswordDto data) throws KeycloakRemoteException, AccessDeniedException, UserNotFoundException; + void updatePassword(UUID id, UserPasswordDto data) throws UserNotFoundException; /** * Updates the user theme for a user with given id. diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..4a8a6c7fb3e9a195c83fe60c512e2af2ba69a413 --- /dev/null +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java @@ -0,0 +1,51 @@ +package at.tuwien.service.impl; + +import at.tuwien.api.auth.SignupRequestDto; +import at.tuwien.api.keycloak.UserDto; +import at.tuwien.api.user.UserPasswordDto; +import at.tuwien.exception.*; +import at.tuwien.gateway.KeycloakGateway; +import at.tuwien.mapper.UserMapper; +import at.tuwien.service.AuthenticationService; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.UUID; + +@Log4j2 +@Service +public class AuthenticationServiceImpl implements AuthenticationService { + + private final UserMapper userMapper; + private final KeycloakGateway keycloakGateway; + + @Autowired + public AuthenticationServiceImpl(UserMapper userMapper, KeycloakGateway keycloakGateway) { + this.userMapper = userMapper; + this.keycloakGateway = keycloakGateway; + } + + @Override + public void create(SignupRequestDto data) throws KeycloakRemoteException, AccessDeniedException, + UserEmailAlreadyExistsException, UserAlreadyExistsException { + keycloakGateway.createUser(userMapper.signupRequestDtoToUserCreateDto(data)); + } + + @Override + public void delete(UUID userId) throws UserNotFoundException, KeycloakRemoteException, AccessDeniedException { + keycloakGateway.deleteUser(userId); + } + + @Override + public UserDto findByUsername(String username) throws UserNotFoundException, KeycloakRemoteException, + AccessDeniedException { + return keycloakGateway.findByUsername(username); + } + + @Override + public void updatePassword(UUID id, UserPasswordDto data) throws KeycloakRemoteException, AccessDeniedException { + keycloakGateway.updateUserCredentials(id, data); + } + +} diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java index 40272285475a690f8904c7583f5c8a8f6b88d4e2..3c7758a8f715ad2d76130108a52b1f36d972ae59 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java @@ -8,7 +8,8 @@ import at.tuwien.entities.database.Database; import at.tuwien.entities.database.table.Table; import at.tuwien.entities.user.User; import at.tuwien.exception.AmqpException; -import at.tuwien.exception.BrokerVirtualHostCreationException; +import at.tuwien.exception.BrokerRemoteException; +import at.tuwien.exception.BrokerVirtualHostModificationException; import at.tuwien.exception.BrokerVirtualHostGrantException; import at.tuwien.gateway.BrokerServiceGateway; import at.tuwien.mapper.AmqpMapper; @@ -20,7 +21,6 @@ import at.tuwien.service.TableService; import com.fasterxml.jackson.databind.ObjectMapper; import com.rabbitmq.client.BuiltinExchangeType; import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import lombok.extern.log4j.Log4j2; import org.apache.http.auth.BasicUserPrincipal; @@ -104,12 +104,17 @@ public class RabbitMqServiceImpl implements MessageQueueService { } @Override - public void createUser(String username) throws BrokerVirtualHostCreationException { + public void createUser(String username) throws BrokerRemoteException, BrokerVirtualHostModificationException { brokerServiceGateway.createUser(username); } @Override - public void updatePermissions(User user) throws BrokerVirtualHostGrantException { + public void deleteUser(String username) throws BrokerRemoteException, BrokerVirtualHostModificationException { + brokerServiceGateway.deleteUser(username); + } + + @Override + public void updatePermissions(User user) throws BrokerRemoteException, BrokerVirtualHostGrantException { final GrantVirtualHostPermissionsDto permissions = GrantVirtualHostPermissionsDto.builder() .configure(amqpMapper.databaseListToPermissionString(databaseRepository.findConfigureAccess(user.getId()))) .write(amqpMapper.databaseListToPermissionString(databaseRepository.findWriteAccess(user.getId()))) @@ -145,7 +150,7 @@ public class RabbitMqServiceImpl implements MessageQueueService { } @Override - public void restore() throws AmqpException { + public void restore() throws AmqpException, BrokerRemoteException { final List<Table> tables = tableService.findAll(); final List<ConsumerDto> consumers = brokerServiceGateway.findAllConsumers(); for (Table table : tables) { diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java index d6862071b92172c548cbecf2650bb5b663ade17c..1baf6ff415e5c5b47c81974bd9d42a2588300a48 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java @@ -63,18 +63,17 @@ public class UserServiceImpl implements UserService { } @Override - public User create(SignupRequestDto data) throws UserAlreadyExistsException, AccessDeniedException, + public User create(SignupRequestDto data, UUID id) throws UserAlreadyExistsException, AccessDeniedException, KeycloakRemoteException, UserNotFoundException, UserEmailAlreadyExistsException { /* create at authentication service */ final User entity = User.builder() + .id(id) .username(data.getUsername()) .email(data.getEmail()) .themeDark(false) .mariadbPassword(getMariaDbPassword(data.getPassword())) .build(); - keycloakGateway.createUser(userMapper.signupRequestDtoToUserCreateDto(data)); /* create at metadata database */ - entity.setId(keycloakGateway.findByUsername(data.getUsername()).getId()); final User user = userRepository.save(entity); log.info("Created user with id {} in metadata database", user.getId()); /* save in open search database */ @@ -96,14 +95,10 @@ public class UserServiceImpl implements UserService { } @Override - public void updatePassword(UUID id, UserPasswordDto data) throws KeycloakRemoteException, AccessDeniedException, - UserNotFoundException { + public void updatePassword(UUID id, UserPasswordDto data) throws UserNotFoundException { final User user = find(id); user.setMariadbPassword(getMariaDbPassword(data.getPassword())); userRepository.save(user); - log.debug("updated password in metadata database"); - keycloakGateway.updateUserCredentials(id, data); - log.debug("updated password in keycloak"); log.info("Updated user password with id {}", id); } diff --git a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/utils/ObjectUtil.java b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/utils/ObjectUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..10286fc6bd0635fc340ef538658fcb9d4c267cde --- /dev/null +++ b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/utils/ObjectUtil.java @@ -0,0 +1,15 @@ +package at.tuwien.test.utils; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class ObjectUtil { + + public static String asJsonString(final Object obj) { + try { + return new ObjectMapper().writeValueAsString(obj); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/dbrepo-ui/Dockerfile b/dbrepo-ui/Dockerfile index def7915b216fe1f8e919e397219f6cb00f4c87f5..066d69cb53013b5a7de4d685545cfd0d2538d18b 100644 --- a/dbrepo-ui/Dockerfile +++ b/dbrepo-ui/Dockerfile @@ -41,7 +41,7 @@ EXPOSE 9100 ENV BROKER_USERNAME="fda" ENV BROKER_PASSWORD="fda" -ENV BROKER_LOGIN_URL="/broker/" +ENV BROKER_LOGIN_URL="/admin/broker/" ENV KEYCLOAK_LOGIN_URL="/api/auth/" ENV SHARED_FILESYSTEM="/tmp" ENV LOGO="/logo.png" diff --git a/dbrepo-ui/config.js b/dbrepo-ui/config.js index 1ccca7b563eec4b1a6cf57d9325cb53b30fb229c..29847aaebe790ef5cdcc825951b8496496931eeb 100644 --- a/dbrepo-ui/config.js +++ b/dbrepo-ui/config.js @@ -1,21 +1,21 @@ const config = {} -config.title = process.env.TITLE || null -config.icon = process.env.ICON || null -config.brokerUsername = process.env.BROKER_USERNAME || null -config.brokerPassword = process.env.BROKER_PASSWORD || null -config.brokerLoginUrl = process.env.BROKER_LOGIN_URL || null -config.keycloakLoginUrl = process.env.KEYCLOAK_LOGIN_URL || null -config.sharedFilesystem = process.env.SHARED_FILESYSTEM || null -config.version = process.env.VERSION || null -config.logo = process.env.LOGO || null -config.tokenMax = process.env.TOKEN_MAX || null -config.searchUsername = process.env.SEARCH_USERNAME || null -config.searchPassword = process.env.SEARCH_PASSWORD || null -config.clientId = process.env.DBREPO_CLIENT_ID || null -config.clientSecret = process.env.DBREPO_CLIENT_SECRET || null -config.defaultPublisher = process.env.DEFAULT_PID_PUBLISHER || null -config.doiUrl = process.env.DOI_URL || null -config.uploadPath = process.env.UPLOAD_PATH || null +config.title = process.env.TITLE +config.icon = process.env.ICON +config.brokerUsername = process.env.BROKER_USERNAME +config.brokerPassword = process.env.BROKER_PASSWORD +config.brokerLoginUrl = process.env.BROKER_LOGIN_URL +config.keycloakLoginUrl = process.env.KEYCLOAK_LOGIN_URL +config.sharedFilesystem = process.env.SHARED_FILESYSTEM +config.version = process.env.VERSION +config.logo = process.env.LOGO +config.tokenMax = process.env.TOKEN_MAX +config.searchUsername = process.env.SEARCH_USERNAME +config.searchPassword = process.env.SEARCH_PASSWORD +config.clientId = process.env.DBREPO_CLIENT_ID +config.clientSecret = process.env.DBREPO_CLIENT_SECRET +config.defaultPublisher = process.env.DEFAULT_PID_PUBLISHER +config.doiUrl = process.env.DOI_URL +config.uploadPath = process.env.UPLOAD_PATH module.exports = config diff --git a/dbrepo-ui/package.json b/dbrepo-ui/package.json index 61548c8c365411c283009ea84c387b58ec219161..d9fc5ed0314786876d03ee1fe8f4fb74fcd94f64 100644 --- a/dbrepo-ui/package.json +++ b/dbrepo-ui/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "scripts": { - "dev": "NODE_ENV=development nuxt --port 3001", + "dev": "NODE_ENV=development nuxt --dotenv ../.env --port 3001", "docker": "nuxt > /dev/null", "build": "nuxt build", "start": "nuxt start", diff --git a/dbrepo-ui/pages/login.vue b/dbrepo-ui/pages/login.vue index 4ffb24e8e72363cb563a02d83745a175b48612c0..fe6ce6651a514c5a2c904b6e8e623eeb64ae873d 100644 --- a/dbrepo-ui/pages/login.vue +++ b/dbrepo-ui/pages/login.vue @@ -51,19 +51,16 @@ Login </v-btn> </v-card-actions> + <v-card-subtitle class="text-right"> + <a v-if="rabbitMqUrl" class="mr-1" :href="rabbitMqUrl" target="_blank"> + RabbitMQ Admin <sup><v-icon color="primary" x-small>mdi-open-in-new</v-icon></sup> + </a> + <a v-if="keycloakUrl" class="ml-1" :href="keycloakUrl" target="_blank"> + Keycloak Admin <sup><v-icon color="primary" x-small>mdi-open-in-new</v-icon></sup> + </a> + </v-card-subtitle> </v-card> </v-form> - <v-toolbar v-if="!token" flat> - <v-spacer /> - <v-toolbar-title> - <v-btn v-if="rabbitMqUrl" color="orange" plain :href="rabbitMqUrl"> - <v-icon left>mdi-rabbit</v-icon> RabbitMQ - </v-btn> - <v-btn v-if="keycloakUrl" color="secondary" plain :href="keycloakUrl"> - <v-icon left>mdi-key</v-icon> Keycloak - </v-btn> - </v-toolbar-title> - </v-toolbar> </div> </template> diff --git a/docker-compose.yml b/docker-compose.yml index bd8b3eb5e6173903aa54ea3a4290f3491718769e..4cbbac46746c4affcd1c5664ae558887e3d0d50f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,7 +47,7 @@ services: MARIADB_DATABASE: "${METADATA_DB:-fda}" MARIADB_ROOT_PASSWORD: "${METADATA_PASSWORD:-dbrepo}" healthcheck: - test: mysqladmin ping --user="$METADATA_USERNAME" --password="$METADATA_PASSWORD" --silent + test: mysqladmin ping --user="${METADATA_USERNAME:-root}" --password="${METADATA_PASSWORD:-dbrepo}" --silent interval: 10s timeout: 5s retries: 12 @@ -70,7 +70,7 @@ services: environment: MARIADB_ROOT_PASSWORD: "${USER_DB_PASSWORD:-dbrepo}" healthcheck: - test: mysqladmin ping --user="$USER_DB_USERNAME" --password="$USER_DB_PASSWORD" --silent + test: mysqladmin ping --user="${USER_DB_USERNAME:-root}" --password="${USER_DB_PASSWORD:-dbrepo}" --silent interval: 10s timeout: 5s retries: 12 @@ -93,7 +93,7 @@ services: MARIADB_DATABASE: "${AUTH_DB:-keycloak}" MARIADB_ROOT_PASSWORD: "${AUTH_PASSWORD:-dbrepo}" healthcheck: - test: mysqladmin ping --user="$AUTH_USERNAME" --password="$AUTH_PASSWORD" --silent + test: mysqladmin ping --user="${AUTH_USERNAME:-root}" --password="${AUTH_PASSWORD:-dbrepo}" --silent interval: 10s timeout: 5s retries: 12 @@ -157,13 +157,13 @@ services: BASE_URL: "${BASE_URL:-http://localhost}" GRANT_PRIVILEGES: "${GRANT_PRIVILEGES:-SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE}" BROKER_CONSUMERS: "${BROKER_CONSUMERS:-2}" - BROKER_ENDPOINT: "${BROKER_ENDPOINT:-http://broker-service:15672}" + BROKER_ENDPOINT: "${BROKER_ENDPOINT:-http://broker-service:15672/admin/broker}" BROKER_USERNAME: "${BROKER_USERNAME:-fda}" BROKER_PASSWORD: "${BROKER_PASSWORD:-fda}" DELETED_RECORD: "${DELETED_RECORD:-persistent}" EARLIEST_DATESTAMP: "${EARLIEST_DATESTAMP:-2022-09-17T18:23:00Z}" GRANULARITY: "${GRANULARITY:-YYYY-MM-DDThh:mm:ssZ}" - JWT_ISSUER: "${JWT_ISSUER:-http://localhost/realms/dbrepo}" + JWT_ISSUER: "${JWT_ISSUER:-http://localhost/api/auth/realms/dbrepo}" JWT_PUBKEY: "${JWT_PUBKEY:-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqqnHQ2BWWW9vDNLRCcxD++xZg/16oqMo/c1l+lcFEjjAIJjJp/HqrPYU/U9GvquGE6PbVFtTzW1KcKawOW+FJNOA3CGo8Q1TFEfz43B8rZpKsFbJKvQGVv1Z4HaKPvLUm7iMm8Hv91cLduuoWx6Q3DPe2vg13GKKEZe7UFghF+0T9u8EKzA/XqQ0OiICmsmYPbwvf9N3bCKsB/Y10EYmZRb8IhCoV9mmO5TxgWgiuNeCTtNCv2ePYqL/U0WvyGFW0reasIK8eg3KrAUj8DpyOgPOVBn3lBGf+3KFSYi+0bwZbJZWqbC/Xlk20Go1YfeJPRIt7ImxD27R/lNjgDO/MwIDAQAB}" LOG_LEVEL: "${LOG_LEVEL:-debug}" METADATA_DB: "${METADATA_DB:-fda}" @@ -223,6 +223,11 @@ services: ports: - "5672:5672" - "15672:15672" + healthcheck: + test: wget -qO- localhost:15672/admin/broker | grep "RabbitMQ" || exit 1 + interval: 10s + timeout: 5s + retries: 12 depends_on: dbrepo-authentication-service: condition: service_healthy @@ -273,8 +278,8 @@ services: environment: BROKER_USERNAME: "${BROKER_USERNAME:-fda}" BROKER_PASSWORD: "${BROKER_PASSWORD:-fda}" - BROKER_LOGIN_URL: "${BROKER_LOGIN_URL:-/broker/}" - KEYCLOAK_LOGIN_URL: "${KEYCLOAK_LOGIN_URL:-/api/auth/}" + BROKER_LOGIN_URL: "${BROKER_LOGIN_URL:-/admin/broker/}" + KEYCLOAK_LOGIN_URL: "${KEYCLOAK_LOGIN_URL:-/api/auth/admin/}" SHARED_FILESYSTEM: "${SHARED_FILESYSTEM:-/tmp}" LOGO: "${LOGO:-/logo.png}" SEARCH_USERNAME: "${SEARCH_USERNAME:-admin}"