diff --git a/build.gradle b/build.gradle index 6f6c6dc2..79a36101 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.liquibase:liquibase-core' implementation 'org.springframework.data:spring-data-rest-hal-explorer' + implementation 'com.vladmihalcea:hibernate-types-55:2.17.1' compileOnly 'org.projectlombok:lombok' diff --git a/src/main/java/net/hostsharing/hsadminng/config/PostgreSQL95CustomDialect.java b/src/main/java/net/hostsharing/hsadminng/config/PostgreSQL95CustomDialect.java new file mode 100644 index 00000000..70781854 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/config/PostgreSQL95CustomDialect.java @@ -0,0 +1,13 @@ +package net.hostsharing.hsadminng.config; + +import com.vladmihalcea.hibernate.type.array.StringArrayType; +import org.hibernate.dialect.PostgreSQL95Dialect; + +@SuppressWarnings("unused") // configured in application.yml +public class PostgreSQL95CustomDialect extends PostgreSQL95Dialect { + + public PostgreSQL95CustomDialect() { + this.registerHibernateType(2003, StringArrayType.class.getName()); + } + +} diff --git a/src/main/java/net/hostsharing/hsadminng/context/Context.java b/src/main/java/net/hostsharing/hsadminng/context/Context.java new file mode 100644 index 00000000..82693667 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/context/Context.java @@ -0,0 +1,51 @@ +package net.hostsharing.hsadminng.context; + +import org.springframework.stereotype.Service; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; + +@Service +public class Context { + + @PersistenceContext + private EntityManager em; + + @Transactional(Transactional.TxType.MANDATORY) + public void setCurrentUser(final String userName) { + em.createNativeQuery( + String.format( + "set local hsadminng.currentUser = '%s';", + userName + ) + ).executeUpdate(); + assumeNoSpecialRole(); + } + + public String getCurrentUser() { + return String.valueOf(em.createNativeQuery("select currentUser()").getSingleResult()); + } + + @Transactional(Transactional.TxType.MANDATORY) + public void assumeRoles(final String roles) { + em.createNativeQuery( + String.format( + "set local hsadminng.assumedRoles = '%s';", + roles + ) + ).executeUpdate(); + } + + @Transactional(Transactional.TxType.MANDATORY) + public void assumeNoSpecialRole() { + em.createNativeQuery( + "set local hsadminng.assumedRoles = '';" + ).executeUpdate(); + } + + public String[] getAssumedRoles() { + return (String[]) em.createNativeQuery("select assumedRoles()").getSingleResult(); + } + +} diff --git a/src/main/java/net/hostsharing/hsadminng/TestController.java b/src/main/java/net/hostsharing/hsadminng/controller/TestController.java similarity index 77% rename from src/main/java/net/hostsharing/hsadminng/TestController.java rename to src/main/java/net/hostsharing/hsadminng/controller/TestController.java index fbd7f6ce..483cf5f1 100644 --- a/src/main/java/net/hostsharing/hsadminng/TestController.java +++ b/src/main/java/net/hostsharing/hsadminng/controller/TestController.java @@ -1,5 +1,7 @@ -package net.hostsharing.hsadminng; +package net.hostsharing.hsadminng.controller; +import net.hostsharing.hsadminng.context.Context; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -15,6 +17,9 @@ public class TestController { @PersistenceContext private EntityManager em; + @Autowired + private Context context; + @ResponseBody @RequestMapping(value = "/api/ping", method = RequestMethod.GET) public String ping() { @@ -25,8 +30,8 @@ public class TestController { @ResponseBody @RequestMapping(value = "/api/currentUser", method = RequestMethod.GET) public String currentUser() { - em.createNativeQuery("SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net';").executeUpdate(); - em.createNativeQuery("SET LOCAL hsadminng.assumedRoles = '';").executeUpdate(); + context.setCurrentUser("mike@hostsharing.net"); + final var query = em.createNativeQuery("select currentUser()"); return query.getSingleResult() + "\n"; } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5cd25fa5..7bcbda1b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,14 +1,19 @@ spring: - datasource: - driver-class-name: org.postgresql.Driver - password: password - url: jdbc:postgresql://localhost:5432/postgres - username: postgres + datasource: + driver-class-name: org.postgresql.Driver + password: password + url: jdbc:postgresql://localhost:5432/postgres + username: postgres - sql: - init: - mode: never + sql: + init: + mode: never + + jpa: + properties: + hibernate: + dialect: net.hostsharing.hsadminng.config.PostgreSQL95CustomDialect liquibase: contexts: dev diff --git a/src/test/java/net/hostsharing/hsadminng/RbacIntegrationTests.java b/src/test/java/net/hostsharing/hsadminng/RbacIntegrationTests.java deleted file mode 100644 index a6f39d89..00000000 --- a/src/test/java/net/hostsharing/hsadminng/RbacIntegrationTests.java +++ /dev/null @@ -1,26 +0,0 @@ -package net.hostsharing.hsadminng; - -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; - -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.transaction.Transactional; - -@DataJpaTest -class RbacIntegrationTests { - - @PersistenceContext - private EntityManager em; - - @Test - @Transactional - void currentUser() { - em.createNativeQuery("SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net';").executeUpdate(); - em.createNativeQuery("SET LOCAL hsadminng.assumedRoles = '';").executeUpdate(); - - final var result = em.createNativeQuery("select currentUser()").getSingleResult(); - Assertions.assertThat(result).isEqualTo("mike@hostsharing.net"); - } -} diff --git a/src/test/java/net/hostsharing/hsadminng/context/ContextIntegrationTests.java b/src/test/java/net/hostsharing/hsadminng/context/ContextIntegrationTests.java new file mode 100644 index 00000000..25b90e4e --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/context/ContextIntegrationTests.java @@ -0,0 +1,43 @@ +package net.hostsharing.hsadminng.context; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.ComponentScan; + +import javax.transaction.Transactional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@ComponentScan(basePackageClasses = Context.class) +class ContextIntegrationTests { + + @Autowired + private Context context; + + @Test + @Transactional + void setCurrentUser() { + context.setCurrentUser("mike@hostsharing.net"); + + final var currentUser = context.getCurrentUser(); + assertThat(currentUser).isEqualTo("mike@hostsharing.net"); + + final var assumedRoles = context.getAssumedRoles(); + assertThat(assumedRoles).isEmpty(); + } + + @Test + @Transactional + void assumeRoles() { + context.setCurrentUser("mike@hostsharing.net"); + context.assumeRoles("customer#aaa.owner;customer#aab.owner"); + + final var currentUser = context.getCurrentUser(); + assertThat(currentUser).isEqualTo("mike@hostsharing.net"); + + final var assumedRoles = context.getAssumedRoles(); + assertThat(assumedRoles).containsExactlyInAnyOrder("customer#aaa.owner", "customer#aab.owner"); + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index addd94d1..0629162b 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -12,7 +12,7 @@ spring: properties: hibernate: default_schema: public - dialect: org.hibernate.dialect.PostgreSQLDialect + dialect: net.hostsharing.hsadminng.config.PostgreSQL95CustomDialect hibernate: ddl-auto: none show-sql: true @@ -29,9 +29,5 @@ logging: level: liquibase: INFO - # spring.datasource.driver-class-name=org.postgresql.Driver -# spring.datasource.url=${DB_URL} -# spring.datasource.username=${DB_USERNAME} -# spring.datasource.password=${DB_PASSWORD} - -# spring.jpa.properties.hibernate.default-schema=public +liquibase: + contexts: dev,tc