diff --git a/build.gradle.kts b/build.gradle.kts index 871eab7..91617c4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,14 +1,13 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { + kotlin("jvm") apply false // Apply the Kotlin JVM plugin to add support for Kotlin on the JVM. - id("org.jetbrains.kotlin.jvm").version("1.4.0").apply(false) - idea - id("org.jetbrains.gradle.plugin.idea-ext").version("0.9") + id("org.jetbrains.gradle.plugin.idea-ext") version "0.9" } subprojects { - version = "0.1.1" + version = "0.2.0" repositories { // Use jcenter for resolving your dependencies. @@ -26,5 +25,5 @@ subprojects { } tasks.wrapper { - gradleVersion = "6.6" + gradleVersion = "6.6.1" } diff --git a/docker-compose.yml b/docker-compose.yml index 1b51bd0..8f3ade6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: # Management Portal # #---------------------------------------------------------------------------# managementportal: - image: radarbase/management-portal:0.5.6 + image: radarbase/management-portal:0.6.0 depends_on: - mp-postgresql ports: @@ -41,19 +41,19 @@ services: #---------------------------------------------------------------------------# # RADAR Upload Source connector # #---------------------------------------------------------------------------# -# app-config: -# build: -# context: . -# dockerfile: radar-app-config/Dockerfile -# image: radarbase/radar-app-config -# restart: on-failure -# volumes: -# - ./etc/appconfig.yml:/etc/radar-app-config/appconfig.yml -# depends_on: -# - managementportal -# labels: -# - "traefik.http.routers.appconfig.rule=PathPrefix(`/appconfig/api`)" -# - "traefik.http.services.appconfig.loadbalancer.server.port=8090" + app-config: + build: + context: . + dockerfile: radar-app-config/Dockerfile + image: radarbase/radar-app-config + restart: on-failure + volumes: + - ./etc/appconfig.yml:/etc/radar-app-config/appconfig.yml + depends_on: + - managementportal + labels: + - "traefik.http.routers.appconfig.rule=PathPrefix(`/appconfig/api`)" + - "traefik.http.services.appconfig.loadbalancer.server.port=8090" traefik: image: traefik:2.1 @@ -63,14 +63,14 @@ services: ports: - "8080:80" - smtp: - image: namshi/smtp - container_name: smtp - restart: always - ports: - - "25:25" - env_file: - - etc/smtp.env +# smtp: +# image: namshi/smtp +# container_name: smtp +# restart: always +# ports: +# - "25:25" +# env_file: +# - etc/smtp.env # environment: # - GMAIL_USER= # - GMAIL_PASSWORD= diff --git a/etc/appconfig.yml b/etc/appconfig.yml index 256b5ba..822d252 100644 --- a/etc/appconfig.yml +++ b/etc/appconfig.yml @@ -1,17 +1,19 @@ -baseUri: http://0.0.0.0:8095/appconfig/api +baseUri: http://0.0.0.0:8090/appconfig/api isJmxEnabled: false inject: enhancerFactory: org.radarbase.appconfig.inject.ManagementPortalEnhancerFactory -authentication: - url: http://localhost:8080/managementportal/ - clientId: appconfig - clientSecret: test - resourceName: res_appconfig +auth: + managementPortal: + url: http://managementportal:8080/managementportal/ + clientId: appconfig + clientSecret: test + jwtResourceName: res_appconfig -jdbc: +database: driver: "org.h2.Driver" url: "jdbc:h2:./data/h2" user: password: + dialect: org.hibernate.dialect.H2Dialect diff --git a/etc/mp-config/oauth_client_details.csv b/etc/mp-config/oauth_client_details.csv index b93b6cc..e5e5eee 100644 --- a/etc/mp-config/oauth_client_details.csv +++ b/etc/mp-config/oauth_client_details.csv @@ -4,5 +4,5 @@ aRMT;res_ManagementPortal,res_gateway;secret;MEASUREMENT.CREATE,SUBJECT.UPDATE,S THINC-IT;res_ManagementPortal,res_gateway;secret;MEASUREMENT.CREATE,SUBJECT.UPDATE,SUBJECT.READ,PROJECT.READ,SOURCETYPE.READ,SOURCE.READ,SOURCETYPE.READ,SOURCEDATA.READ,USER.READ,ROLE.READ;refresh_token,authorization_code;;;43200;7948800;{"dynamic_registration": true}; radar_upload_connect;res_ManagementPortal,res_upload;upload_secret;SUBJECT.READ,SUBJECT.UPDATE,PROJECT.READ,SOURCE.READ,SOURCETYPE.READ,MEASUREMENT.CREATE;client_credentials;;;43200;259200;{}; radar_upload_frontend;res_ManagementPortal,res_upload;;SOURCETYPE.READ,MEASUREMENT.CREATE,PROJECT.READ,SUBJECT.READ;authorization_code;http://localhost:8080/upload/login;3600;78000;; -appconfig;res_ManagementPortal;test;SOURCETYPE.READ,MEASUREMENT.CREATE,PROJECT.READ,SUBJECT.READ,OAUTHCLIENTS.READ;client_credentials;;3600;78000;; +appconfig;res_ManagementPortal,res_appconfig;test;SOURCETYPE.READ,MEASUREMENT.CREATE,PROJECT.READ,SUBJECT.READ,OAUTHCLIENTS.READ;client_credentials;;3600;78000;; appconfig_frontend;res_appconfig;;SOURCETYPE.READ,MEASUREMENT.CREATE,PROJECT.READ,PROJECT.CREATE,PROJECT.UPDATE,SUBJECT.READ,SUBJECT.UPDATE,OAUTHCLIENTS.READ;authorization_code,refresh_token;http://localhost:4200/login;3600;78000;; diff --git a/gradle.properties b/gradle.properties index ca1825a..e1b25c0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,7 @@ -org.jetbrains.idea.gradle.syncTasks=:radar-expression-lang:generateGrammarSource \ No newline at end of file +org.jetbrains.idea.gradle.syncTasks=:radar-expression-lang:generateGrammarSource +kotlinVersion=1.4.10 +jacksonVersion=2.11.2 +antlrVersion=4.8-1 +radarJerseyVersion=0.3.2 +h2Version=1.4.200 + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6c9a224..12d38de 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/radar-app-config/Dockerfile b/radar-app-config/Dockerfile index 177567b..56300d0 100644 --- a/radar-app-config/Dockerfile +++ b/radar-app-config/Dockerfile @@ -1,9 +1,22 @@ -FROM gradle:6.6-jdk11 as builder +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM gradle:6.6.1-jdk11 as builder RUN mkdir /code WORKDIR /code +ENV GRADLE_USER_HOME=/code/.gradlecache -COPY ./build.gradle.kts ./settings.gradle.kts /code/ +COPY build.gradle.kts settings.gradle.kts gradle.properties /code/ COPY radar-app-config/build.gradle.kts /code/radar-app-config/ COPY radar-expression-lang/build.gradle.kts /code/radar-expression-lang/ RUN gradle :radar-app-config:downloadDependencies @@ -11,7 +24,7 @@ RUN gradle :radar-app-config:downloadDependencies COPY radar-expression-lang/src /code/radar-expression-lang/src COPY radar-app-config/src /code/radar-app-config/src -RUN gradle -Dkotlin.compiler.execution.strategy="in-process" -Dorg.gradle.parallel=false -Pkotlin.incremental=false :radar-app-config:distTar \ +RUN gradle :radar-app-config:distTar \ && cd radar-app-config/build/distributions \ && tar xf *.tar \ && rm *.tar radar-app-config*/lib/radar-app-config*.jar diff --git a/radar-app-config/build.gradle.kts b/radar-app-config/build.gradle.kts index ad2d570..b05401c 100644 --- a/radar-app-config/build.gradle.kts +++ b/radar-app-config/build.gradle.kts @@ -7,19 +7,6 @@ application { mainClassName = "org.radarbase.appconfig.MainKt" } -project.extra.apply { - set("okhttpVersion", "4.8.1") - set("radarAuthVersion", "0.2.4") - set("radarSchemasVersion", "0.5.13") - set("jacksonVersion", "2.11.2") - set("slf4jVersion", "1.7.30") - set("logbackVersion", "1.2.3") - set("hibernateVersion", "5.4.20.Final") - set("h2Version", "1.4.200") - set("postgresqlVersion", "42.2.16") - set("liquibaseVersion", "3.10.2") -} - repositories { jcenter() maven(url = "https://dl.bintray.com/radar-base/org.radarbase") @@ -31,23 +18,13 @@ dependencies { implementation(kotlin("stdlib-jdk8")) implementation(kotlin("reflect")) - implementation("com.squareup.okhttp3:okhttp:${project.extra["okhttpVersion"]}") implementation(project(":radar-expression-lang")) - implementation("org.radarbase:radar-jersey:${project.extra["radarAuthVersion"]}") - - implementation("com.fasterxml.jackson.core:jackson-databind:${project.extra["jacksonVersion"]}") - implementation("com.fasterxml.jackson.module:jackson-module-kotlin:${project.extra["jacksonVersion"]}") - - implementation("org.slf4j:slf4j-api:${project.extra["slf4jVersion"]}") - - implementation("org.hibernate:hibernate-core:${project.extra["hibernateVersion"]}") - implementation("org.liquibase:liquibase-core:${project.extra["liquibaseVersion"]}") + val radarJerseyVersion: String by project + implementation("org.radarbase:radar-jersey:$radarJerseyVersion") + implementation("org.radarbase:radar-jersey-hibernate:$radarJerseyVersion") - runtimeOnly("com.h2database:h2:${project.extra["h2Version"]}") - runtimeOnly("org.postgresql:postgresql:${project.extra["postgresqlVersion"]}") - runtimeOnly("ch.qos.logback:logback-classic:${project.extra["logbackVersion"]}") - runtimeOnly("org.hibernate:hibernate-c3p0:${project.extra["hibernateVersion"]}") + runtimeOnly("com.h2database:h2:${project.property("h2Version")}") testImplementation("org.junit.jupiter:junit-jupiter:5.6.2") testImplementation("org.junit.jupiter:junit-jupiter-params:5.6.2") diff --git a/radar-app-config/root.crt b/radar-app-config/root.crt new file mode 100644 index 0000000..2bd16eb --- /dev/null +++ b/radar-app-config/root.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/config/ApplicationConfig.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/config/ApplicationConfig.kt index 54b3a31..94746a1 100644 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/config/ApplicationConfig.kt +++ b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/config/ApplicationConfig.kt @@ -1,11 +1,13 @@ package org.radarbase.appconfig.config +import org.radarbase.jersey.auth.AuthConfig +import org.radarbase.jersey.hibernate.config.DatabaseConfig import java.net.URI data class ApplicationConfig( val baseUri: URI = URI.create("http://0.0.0.0:8090/appconfig/"), val isJmxEnabled: Boolean = true, val isCorsEnabled: Boolean = false, - val authentication: AuthenticationConfig = AuthenticationConfig(), + val auth: AuthConfig = AuthConfig(jwtResourceName = "res_appconfig"), val inject: InjectConfig = InjectConfig(), - val jdbc: JdbcConfig? = null) + val database: DatabaseConfig? = null) diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/config/AuthenticationConfig.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/config/AuthenticationConfig.kt deleted file mode 100644 index 204f58c..0000000 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/config/AuthenticationConfig.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.radarbase.appconfig.config - -import java.net.URL - -data class AuthenticationConfig( - val url: URL = URL("http://managementportal:8080/managementportal/"), - val resourceName: String = "res_appconfig", - val clientId: String = "appconfig", - val clientSecret: String? = null) diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/config/JdbcConfig.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/config/JdbcConfig.kt deleted file mode 100644 index 790be7d..0000000 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/config/JdbcConfig.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.radarbase.appconfig.config - -data class JdbcConfig( - val driver: String, - val url: String, - val user: String? = null, - val password: String? = null, - val properties: Map = emptyMap()) diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/domain/Project.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/domain/Project.kt index 18c2da9..152b7a1 100644 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/domain/Project.kt +++ b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/domain/Project.kt @@ -4,19 +4,32 @@ import com.fasterxml.jackson.annotation.JsonProperty import nl.thehyve.lang.expression.Expression import nl.thehyve.lang.expression.ResolvedVariable import nl.thehyve.lang.expression.Scope +import org.radarbase.jersey.service.managementportal.MPOAuthClient +import org.radarbase.jersey.service.managementportal.MPProject +import org.radarbase.jersey.service.managementportal.MPUser import java.util.stream.Collectors import java.util.stream.Stream data class ProjectList(val projects: Collection) -data class Project(val id: Long, @JsonProperty("projectName") val name: String, @JsonProperty("humanReadableProjectName") val humanReadableName: String? = null, val location: String? = null, val organization: String? = null, val description: String? = null) +data class Project(@JsonProperty("projectName") val name: String, @JsonProperty("humanReadableProjectName") val humanReadableName: String? = null, val location: String? = null, val organization: String? = null, val description: String? = null) +fun MPProject.toProject(): Project = Project( + name = id, + humanReadableName = name, + location = location, + organization = organization, + description = description +) data class UserList(val users: Collection) data class User(val id: String, val externalUserId: String? = null, val hasConfig: Boolean? = null) -data class MPUser(val login: String, val externalUserId: String? = null) + +fun MPUser.toUser(): User = User(id = id, externalUserId = externalId) data class OAuthClient(@JsonProperty("clientId") val id: String) +fun MPOAuthClient.toOAuthClient() = OAuthClient(id = id) + data class OAuthClientList(val clients: List) data class ConditionList(val conditions: List) @@ -41,4 +54,4 @@ data class ClientConfig(val clientId: String?, val scope: String?, val config: L } } -data class SingleVariable(val name: String, val value: String?, val scope: String? = null) \ No newline at end of file +data class SingleVariable(val name: String, val value: String?, val scope: String? = null) diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/AppConfigResourceEnhancer.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/AppConfigResourceEnhancer.kt index be767b8..e2e36d3 100644 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/AppConfigResourceEnhancer.kt +++ b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/AppConfigResourceEnhancer.kt @@ -1,26 +1,13 @@ package org.radarbase.appconfig.inject -import com.fasterxml.jackson.annotation.JsonInclude -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.module.SimpleModule -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import nl.thehyve.lang.expression.* -import nl.thehyve.lang.expression.Function import org.glassfish.jersey.internal.inject.AbstractBinder -import org.glassfish.jersey.internal.inject.PerThread -import org.glassfish.jersey.server.ResourceConfig import org.radarbase.appconfig.config.ApplicationConfig -import org.radarbase.appconfig.managementportal.MPClient import org.radarbase.appconfig.service.* -import org.radarbase.jersey.auth.ProjectService import org.radarbase.jersey.config.ConfigLoader import org.radarbase.jersey.config.JerseyResourceEnhancer import javax.inject.Singleton -import javax.ws.rs.ext.ContextResolver class AppConfigResourceEnhancer(private val config: ApplicationConfig): JerseyResourceEnhancer { - private val mapper = createMapper() - override val classes: Array> = if (config.isCorsEnabled) arrayOf( ConfigLoader.Filters.cors, ConfigLoader.Filters.logResponse) @@ -29,10 +16,6 @@ class AppConfigResourceEnhancer(private val config: ApplicationConfig): JerseyRe override val packages: Array = arrayOf("org.radarbase.appconfig.resource") - override fun ResourceConfig.enhance() { - register(ContextResolver { mapper }) - } - override fun AbstractBinder.enhance() { // Bind instances. These cannot use any injects themselves bind(config) @@ -50,10 +33,6 @@ class AppConfigResourceEnhancer(private val config: ApplicationConfig): JerseyRe .to(ConfigProjectService::class.java) .`in`(Singleton::class.java) - bind(ProjectAuthService::class.java) - .to(ProjectService::class.java) - .`in`(Singleton::class.java) - bind(ClientService::class.java) .to(ClientService::class.java) .`in`(Singleton::class.java) @@ -65,30 +44,5 @@ class AppConfigResourceEnhancer(private val config: ApplicationConfig): JerseyRe bind(UserService::class.java) .to(UserService::class.java) .`in`(Singleton::class.java) - - bind(MPClient::class.java) - .to(MPClient::class.java) - .`in`(PerThread::class.java) - - bind(mapper) - .to(ObjectMapper::class.java) - } - - companion object { - fun createMapper(): ObjectMapper { - val allowedFunctions = listOf( - SumFunction(), - ListVariablesFunction(), - CountFunction() - ) - val deserializer = ExpressionDeserializer(ExpressionParser(allowedFunctions)) - - return jacksonObjectMapper() - .registerModule(SimpleModule().apply { - addDeserializer(Expression::class.java, deserializer) - }) - .setSerializationInclusion(JsonInclude.Include.NON_NULL) - } - } -} \ No newline at end of file +} diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/DoaEntityManagerFactory.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/DoaEntityManagerFactory.kt deleted file mode 100644 index b358a4f..0000000 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/DoaEntityManagerFactory.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * * Copyright 2019 The Hyve - * * - * * Licensed under the Apache License, Version 2.0 (the "License"); - * * you may not use this file except in compliance with the License. - * * You may obtain a copy of the License at - * * - * * http://www.apache.org/licenses/LICENSE-2.0 - * * - * * Unless required by applicable law or agreed to in writing, software - * * distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * See the License for the specific language governing permissions and - * * limitations under the License. - * * - * - */ - -package org.radarbase.appconfig.inject - -import org.glassfish.jersey.internal.inject.DisposableSupplier -import org.slf4j.LoggerFactory -import javax.persistence.EntityManager -import javax.persistence.EntityManagerFactory -import javax.ws.rs.core.Context - -class DoaEntityManagerFactory( - @Context private val emf: EntityManagerFactory -) : DisposableSupplier { - - override fun get(): EntityManager { - logger.debug("Creating EntityManager...") - return emf.createEntityManager() - } - - override fun dispose(instance: EntityManager?) { - instance?.let { - logger.debug("Disposing EntityManager") - it.close() - } - } - - companion object { - private val logger = LoggerFactory.getLogger(DoaEntityManagerFactory::class.java) - } -} diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/DoaEntityManagerFactoryFactory.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/DoaEntityManagerFactoryFactory.kt deleted file mode 100644 index 5950aa7..0000000 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/DoaEntityManagerFactoryFactory.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * - * * Copyright 2019 The Hyve - * * - * * Licensed under the Apache License, Version 2.0 (the "License"); - * * you may not use this file except in compliance with the License. - * * You may obtain a copy of the License at - * * - * * http://www.apache.org/licenses/LICENSE-2.0 - * * - * * Unless required by applicable law or agreed to in writing, software - * * distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * See the License for the specific language governing permissions and - * * limitations under the License. - * * - * - */ - -package org.radarbase.appconfig.inject - -import liquibase.Liquibase -import liquibase.database.DatabaseFactory -import liquibase.database.jvm.JdbcConnection -import liquibase.exception.LiquibaseException -import liquibase.resource.ClassLoaderResourceAccessor -import org.glassfish.jersey.internal.inject.DisposableSupplier -import org.hibernate.internal.SessionImpl -import org.radarbase.appconfig.config.ApplicationConfig -import org.slf4j.LoggerFactory -import javax.persistence.EntityManagerFactory -import javax.persistence.Persistence -import javax.ws.rs.core.Context - -class DoaEntityManagerFactoryFactory( - @Context config: ApplicationConfig -) : DisposableSupplier { - @Suppress("UNCHECKED_CAST") - private val configMap = ( - mapOf( - "javax.persistence.jdbc.driver" to config.jdbc?.driver, - "javax.persistence.jdbc.url" to config.jdbc?.url, - "javax.persistence.jdbc.user" to config.jdbc?.user, - "javax.persistence.jdbc.password" to config.jdbc?.password) - + (config.jdbc?.properties ?: emptyMap())) - .filterValues { it != null } as Map - - override fun get(): EntityManagerFactory { - logger.info("Initializing EntityManagerFactory with config: $configMap") - return Persistence.createEntityManagerFactory("org.radarbase.appconfig.domain", configMap) - .also { initializeDatabase(it) } - } - - private fun initializeDatabase(emf: EntityManagerFactory) { - logger.info("Initializing Liquibase") - val connection = emf.createEntityManager().unwrap(SessionImpl::class.java).connection() - try { - val database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(JdbcConnection(connection)) - val liquibase = Liquibase("dbChangelog.xml", ClassLoaderResourceAccessor(), database) - liquibase.update("test") - } catch (e: LiquibaseException) { - logger.error("Failed to initialize database", e) - } - } - - override fun dispose(instance: EntityManagerFactory?) { - logger.info("Disposing EntityManagerFactory") - instance?.close() - } - - companion object { - private val logger = LoggerFactory.getLogger(DoaEntityManagerFactoryFactory::class.java) - } -} diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/HibernatePersistenceResourceEnhancer.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/HibernatePersistenceResourceEnhancer.kt index d1d522d..cfcd43f 100644 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/HibernatePersistenceResourceEnhancer.kt +++ b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/HibernatePersistenceResourceEnhancer.kt @@ -2,12 +2,10 @@ package org.radarbase.appconfig.inject import nl.thehyve.lang.expression.VariableResolver import org.glassfish.jersey.internal.inject.AbstractBinder -import org.glassfish.jersey.process.internal.RequestScoped import org.radarbase.appconfig.persistence.HibernateVariableResolver import org.radarbase.jersey.config.JerseyResourceEnhancer import javax.inject.Singleton import javax.persistence.EntityManager -import javax.persistence.EntityManagerFactory import javax.ws.rs.core.Context class HibernatePersistenceResourceEnhancer : JerseyResourceEnhancer { @@ -15,20 +13,11 @@ class HibernatePersistenceResourceEnhancer : JerseyResourceEnhancer { bind(HibernateClientVariableResolver::class.java) .to(ClientVariableResolver::class.java) .`in`(Singleton::class.java) - - // Bind factories. - bindFactory(DoaEntityManagerFactoryFactory::class.java) - .to(EntityManagerFactory::class.java) - .`in`(Singleton::class.java) - - bindFactory(DoaEntityManagerFactory::class.java) - .to(EntityManager::class.java) - .`in`(RequestScoped::class.java) } class HibernateClientVariableResolver( @Context private val em: javax.inject.Provider ): ClientVariableResolver { - override fun get(clientId: String): VariableResolver = HibernateVariableResolver(em.get(), clientId) + override fun get(clientId: String): VariableResolver = HibernateVariableResolver(em, clientId) } -} \ No newline at end of file +} diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/ManagementPortalEnhancerFactory.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/ManagementPortalEnhancerFactory.kt index 42e7270..ed464fa 100644 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/ManagementPortalEnhancerFactory.kt +++ b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/inject/ManagementPortalEnhancerFactory.kt @@ -1,28 +1,46 @@ package org.radarbase.appconfig.inject +import com.fasterxml.jackson.databind.module.SimpleModule +import nl.thehyve.lang.expression.* +import nl.thehyve.lang.expression.Function import org.radarbase.appconfig.config.ApplicationConfig -import org.radarbase.jersey.auth.AuthConfig +import org.radarbase.appconfig.persistence.entity.ConfigEntity import org.radarbase.jersey.config.ConfigLoader import org.radarbase.jersey.config.EnhancerFactory import org.radarbase.jersey.config.JerseyResourceEnhancer +import org.radarbase.jersey.hibernate.config.HibernateResourceEnhancer /** This binder needs to register all non-Jersey classes, otherwise initialization fails. */ class ManagementPortalEnhancerFactory(private val config: ApplicationConfig) : EnhancerFactory { override fun createEnhancers(): List { - val resolverEnhancer = if (config.jdbc != null) { - HibernatePersistenceResourceEnhancer() + val resolverEnhancer = if (config.database != null) { + listOf( + HibernateResourceEnhancer(config.database.copy( + managedClasses = listOf(ConfigEntity::class.qualifiedName!!) + )), + HibernatePersistenceResourceEnhancer()) } else { - InMemoryResourceEnhancer() + listOf(InMemoryResourceEnhancer()) } + + val radarEnhancer = ConfigLoader.Enhancers.radar(config.auth) + radarEnhancer.mapper.registerModule(SimpleModule().apply { + val allowedFunctions = listOf( + SumFunction(), + ListVariablesFunction(), + CountFunction() + ) + val deserializer = ExpressionDeserializer(ExpressionParser(allowedFunctions)) + + addDeserializer(Expression::class.java, deserializer) + }) + return listOf( AppConfigResourceEnhancer(config), - resolverEnhancer, - ConfigLoader.Enhancers.radar(AuthConfig( - managementPortalUrl = config.authentication.url.toString(), - jwtResourceName = config.authentication.resourceName - )), - ConfigLoader.Enhancers.managementPortal, + radarEnhancer, + ConfigLoader.Enhancers.managementPortal(config.auth), + ConfigLoader.Enhancers.health, ConfigLoader.Enhancers.generalException, - ConfigLoader.Enhancers.httpException) + ConfigLoader.Enhancers.httpException) + resolverEnhancer } } diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/managementportal/MPClient.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/managementportal/MPClient.kt deleted file mode 100644 index e5d1ebb..0000000 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/managementportal/MPClient.kt +++ /dev/null @@ -1,117 +0,0 @@ -package org.radarbase.appconfig.managementportal - -import com.fasterxml.jackson.core.type.TypeReference -import com.fasterxml.jackson.databind.DeserializationFeature -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import okhttp3.* -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import org.radarbase.appconfig.config.ApplicationConfig -import org.radarbase.appconfig.domain.MPUser -import org.radarbase.appconfig.domain.OAuthClient -import org.radarbase.appconfig.domain.Project -import org.radarbase.appconfig.domain.User -import org.radarbase.jersey.auth.Auth -import org.radarbase.jersey.exception.HttpBadGatewayException -import org.radarcns.auth.authorization.Permission -import org.slf4j.LoggerFactory -import java.net.MalformedURLException -import java.time.Duration -import java.time.Instant -import javax.ws.rs.core.Context - -class MPClient( - @Context config: ApplicationConfig, - @Context private val auth: Auth -) { - private val clientId: String = config.authentication.clientId - private val clientSecret: String = config.authentication.clientSecret ?: throw IllegalArgumentException("Cannot configure managementportal client without client secret") - private val httpClient = OkHttpClient() - private val baseUrl: HttpUrl = config.authentication.url.toHttpUrlOrNull() - ?.newBuilder() - ?.addPathSegment("") // force trailing slash - ?.build() - ?: throw MalformedURLException("Cannot parse base URL ${config.authentication.url} as an URL") - private val mapper = jacksonObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - private val projectListReader = mapper.readerFor(object : TypeReference>(){}) - private val userListReader = mapper.readerFor(object : TypeReference>(){}) - private val clientListReader = mapper.readerFor(object : TypeReference>(){}) - - private var token: String? = null - private var expiration: Instant? = null - - private val validToken: String? - get() { - val localToken = token ?: return null - expiration?.takeIf { it > Instant.now() } ?: return null - return localToken - } - - private fun ensureToken(): String { - var localToken = validToken - - return if (localToken != null) { - localToken - } else { - val result = mapper.readTree(execute(Request.Builder().apply { - url(baseUrl.resolve("oauth/token")!!) - post(FormBody.Builder().apply { - add("grant_type", "client_credentials") - add("client_id", clientId) - add("client_secret", clientSecret) - }.build()) - header("Authorization", Credentials.basic(clientId, clientSecret)) - }.build())) - - localToken = result["access_token"].asText() - ?: throw HttpBadGatewayException("ManagementPortal did not provide an access token") - expiration = Instant.now() + Duration.ofSeconds(result["expires_in"].asLong()) - Duration.ofMinutes(5) - token = localToken - localToken - } - } - - fun readProjects(): List { - val request = Request.Builder().apply { - url(baseUrl.resolve("api/projects")!!) - header("Authorization", "Bearer ${ensureToken()}") - }.build() - - return projectListReader.readValue(execute(request)) - } - - fun readUsers(projectId: String): List { - val request = Request.Builder().apply { - url(baseUrl.resolve("api/projects/$projectId/subjects")!!) - header("Authorization", "Bearer ${ensureToken()}") - }.build() - - return userListReader.readValue>(execute(request)) - .filter { auth.token.hasPermissionOnSubject(Permission.SUBJECT_READ, projectId, it.login) } - .map { User(id = it.login, externalUserId = it.externalUserId) } - } - - private fun execute(request: Request): String { - return httpClient.newCall(request).execute().use { response -> - if (response.isSuccessful) { - response.body?.string() - ?: throw HttpBadGatewayException("ManagementPortal did not provide a result") - } else { - logger.error("Failed to reach ManagementPortal URL {} (code {}): {}", request.url, response.code, response.body?.string()) - throw HttpBadGatewayException("Cannot connect to managementportal") - } - } - } - - fun readClients(): List { - val request = Request.Builder().apply { - url(baseUrl.resolve("api/oauth-clients")!!) - header("Authorization", "Bearer ${ensureToken()}") - }.build() - - return clientListReader.readValue(execute(request)) - } - - companion object { - private val logger = LoggerFactory.getLogger(MPClient::class.java) - } -} diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/persistence/EntityManagerExtensions.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/persistence/EntityManagerExtensions.kt deleted file mode 100644 index a8ace3a..0000000 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/persistence/EntityManagerExtensions.kt +++ /dev/null @@ -1,54 +0,0 @@ -package org.radarbase.appconfig.persistence - -import org.radarbase.jersey.exception.HttpInternalServerException -import org.slf4j.LoggerFactory -import java.io.Closeable -import javax.persistence.EntityManager -import javax.persistence.EntityTransaction - -/** - * Run a transaction and commit it. If an exception occurs, the transaction is rolled back. - */ -fun EntityManager.transact(transactionOperation: EntityManager.() -> T) = createTransaction { - it.use { transactionOperation() } -} - -/** - * Start a transaction without committing it. If an exception occurs, the transaction is rolled back. - */ -fun EntityManager.createTransaction(transactionOperation: EntityManager.(CloseableTransaction) -> T): T { - val currentTransaction = transaction - ?: throw HttpInternalServerException("transaction_not_found", "Cannot find a transaction from EntityManager") - - currentTransaction.begin() - try { - return transactionOperation(object : CloseableTransaction { - override val transaction: EntityTransaction = currentTransaction - - override fun close() { - try { - transaction.commit() - } catch (ex: Exception) { - logger.error("Rolling back operation", ex) - if (currentTransaction.isActive) { - currentTransaction.rollback() - } - throw ex - } - } - }) - } catch (ex: Exception) { - logger.error("Rolling back operation", ex) - if (currentTransaction.isActive) { - currentTransaction.rollback() - } - throw ex - } -} - -private val logger = LoggerFactory.getLogger(EntityManager::class.java) - -interface CloseableTransaction: Closeable { - val transaction: EntityTransaction - override fun close() -} diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/persistence/HibernateVariableResolver.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/persistence/HibernateVariableResolver.kt index 1e74197..2eb1d4a 100644 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/persistence/HibernateVariableResolver.kt +++ b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/persistence/HibernateVariableResolver.kt @@ -3,26 +3,24 @@ package org.radarbase.appconfig.persistence import nl.thehyve.lang.expression.* import org.hibernate.criterion.MatchMode import org.radarbase.appconfig.persistence.entity.ConfigEntity +import org.radarbase.jersey.hibernate.HibernateRepository import java.util.stream.Collectors import java.util.stream.Stream +import javax.inject.Provider import javax.persistence.EntityManager import javax.persistence.Query import javax.persistence.TypedQuery class HibernateVariableResolver( - private val em: EntityManager, + em: Provider, private val clientId: String -): VariableResolver { - override fun replace(scope: Scope, prefix: QualifiedId?, variables: Stream>) = em.run { - val query = deleteConfig(scope, prefix) +): VariableResolver, HibernateRepository(em) { + override fun replace(scope: Scope, prefix: QualifiedId?, variables: Stream>) = transact { + deleteConfig(scope, prefix).executeUpdate() - transact { - query.executeUpdate() - - variables - .filter { (id, _) -> !id.isEmpty() } - .forEach { (id, variable) -> save(scope, id, variable) } - } + variables + .filter { (id, _) -> !id.isEmpty() } + .forEach { (id, variable) -> save(scope, id, variable) } } private fun EntityManager.save(scope: Scope, id: QualifiedId, variable: Variable) { @@ -39,7 +37,7 @@ class HibernateVariableResolver( persist(configEntity) } - override fun register(scope: Scope, id: QualifiedId, variable: Variable) = em.transact { + override fun register(scope: Scope, id: QualifiedId, variable: Variable) = transact { save(scope, id, variable) } @@ -47,46 +45,35 @@ class HibernateVariableResolver( if (id.isEmpty()) { throw IllegalArgumentException("Cannot get variable without variable name") } - - return em.run { - val query = selectConfigName(scopes, id) - - transact { - query.resultStream - .map { it.toResolvedVariable() } - .reduce(higherScopedVariable(scopes)) - .orElseThrow { NoSuchFieldError("Unknown variable $id in scopes $scopes.") } - } - } - } - - override fun resolveAll(scopes: List, prefix: QualifiedId?): Stream = em.run { - val query = selectConfig(scopes, prefix) - - transact { - query.resultStream + return transact { + selectConfigName(scopes, id) + .resultStream .map { it.toResolvedVariable() } - .collect(Collectors.toMap({ - if (it.scope == scopes[0]) ActualVariableKey(it.id) - else DefaultsVariableKey(it.id) - }, { it }, higherScopedVariable(scopes))) - .values - .stream() + .reduce(higherScopedVariable(scopes)) + .orElseThrow { NoSuchFieldError("Unknown variable $id in scopes $scopes.") } } } - override fun list(scopes: List, prefix: QualifiedId?): Stream = em.run { - val query = listConfig(scopes, prefix) - - transact> { - (query.resultStream as Stream<*>) - .map { rawResult -> - val result = rawResult as Array<*> - QualifiedId(result[0] as String, result[1] as String) - } - .collect(Collectors.toList()) - .stream() - } + override fun resolveAll(scopes: List, prefix: QualifiedId?): Stream = transact { + selectConfig(scopes, prefix) + .resultStream + .map { it.toResolvedVariable() } + .collect(Collectors.toMap({ + if (it.scope == scopes[0]) ActualVariableKey(it.id) + else DefaultsVariableKey(it.id) + }, { it }, higherScopedVariable(scopes))) + .values + .stream() + } + + override fun list(scopes: List, prefix: QualifiedId?): Stream = transact { + (listConfig(scopes, prefix).resultStream as Stream<*>) + .map { rawResult -> + val result = rawResult as Array<*> + QualifiedId(result[0] as String, result[1] as String) + } + .collect(Collectors.toList()) + .stream() } private fun EntityManager.deleteConfig(scope: Scope, prefix: QualifiedId?): Query { diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/resource/ProjectResource.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/resource/ProjectResource.kt index 4cbdcba..9136377 100644 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/resource/ProjectResource.kt +++ b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/resource/ProjectResource.kt @@ -3,6 +3,7 @@ package org.radarbase.appconfig.resource import org.radarbase.appconfig.domain.ClientConfig import org.radarbase.appconfig.domain.Project import org.radarbase.appconfig.domain.ProjectList +import org.radarbase.appconfig.domain.toProject import org.radarbase.appconfig.service.ClientService import org.radarbase.appconfig.service.ConfigProjectService import org.radarbase.appconfig.service.ConfigService @@ -10,6 +11,8 @@ import org.radarbase.jersey.auth.Auth import org.radarbase.jersey.auth.Authenticated import org.radarbase.jersey.auth.NeedsPermission import org.radarbase.jersey.exception.HttpNotFoundException +import org.radarbase.jersey.service.managementportal.MPProject +import org.radarbase.jersey.service.managementportal.RadarProjectService import org.radarcns.auth.authorization.Permission import org.radarcns.auth.authorization.Permission.Entity import org.radarcns.auth.authorization.Permission.Operation @@ -25,21 +28,21 @@ import javax.ws.rs.core.Response @Singleton @Authenticated class ProjectResource( + @Context private val radarProjectService: RadarProjectService, @Context private val projectService: ConfigProjectService, @Context private val configService: ConfigService, @Context private val clientService: ClientService ) { @GET @NeedsPermission(Entity.PROJECT, Operation.READ) - fun listProjects(@Context auth: Auth) = ProjectList(projectService.listProjects() - .filter { auth.token.hasPermissionOnProject(Permission.PROJECT_READ, it.name) }) + fun listProjects(@Context auth: Auth) = ProjectList(radarProjectService.userProjects(auth) + .map(MPProject::toProject)) @GET @NeedsPermission(Entity.PROJECT, Operation.READ, "projectId") @Path("{projectId}") fun get(@PathParam("projectId") projectId: String): Project = - projectService.find(projectId) - ?: throw HttpNotFoundException("project_missing", "Project $projectId not found") + radarProjectService.project(projectId).toProject() @Path("{projectId}/config/{clientId}") @GET @@ -64,4 +67,4 @@ class ProjectResource( projectService.putProjectConfig(clientId, projectId, clientConfig) return projectService.projectConfig(clientId, projectId) } -} \ No newline at end of file +} diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/resource/RootResource.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/resource/RootResource.kt index 75a8175..29269a6 100644 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/resource/RootResource.kt +++ b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/resource/RootResource.kt @@ -1,9 +1,11 @@ package org.radarbase.appconfig.resource import org.radarbase.appconfig.domain.OAuthClientList +import org.radarbase.appconfig.domain.toOAuthClient import org.radarbase.appconfig.service.ClientService import org.radarbase.jersey.auth.Authenticated import org.radarbase.jersey.auth.NeedsPermission +import org.radarbase.jersey.service.managementportal.MPOAuthClient import org.radarcns.auth.authorization.Permission import javax.inject.Singleton import javax.ws.rs.Consumes @@ -25,9 +27,8 @@ class RootResource( @Path("clients") @GET @NeedsPermission(Permission.Entity.OAUTHCLIENTS, Permission.Operation.READ) - fun clients(): OAuthClientList { - return OAuthClientList(clientService.readClients().toList()) - } + fun clients() = OAuthClientList(clientService.readClients() + .map(MPOAuthClient::toOAuthClient)) } /** diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/resource/UserResource.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/resource/UserResource.kt index 28d98cb..c853e27 100644 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/resource/UserResource.kt +++ b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/resource/UserResource.kt @@ -3,14 +3,16 @@ package org.radarbase.appconfig.resource import org.radarbase.appconfig.domain.ClientConfig import org.radarbase.appconfig.domain.User import org.radarbase.appconfig.domain.UserList +import org.radarbase.appconfig.domain.toUser import org.radarbase.appconfig.service.ClientService -import org.radarbase.appconfig.service.ProjectAuthService import org.radarbase.appconfig.service.UserService -import org.radarbase.jersey.auth.Auth +import org.radarbase.appconfig.service.ensureUser +import org.radarbase.appconfig.service.find import org.radarbase.jersey.auth.Authenticated import org.radarbase.jersey.auth.NeedsPermission -import org.radarbase.jersey.auth.ProjectService import org.radarbase.jersey.exception.HttpNotFoundException +import org.radarbase.jersey.service.managementportal.MPUser +import org.radarbase.jersey.service.managementportal.RadarProjectService import org.radarcns.auth.authorization.Permission import javax.inject.Singleton import javax.ws.rs.* @@ -25,17 +27,16 @@ import javax.ws.rs.core.MediaType @Singleton class UserResource( @Context private val userService: UserService, - @Context private val projectService: ProjectService, - @Context private val clientService: ClientService + @Context private val clientService: ClientService, + @Context private val radarProjectService: RadarProjectService ) { @GET @NeedsPermission(Permission.Entity.SUBJECT, Permission.Operation.READ, "projectId") fun userClientConfig( - @PathParam("projectId") projectId: String, - @Context auth: Auth + @PathParam("projectId") projectId: String ): UserList { - return UserList(userService.list(projectId) - .filter { auth.token.hasPermissionOnSubject(Permission.SUBJECT_READ, projectId, it.id) }) + return UserList(radarProjectService.projectUsers(projectId) + .map(MPUser::toUser)) } @Path("/{userId}") @@ -45,7 +46,7 @@ class UserResource( @PathParam("projectId") projectId: String, @PathParam("userId") userId: String ): User { - return userService.find(projectId, userId) + return radarProjectService.find(projectId, userId) ?: throw HttpNotFoundException("user_missing", "User not found") } @@ -58,7 +59,7 @@ class UserResource( @PathParam("clientId") clientId: String ): ClientConfig { clientService.ensureClient(clientId) - userService.ensureUser(projectId, userId) + radarProjectService.ensureUser(projectId, userId) return userService.userConfig(clientId, projectId, userId) } @@ -72,7 +73,7 @@ class UserResource( clientConfig: ClientConfig ): ClientConfig { clientService.ensureClient(clientId) - userService.ensureUser(projectId, userId) + radarProjectService.ensureUser(projectId, userId) userService.putUserConfig(clientId, userId, clientConfig) return userService.userConfig(clientId, projectId, userId) } diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/ClientService.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/ClientService.kt index 220948d..a8fdecd 100644 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/ClientService.kt +++ b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/ClientService.kt @@ -1,16 +1,16 @@ package org.radarbase.appconfig.service -import org.radarbase.appconfig.domain.OAuthClient -import org.radarbase.appconfig.managementportal.MPClient -import org.radarbase.appconfig.util.CachedSet import org.radarbase.jersey.exception.HttpNotFoundException +import org.radarbase.jersey.service.managementportal.MPClient +import org.radarbase.jersey.service.managementportal.MPOAuthClient +import org.radarbase.jersey.util.CachedSet import java.time.Duration import javax.ws.rs.core.Context class ClientService(@Context private val mpClient: MPClient) { private val clients = CachedSet(Duration.ofHours(1), Duration.ofMinutes(5), mpClient::readClients) - fun readClients(): Set = clients.get() + fun readClients(): Set = clients.get() fun ensureClient(name: String) { if (!contains(name)) { @@ -18,5 +18,5 @@ class ClientService(@Context private val mpClient: MPClient) { } } - fun contains(clientId: String) = clients.contains(OAuthClient(clientId)) + fun contains(clientId: String) = clients.contains(MPOAuthClient(clientId)) } diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/ConfigProjectService.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/ConfigProjectService.kt index c60c1f5..cda2011 100644 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/ConfigProjectService.kt +++ b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/ConfigProjectService.kt @@ -4,9 +4,6 @@ import org.radarbase.appconfig.domain.ClientConfig import org.radarbase.appconfig.domain.Project interface ConfigProjectService { - operator fun contains(name: String): Boolean - fun listProjects(): Set fun projectConfig(clientId: String, projectId: String): ClientConfig fun putProjectConfig(clientId: String, projectId: String, clientConfig: ClientConfig) - fun find(projectId: String): Project? } diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/ConfigService.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/ConfigService.kt index 1b90b48..bf9dc2e 100644 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/ConfigService.kt +++ b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/ConfigService.kt @@ -3,14 +3,10 @@ package org.radarbase.appconfig.service import nl.thehyve.lang.expression.* import org.radarbase.appconfig.domain.ClientConfig import org.radarbase.appconfig.inject.ClientVariableResolver -import org.radarbase.appconfig.service.ConditionService.Companion.conditionScope -import org.radarbase.appconfig.service.MPProjectService.Companion.projectScope -import org.radarbase.jersey.auth.ProjectService import javax.ws.rs.core.Context class ConfigService( @Context private val resolver: ClientVariableResolver, - @Context private val projectService: ProjectService, @Context private val conditionService: ConditionService, @Context private val clientService: ClientService ) { diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/MPProjectService.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/MPProjectService.kt index a0a6ba3..9577543 100644 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/MPProjectService.kt +++ b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/MPProjectService.kt @@ -2,27 +2,12 @@ package org.radarbase.appconfig.service import nl.thehyve.lang.expression.* import org.radarbase.appconfig.domain.ClientConfig -import org.radarbase.appconfig.domain.Project import org.radarbase.appconfig.inject.ClientVariableResolver -import org.radarbase.appconfig.managementportal.MPClient -import org.radarbase.appconfig.util.CachedSet -import org.radarbase.jersey.auth.Auth -import org.radarcns.auth.authorization.Permission -import java.time.Duration import javax.ws.rs.core.Context class MPProjectService( - @Context private val auth: Auth, - @Context private val mpClient: MPClient, @Context private val resolver: ClientVariableResolver ): ConfigProjectService { - private val projects = CachedSet(Duration.ofMinutes(5), Duration.ofMinutes(1), mpClient::readProjects) - - override operator fun contains(name: String) = projects.get().any { it.name == name } - - override fun listProjects(): Set = projects.get() - .filterTo(LinkedHashSet()) { auth.token.hasPermissionOnProject(Permission.PROJECT_READ, it.name) } - override fun projectConfig(clientId: String, projectId: String): ClientConfig { val scope = projectScope(projectId) return ClientConfig.fromStream(clientId, scope, @@ -39,9 +24,6 @@ class MPProjectService( }) } - override fun find(projectId: String): Project? = projects.find { it.name == projectId } - ?.takeIf { auth.token.hasPermissionOnProject(Permission.PROJECT_READ, it.name) } - companion object { fun projectScope(projectId: String): Scope = SimpleScope(QualifiedId(listOf("project", projectId))) } diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/ProjectAuthService.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/ProjectAuthService.kt deleted file mode 100644 index 75405d1..0000000 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/ProjectAuthService.kt +++ /dev/null @@ -1,15 +0,0 @@ -package org.radarbase.appconfig.service - -import org.radarbase.jersey.auth.ProjectService -import org.radarbase.jersey.exception.HttpNotFoundException -import javax.ws.rs.core.Context - -class ProjectAuthService( - @Context private val configProjectService: ConfigProjectService -): ProjectService { - override fun ensureProject(projectId: String) { - if (projectId !in configProjectService) { - throw HttpNotFoundException("project_not_found", "Project $projectId not found.") - } - } -} diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/RadarProjectServiceExtensions.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/RadarProjectServiceExtensions.kt new file mode 100644 index 0000000..4b59d56 --- /dev/null +++ b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/RadarProjectServiceExtensions.kt @@ -0,0 +1,16 @@ +package org.radarbase.appconfig.service + +import org.radarbase.appconfig.domain.User +import org.radarbase.appconfig.domain.toUser +import org.radarbase.jersey.exception.HttpNotFoundException +import org.radarbase.jersey.service.managementportal.RadarProjectService + +fun RadarProjectService.ensureUser(projectId: String, userId: String) { + if (projectUsers(projectId).find { it.id == userId } == null) { + throw HttpNotFoundException("user_missing", "User $userId not found in project $projectId") + } +} + +fun RadarProjectService.find(projectId: String, userId: String): User? = projectUsers(projectId) + .find { it.id == userId } + ?.toUser() diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/UserService.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/UserService.kt index 06eb797..b63f051 100644 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/UserService.kt +++ b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/service/UserService.kt @@ -5,38 +5,15 @@ import nl.thehyve.lang.expression.QualifiedId import nl.thehyve.lang.expression.Scope import nl.thehyve.lang.expression.toVariable import org.radarbase.appconfig.domain.ClientConfig -import org.radarbase.appconfig.domain.User import org.radarbase.appconfig.inject.ClientVariableResolver -import org.radarbase.appconfig.managementportal.MPClient import org.radarbase.appconfig.service.ConfigService.Companion.userScope import org.radarbase.appconfig.service.MPProjectService.Companion.projectScope -import org.radarbase.appconfig.util.CachedSet -import org.radarbase.jersey.exception.HttpNotFoundException -import java.time.Duration -import java.util.concurrent.ConcurrentHashMap import javax.ws.rs.core.Context class UserService( - @Context private val mpClient: MPClient, @Context private val conditionService: ConditionService, @Context private val resolver: ClientVariableResolver ) { - private val users = ConcurrentHashMap>() - - private operator fun get(projectId: String) = users.computeIfAbsent(projectId) { - CachedSet(Duration.ofMinutes(5), Duration.ofMinutes(1)) { - mpClient.readUsers(projectId) - } - } - - fun ensureUser(projectId: String, userId: String) { - if (this[projectId].find { it.id == userId } == null) { - throw HttpNotFoundException("user_missing", "User $userId not found in project $projectId") - } - } - - fun list(projectId: String) = this[projectId].get() - fun putUserConfig(clientId: String, userId: String, clientConfig: ClientConfig) { resolver[clientId].replace( userScope(userId), @@ -61,6 +38,4 @@ class UserService( + conditions + listOf(projectScope(projectId), ConfigService.globalScope)) } - - fun find(projectId: String, userId: String): User? = this[projectId].find { it.id == userId } -} \ No newline at end of file +} diff --git a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/util/CachedSet.kt b/radar-app-config/src/main/kotlin/org/radarbase/appconfig/util/CachedSet.kt deleted file mode 100644 index 4fd458b..0000000 --- a/radar-app-config/src/main/kotlin/org/radarbase/appconfig/util/CachedSet.kt +++ /dev/null @@ -1,52 +0,0 @@ -package org.radarbase.appconfig.util - -import java.time.Duration -import java.time.Instant - -class CachedSet( - private val refreshDuration: Duration, - private val retryDuration: Duration, - private val supplier: () -> Iterable) { - @set:Synchronized - private var cached: Set = emptySet() - set(value) { - val now = Instant.now() - field = value - nextRefresh = now.plus(refreshDuration) - nextRetry = now.plus(retryDuration) - } - - private var nextRefresh: Instant = Instant.MIN - private var nextRetry: Instant = Instant.MIN - - @get:Synchronized - private val state: State - get () { - val now = Instant.now() - return State(cached, - now.isAfter(nextRefresh), - now.isAfter(nextRetry)) - } - - private fun refresh() = supplier.invoke().toSet() - .also { cached = it } - - fun contains(value: T) = state.query({ it.contains(value) }, { it }) - fun find(predicate: (T) -> Boolean): T? = state.query({ it.find(predicate) }, { it != null }) - fun get(): Set = state.query({ it }, { it.isNotEmpty() }) - - private inner class State(val cache: Set, val mustRefresh: Boolean, val mayRetry: Boolean) { - fun query(method: (Set) -> S, validityPredicate: (S) -> Boolean): S { - var result: S - if (mustRefresh) { - result = method(refresh()) - } else { - result = method(cache) - if (!validityPredicate(result) && mayRetry) { - result = method(refresh()) - } - } - return result - } - } -} diff --git a/radar-app-config/src/main/resources/META-INF/persistence.xml b/radar-app-config/src/main/resources/META-INF/persistence.xml deleted file mode 100644 index e8c3948..0000000 --- a/radar-app-config/src/main/resources/META-INF/persistence.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - org.radarbase.appconfig.persistence.entity.ConfigEntity - - - - - - - - - - - - - - - - - diff --git a/radar-app-config/src/main/resources/dbChangelog.xml b/radar-app-config/src/main/resources/db/changelog/changes/202010071457-create-database.xml similarity index 96% rename from radar-app-config/src/main/resources/dbChangelog.xml rename to radar-app-config/src/main/resources/db/changelog/changes/202010071457-create-database.xml index 5af312d..aa14aad 100644 --- a/radar-app-config/src/main/resources/dbChangelog.xml +++ b/radar-app-config/src/main/resources/db/changelog/changes/202010071457-create-database.xml @@ -22,7 +22,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog - http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"> + http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.9.xsd"> diff --git a/radar-app-config/src/main/resources/db/changelog/changes/db.changelog-master.xml b/radar-app-config/src/main/resources/db/changelog/changes/db.changelog-master.xml new file mode 100644 index 0000000..69cbc14 --- /dev/null +++ b/radar-app-config/src/main/resources/db/changelog/changes/db.changelog-master.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/radar-app-config/src/test/kotlin/org/radarbase/appconfig/managementportal/MPClientTest.kt b/radar-app-config/src/test/kotlin/org/radarbase/appconfig/managementportal/MPClientTest.kt deleted file mode 100644 index 802a04a..0000000 --- a/radar-app-config/src/test/kotlin/org/radarbase/appconfig/managementportal/MPClientTest.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.radarbase.appconfig.managementportal - -import com.nhaarman.mockitokotlin2.doReturn -import com.nhaarman.mockitokotlin2.mock -import org.junit.jupiter.api.Test -import org.mockito.ArgumentMatchers.anyString -import org.mockito.ArgumentMatchers.eq -import org.radarbase.appconfig.config.ApplicationConfig -import org.radarbase.appconfig.config.AuthenticationConfig -import org.radarbase.jersey.auth.Auth -import org.radarcns.auth.authorization.Permission -import org.radarcns.auth.token.RadarToken -import java.net.URL - -internal class MPClientTest { - @Test - fun testProjects() { - val config = ApplicationConfig( - authentication = AuthenticationConfig( - url = URL("https://radar-test.thehyve.net/managementportal/"), - clientSecret = "appconfig_test")) - - val mockToken = mock { - on { hasPermissionOnProject(eq(Permission.PROJECT_READ), anyString()) } doReturn true - } - val auth = mock { - on { token } doReturn mockToken - } - val client = MPClient(config, auth) - println(client.readProjects()) - } -} diff --git a/radar-app-config/src/test/kotlin/org/radarbase/appconfig/service/ProjectServiceTest.kt b/radar-app-config/src/test/kotlin/org/radarbase/appconfig/service/ProjectServiceTest.kt index d7ba237..64fe04e 100644 --- a/radar-app-config/src/test/kotlin/org/radarbase/appconfig/service/ProjectServiceTest.kt +++ b/radar-app-config/src/test/kotlin/org/radarbase/appconfig/service/ProjectServiceTest.kt @@ -1,20 +1,14 @@ package org.radarbase.appconfig.service -import com.nhaarman.mockitokotlin2.mock import nl.thehyve.lang.expression.register import nl.thehyve.lang.expression.toVariable import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.radarbase.appconfig.config.ApplicationConfig -import org.radarbase.appconfig.config.AuthenticationConfig import org.radarbase.appconfig.domain.ClientConfig import org.radarbase.appconfig.domain.SingleVariable import org.radarbase.appconfig.inject.ClientVariableResolver import org.radarbase.appconfig.inject.InMemoryResourceEnhancer -import org.radarbase.appconfig.managementportal.MPClient -import org.radarbase.jersey.auth.Auth -import java.net.URL internal class ProjectServiceTest { private lateinit var projectService: ConfigProjectService @@ -23,14 +17,7 @@ internal class ProjectServiceTest { @BeforeEach fun setUp() { resolver = InMemoryResourceEnhancer.InMemoryClientVariableResolver() - val config = ApplicationConfig( - authentication = AuthenticationConfig( - url = URL("https://radar-test.thehyve.net/managementportal/"), - clientSecret = "peyman")) - - val auth = mock {} - val mpClient = MPClient(config, auth) - projectService = MPProjectService(mpClient, resolver) + projectService = MPProjectService(resolver) } @Test diff --git a/radar-app-config/src/test/kotlin/org/radarbase/appconfig/service/UserServiceTest.kt b/radar-app-config/src/test/kotlin/org/radarbase/appconfig/service/UserServiceTest.kt index ac1ccf0..85e1139 100644 --- a/radar-app-config/src/test/kotlin/org/radarbase/appconfig/service/UserServiceTest.kt +++ b/radar-app-config/src/test/kotlin/org/radarbase/appconfig/service/UserServiceTest.kt @@ -1,19 +1,13 @@ package org.radarbase.appconfig.service -import com.nhaarman.mockitokotlin2.mock import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.radarbase.appconfig.config.ApplicationConfig -import org.radarbase.appconfig.config.AuthenticationConfig import org.radarbase.appconfig.domain.ClientConfig import org.radarbase.appconfig.domain.SingleVariable import org.radarbase.appconfig.inject.ClientInterpreter import org.radarbase.appconfig.inject.ClientVariableResolver import org.radarbase.appconfig.inject.InMemoryResourceEnhancer -import org.radarbase.appconfig.managementportal.MPClient -import org.radarbase.jersey.auth.Auth -import java.net.URL internal class UserServiceTest { private lateinit var userService: UserService @@ -23,16 +17,10 @@ internal class UserServiceTest { @BeforeEach fun setUp() { resolver = InMemoryResourceEnhancer.InMemoryClientVariableResolver() - val config = ApplicationConfig( - authentication = AuthenticationConfig( - url = URL("https://radar-test.thehyve.net/managementportal/"), - clientSecret = "peyman")) - val auth = mock {} - val mpClient = MPClient(config, auth) val conditionService = ConditionService(resolver, ClientInterpreter(resolver)) - userService = UserService(mpClient, conditionService, resolver) - projectService = MPProjectService(mpClient, resolver) + userService = UserService(conditionService, resolver) + projectService = MPProjectService(resolver) } @Test diff --git a/radar-expression-lang/build.gradle.kts b/radar-expression-lang/build.gradle.kts index 0ae3cec..494a8ae 100644 --- a/radar-expression-lang/build.gradle.kts +++ b/radar-expression-lang/build.gradle.kts @@ -17,25 +17,21 @@ sourceSets { } } -project.extra.apply { - set("jacksonVersion", "2.11.2") - set("antlrVersion", "4.8-1") -} - - dependencies { - antlr("org.antlr:antlr4:${project.extra["antlrVersion"]}") + val antlrVersion: String by project + antlr("org.antlr:antlr4:$antlrVersion") - implementation("org.antlr:antlr4-runtime:${project.extra["antlrVersion"]}") + implementation("org.antlr:antlr4-runtime:$antlrVersion") implementation("me.xdrop:fuzzywuzzy:1.2.0") // Use the Kotlin JDK 8 standard library. implementation(kotlin("stdlib-jdk8")) implementation(kotlin("reflect")) - api("com.fasterxml.jackson.core:jackson-annotations:${project.extra["jacksonVersion"]}") - implementation("com.fasterxml.jackson.core:jackson-databind:${project.extra["jacksonVersion"]}") - implementation("com.fasterxml.jackson.module:jackson-module-kotlin:${project.extra["jacksonVersion"]}") + val jacksonVersion: String by project + api("com.fasterxml.jackson.core:jackson-annotations:$jacksonVersion") + implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion") // Use the Kotlin test library. testImplementation("org.jetbrains.kotlin:kotlin-test") diff --git a/settings.gradle.kts b/settings.gradle.kts index 1b2b7d6..ed4617a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,3 +10,13 @@ rootProject.name = "radar-app-config" include(":radar-app-config") include(":radar-expression-lang") + +pluginManagement { + val kotlinVersion: String by settings + plugins { + kotlin("jvm") version kotlinVersion + id("org.jetbrains.kotlin.plugin.noarg") version kotlinVersion + id("org.jetbrains.kotlin.plugin.jpa") version kotlinVersion + id("org.jetbrains.kotlin.plugin.allopen") version kotlinVersion + } +}