From d727ca77a89202af0272ce363a0daf625f238c1f Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 9 Aug 2022 17:13:24 +0200 Subject: [PATCH 1/3] fixed error in userinfo on first login (without user in db) --- .../api/rest/controller/UserController.java | 2 ++ .../ResourceSecurityExpressionRoot.java | 33 +++++++++++++++---- .../api/service/UserAuthService.java | 6 ++++ .../api/service/UserServiceImpl.java | 7 ++-- 4 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/github/patternatlas/api/rest/controller/UserController.java b/src/main/java/io/github/patternatlas/api/rest/controller/UserController.java index 98f24ab..77ed482 100644 --- a/src/main/java/io/github/patternatlas/api/rest/controller/UserController.java +++ b/src/main/java/io/github/patternatlas/api/rest/controller/UserController.java @@ -28,6 +28,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PostFilter; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -93,6 +94,7 @@ ResponseEntity> getUserById(@PathVariable UUID userId) { */ @RequestMapping(method = RequestMethod.GET, value = "/userinfo") @ResponseBody + @PreAuthorize(value = "isLoggedIn()") // must be checked here since user needs to be logged in to get info public Map user(Principal principal) { if (principal != null) { UUID id = UUID.fromString(principal.getName()); diff --git a/src/main/java/io/github/patternatlas/api/security/ResourceSecurityExpressionRoot.java b/src/main/java/io/github/patternatlas/api/security/ResourceSecurityExpressionRoot.java index e22c974..82934ea 100644 --- a/src/main/java/io/github/patternatlas/api/security/ResourceSecurityExpressionRoot.java +++ b/src/main/java/io/github/patternatlas/api/security/ResourceSecurityExpressionRoot.java @@ -17,6 +17,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.security.access.expression.SecurityExpressionRoot; import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations; import org.springframework.security.core.Authentication; @@ -52,6 +53,16 @@ public ResourceSecurityExpressionRoot(Authentication authentication, this.userAuthService = userAuthService; } + /** + * Checks if the user is logged in and makes sure the token-Information + * is stored in patternatlas db. + * @return true if the user is present in db and logged in, false otherwise + */ + public boolean isLoggedIn() { + Optional userId = loggedInUUID(); + return userId.isPresent(); + } + /** * Checks global permission for user. * Will only check for the exact permission (e.g. ISSUE_CREATE) @@ -83,13 +94,21 @@ private void createUser(UUID userId, Authentication authentication) { UserModelRequest req = new UserModelRequest(); req.setId(userId); req.setName(preferredUsername); - - if(this.userAuthService.hasUsers()) { - // Create standard member user - this.userAuthService.createInitialMember(req); - } else { - // There are no other users registered => create admin user as first user - this.userAuthService.createInitialAdmin(req); + try { + if(this.userAuthService.hasUsers()) { + // Create standard member user + this.userAuthService.createInitialMember(req); + } else { + // There are no other users registered => create admin user as first user + this.userAuthService.createInitialAdmin(req); + } + } catch (DataIntegrityViolationException e) { + /* + Is thrown if two simultaneous calls are the first calls a user makes to the API. + In this case the second create user call will fail since both calls are made with + the same ID. Since the call only fails if the user was properly created + by the first call, this exception can be ignored. + */ } } catch (JsonProcessingException e) { throw new UserNotFoundException("Cannot infer preferred username from Token"); diff --git a/src/main/java/io/github/patternatlas/api/service/UserAuthService.java b/src/main/java/io/github/patternatlas/api/service/UserAuthService.java index 4d1be48..61cd4ab 100644 --- a/src/main/java/io/github/patternatlas/api/service/UserAuthService.java +++ b/src/main/java/io/github/patternatlas/api/service/UserAuthService.java @@ -5,14 +5,20 @@ import java.util.UUID; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; + /** * Service for manipulating users based on incomping JW-Tokens. * Not to be used for REST-Access. * Used to query user information to handle authentication. */ public interface UserAuthService { + + @Transactional UserEntity createInitialMember(UserModelRequest userModelRequest); + @Transactional UserEntity createInitialAdmin(UserModelRequest userModelRequest); boolean userExists(UUID userId); diff --git a/src/main/java/io/github/patternatlas/api/service/UserServiceImpl.java b/src/main/java/io/github/patternatlas/api/service/UserServiceImpl.java index d2fe5b0..c32d065 100644 --- a/src/main/java/io/github/patternatlas/api/service/UserServiceImpl.java +++ b/src/main/java/io/github/patternatlas/api/service/UserServiceImpl.java @@ -11,8 +11,10 @@ import io.github.patternatlas.api.rest.model.user.RoleModelRequest; import io.github.patternatlas.api.rest.model.user.UserModelRequest; +import javax.validation.ConstraintViolationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.data.rest.webmvc.ResourceNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -91,6 +93,7 @@ public UserEntity getUserById(UUID userId) { } private UserEntity createInitialUser(UserModelRequest userModelRequest, String requestedRole) { + logger.info("Automatically creating user " + userModelRequest.getName() + " (role: " + requestedRole + ")"); UserEntity user = new UserEntity(userModelRequest, ""); user.setEmail(""); user.getRoles().add(this.roleRepository.findByName(requestedRole)); @@ -99,14 +102,14 @@ private UserEntity createInitialUser(UserModelRequest userModelRequest, String r } @Override + @Transactional public UserEntity createInitialMember(UserModelRequest userModelRequest) { return createInitialUser(userModelRequest, RoleConstant.MEMBER); } @Override + @Transactional public UserEntity createInitialAdmin(UserModelRequest userModelRequest) { - logger.info("Automatically creating admin user " + userModelRequest.getName()); - return createInitialUser(userModelRequest, RoleConstant.ADMIN); } From 36d7963ed0eba0997ca0b0ec4fab37366e15ceca Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 9 Aug 2022 17:15:43 +0200 Subject: [PATCH 2/3] disabled hibernate-sqls in logs --- .docker/application.properties.tpl | 4 ++-- src/main/resources/application.properties | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.docker/application.properties.tpl b/.docker/application.properties.tpl index a272ea4..89f581b 100644 --- a/.docker/application.properties.tpl +++ b/.docker/application.properties.tpl @@ -5,13 +5,13 @@ spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://{{.Env.JDBC_DATABASE_URL}}:{{.Env.JDBC_DATABASE_PORT}}/{{.Env.JDBC_DATABASE_NAME}} spring.datasource.username={{.Env.JDBC_DATABASE_USERNAME}} spring.datasource.password={{.Env.JDBC_DATABASE_PASSWORD}} -spring.jpa.show-sql=true +spring.jpa.show-sql=false spring.jpa.generate-ddl=true spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect spring.jpa.hibernate.ddl-auto=update spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true -spring.jpa.properties.hibernate.show_sql=true +spring.jpa.properties.hibernate.show_sql=false spring.jpa.properties.hibernate.format_sql=true spring.jpa.properties.hibernate.use_sql_comments=true spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 0435388..855a097 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -24,3 +24,6 @@ io.github.patternatlas.api.latexrenderer.port=5030 server.servlet.contextPath=/patternatlas # liquibase file spring.liquibase.change-log=patternatlas.xml +# DB debug logs +spring.jpa.show-sql=false +spring.jpa.properties.hibernate.show_sql=false \ No newline at end of file From b30582de209ba5257331afb38d1d70fddf7f616c Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 9 Aug 2022 17:31:32 +0200 Subject: [PATCH 3/3] disabled verbose loggin in docker application config --- .docker/application.properties.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/.docker/application.properties.tpl b/.docker/application.properties.tpl index 89f581b..4753f4a 100644 --- a/.docker/application.properties.tpl +++ b/.docker/application.properties.tpl @@ -5,6 +5,7 @@ spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://{{.Env.JDBC_DATABASE_URL}}:{{.Env.JDBC_DATABASE_PORT}}/{{.Env.JDBC_DATABASE_NAME}} spring.datasource.username={{.Env.JDBC_DATABASE_USERNAME}} spring.datasource.password={{.Env.JDBC_DATABASE_PASSWORD}} +logging.level.io.github.patternatlas.api=info spring.jpa.show-sql=false spring.jpa.generate-ddl=true spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect