diff --git a/app-sample.yaml b/app-sample.yaml index b46446b..003a25b 100644 --- a/app-sample.yaml +++ b/app-sample.yaml @@ -18,6 +18,8 @@ app: client: rmc scripts: path: /Users/gowthaman.b/IdeaProjects/rmc_modules_api/src/main/resources/scripts + integration: + rmc: https://www.readymixerp.com security: enforce_role_restriction: 'true' private_key: |- diff --git a/src/main/kotlin/com/restapi/Main.kt b/src/main/kotlin/com/restapi/Main.kt index 74f0a96..1d71d6a 100644 --- a/src/main/kotlin/com/restapi/Main.kt +++ b/src/main/kotlin/com/restapi/Main.kt @@ -9,6 +9,7 @@ import com.restapi.domain.DataNotFoundException import com.restapi.domain.ReminderLog import com.restapi.domain.Session.currentTenant import com.restapi.domain.Session.currentUser +import com.restapi.domain.Session.currentUserPlants import com.restapi.domain.Session.objectMapper import com.restapi.domain.Session.setAuthorizedUser import com.restapi.domain.Session.signPayload @@ -43,15 +44,13 @@ fun main(args: Array) { //ratelimit based on IP Only RateLimitUtil.keyFunction = { ctx -> ctx.header("X-Forwarded-For")?.split(",")?.get(0) ?: ctx.ip() } - Javalin - .create { cfg -> + Javalin.create { cfg -> cfg.http.generateEtags = true if (appConfig.corsEnabled()) { cfg.plugins.enableCors { container -> container.add { it.allowHost( - "http://localhost:5173", - *appConfig.corsHosts().toTypedArray() + "http://localhost:5173", *appConfig.corsHosts().toTypedArray() ) } } @@ -60,8 +59,7 @@ fun main(args: Array) { cfg.compression.gzipOnly() cfg.jsonMapper(JavalinJackson(objectMapper)) cfg.accessManager(AppAccessManager()) - } - .routes { + }.routes { path("/auth") { get("/endpoint", Auth::endPoint) @@ -73,9 +71,7 @@ fun main(args: Array) { before("/api/*") { ctx -> NaiveRateLimit.requestPerTimeUnit( - ctx, - appConfig.rateLimit().getOrDefault(30), - TimeUnit.MINUTES + ctx, appConfig.rateLimit().getOrDefault(30), TimeUnit.MINUTES ) val authToken = ctx.getAuthHeader() ?: throw UnauthorizedResponse() @@ -106,6 +102,9 @@ fun main(args: Array) { } path("/api") { + get("/plants") { + it.json(currentUserPlants()) + } post("/audit/{action}") { logger.warn("User ${currentUser()} of tenant ${currentTenant()} has performed ${it.pathParam("action")} @ ${LocalDateTime.now()}") it.json(mapOf("status" to true)) @@ -117,14 +116,10 @@ fun main(args: Array) { post("/batch", VendorCtrl::createBatch, Roles(Role.Explicit("ROLE_VENDOR_CREATE"))) get("/{id}", VendorCtrl::get, Roles(Role.Explicit("ROLE_VENDOR_VIEW", "ROLE_VENDOR_CREATE"))) post( - "/getAll", - VendorCtrl::getAll, - Roles(Role.Explicit("ROLE_VENDOR_VIEW", "ROLE_VENDOR_CREATE")) + "/getAll", VendorCtrl::getAll, Roles(Role.Explicit("ROLE_VENDOR_VIEW", "ROLE_VENDOR_CREATE")) ) get( - "quotes/{id}", - VendorCtrl::getQuotes, - Roles(Role.Explicit("ROLE_QUOTE_VIEW", "ROLE_QUOTE_CREATE", "ROLE_VENDOR_VIEW")) + "quotes/{id}", VendorCtrl::getQuotes, Roles(Role.Explicit("ROLE_QUOTE_VIEW", "ROLE_QUOTE_CREATE", "ROLE_VENDOR_VIEW")) ) get("pos/{id}", VendorCtrl::getPos, Roles(Role.Explicit("ROLE_PO_VIEW", "ROLE_PO_CREATE`"))) put("/rate/{id}/{rating}", VendorCtrl::rate, Roles(Role.Explicit("ROLE_VENDOR_CREATE"))) @@ -134,59 +129,43 @@ fun main(args: Array) { post("", IncomingInventoryCtrl::create, Roles(Role.Explicit("ROLE_INVENTORY_CREATE"))) get("/next", IncomingInventoryCtrl::getNextNum, Roles(Role.Explicit("ROLE_INVENTORY_CREATE"))) get( - "/{id}", - IncomingInventoryCtrl::get, - Roles(Role.Explicit("ROLE_INVENTORY_VIEW", "ROLE_INVENTORY_CREATE")) + "/{id}", IncomingInventoryCtrl::get, Roles(Role.Explicit("ROLE_INVENTORY_VIEW", "ROLE_INVENTORY_CREATE")) ) put("/{id}", IncomingInventoryCtrl::update, Roles(Role.Explicit("ROLE_INVENTORY_CREATE"))) post( - "/getAll", - IncomingInventoryCtrl::getAll, - Roles(Role.Explicit("ROLE_INVENTORY_CREATE", "ROLE_INVENTORY_VIEW")) + "/getAll", IncomingInventoryCtrl::getAll, Roles(Role.Explicit("ROLE_INVENTORY_CREATE", "ROLE_INVENTORY_VIEW")) ) } path("/outgoing") { post("", OutgoingInventoryCtrl::create, Roles(Role.Explicit("ROLE_INVENTORY_CREATE"))) get("/next", OutgoingInventoryCtrl::getNextNum, Roles(Role.Explicit("ROLE_INVENTORY_CREATE"))) get( - "/{id}", - OutgoingInventoryCtrl::get, - Roles(Role.Explicit("ROLE_INVENTORY_VIEW", "ROLE_INVENTORY_CREATE")) + "/{id}", OutgoingInventoryCtrl::get, Roles(Role.Explicit("ROLE_INVENTORY_VIEW", "ROLE_INVENTORY_CREATE")) ) put("/{id}", OutgoingInventoryCtrl::update, Roles(Role.Explicit("ROLE_INVENTORY_CREATE"))) post( - "/getAll", - OutgoingInventoryCtrl::getAll, - Roles(Role.Explicit("ROLE_INVENTORY_CREATE", "ROLE_INVENTORY_VIEW")) + "/getAll", OutgoingInventoryCtrl::getAll, Roles(Role.Explicit("ROLE_INVENTORY_CREATE", "ROLE_INVENTORY_VIEW")) ) } path("/invoice") { post("", InvoiceCtrl::create, Roles(Role.Explicit("ROLE_INVOICE_CREATE"))) get("/next", InvoiceCtrl::getNextNum, Roles(Role.Explicit("ROLE_INVOICE_CREATE"))) get( - "/{id}", - InvoiceCtrl::get, - Roles(Role.Explicit("ROLE_INVOICE_VIEW", "ROLE_INVOICE_CREATE")) + "/{id}", InvoiceCtrl::get, Roles(Role.Explicit("ROLE_INVOICE_VIEW", "ROLE_INVOICE_CREATE")) ) put("/{id}", InvoiceCtrl::update, Roles(Role.Explicit("ROLE_INVOICE_CREATE"))) post( - "/getAll", - InvoiceCtrl::getAll, - Roles(Role.Explicit("ROLE_INVOICE_CREATE", "ROLE_INVOICE_VIEW")) + "/getAll", InvoiceCtrl::getAll, Roles(Role.Explicit("ROLE_INVOICE_CREATE", "ROLE_INVOICE_VIEW")) ) } path("/payment") { post("", PaymentCtrl::create, Roles(Role.Explicit("ROLE_PAYMENT_CREATE"))) get( - "/{id}", - PaymentCtrl::get, - Roles(Role.Explicit("ROLE_PAYMENT_VIEW", "ROLE_PAYMENT_CREATE")) + "/{id}", PaymentCtrl::get, Roles(Role.Explicit("ROLE_PAYMENT_VIEW", "ROLE_PAYMENT_CREATE")) ) put("/{id}", PaymentCtrl::update, Roles(Role.Explicit("ROLE_PAYMENT_CREATE"))) post( - "/getAll", - PaymentCtrl::getAll, - Roles(Role.Explicit("ROLE_PAYMENT_CREATE", "ROLE_PAYMENT_VIEW")) + "/getAll", PaymentCtrl::getAll, Roles(Role.Explicit("ROLE_PAYMENT_CREATE", "ROLE_PAYMENT_VIEW")) ) delete("/{id}", PaymentCtrl::delete, Roles(Role.Explicit("ROLE_PAYMENT_CREATE"))) } @@ -194,88 +173,62 @@ fun main(args: Array) { post("", FleetCtrl::create, Roles(Role.Explicit("ROLE_FLEET_CREATE"))) get( - "/{id}", - FleetCtrl::get, - Roles(Role.Explicit("ROLE_FLEET_VIEW", "ROLE_FLEET_CREATE")) + "/{id}", FleetCtrl::get, Roles(Role.Explicit("ROLE_FLEET_VIEW", "ROLE_FLEET_CREATE")) ) put("/{id}", FleetCtrl::update, Roles(Role.Explicit("ROLE_FLEET_CREATE"))) post( - "/getAll", - FleetCtrl::getAll, - Roles(Role.Explicit("ROLE_FLEET_CREATE", "ROLE_FLEET_VIEW")) + "/getAll", FleetCtrl::getAll, Roles(Role.Explicit("ROLE_FLEET_CREATE", "ROLE_FLEET_VIEW")) ) delete("/{id}", FleetCtrl::delete, Roles(Role.Explicit("ROLE_FLEET_CREATE"))) } path("/renewal") { post("", RenewalCtrl::create, Roles(Role.Explicit("ROLE_FLEET_CREATE"))) get( - "/{id}", - RenewalCtrl::get, - Roles(Role.Explicit("ROLE_FLEET_VIEW", "ROLE_FLEET_CREATE")) + "/{id}", RenewalCtrl::get, Roles(Role.Explicit("ROLE_FLEET_VIEW", "ROLE_FLEET_CREATE")) ) put("/{id}", RenewalCtrl::update, Roles(Role.Explicit("ROLE_FLEET_CREATE"))) post( - "/getAll", - RenewalCtrl::getAll, - Roles(Role.Explicit("ROLE_FLEET_CREATE", "ROLE_FLEET_VIEW")) + "/getAll", RenewalCtrl::getAll, Roles(Role.Explicit("ROLE_FLEET_CREATE", "ROLE_FLEET_VIEW")) ) delete("/{id}", RenewalCtrl::delete, Roles(Role.Explicit("ROLE_FLEET_CREATE"))) } path("/reminder") { post("", ReminderCtrl::create, Roles(Role.Explicit("ROLE_REMINDER_CREATE"))) get( - "/{id}", - ReminderCtrl::get, - Roles(Role.Explicit("ROLE_REMINDER_VIEW", "ROLE_REMINDER_CREATE")) + "/{id}", ReminderCtrl::get, Roles(Role.Explicit("ROLE_REMINDER_VIEW", "ROLE_REMINDER_CREATE")) ) put("/{id}", ReminderCtrl::update, Roles(Role.Explicit("ROLE_REMINDER_CREATE"))) post( - "/getAll", - ReminderLogCtrl::getAll, - Roles(Role.Explicit("ROLE_REMINDER_CREATE", "ROLE_REMINDER_VIEW")) + "/getAll", ReminderLogCtrl::getAll, Roles(Role.Explicit("ROLE_REMINDER_CREATE", "ROLE_REMINDER_VIEW")) ) post( - "/done", - ReminderLogCtrl::done, - Roles(Role.Explicit("ROLE_REMAINDER_CREATE")) + "/done", ReminderLogCtrl::done, Roles(Role.Explicit("ROLE_REMAINDER_CREATE")) ) get( - "/getAll/{id}", - ReminderCtrl::getAllByFleetId, - Roles(Role.Explicit("ROLE_REMINDER_CREATE", "ROLE_REMINDER_VIEW")) + "/getAll/{id}", ReminderCtrl::getAllByFleetId, Roles(Role.Explicit("ROLE_REMINDER_CREATE", "ROLE_REMINDER_VIEW")) ) delete( - "/{id}", - ReminderCtrl::delete, - Roles(Role.Explicit("ROLE_REMINDER_CREATE")) + "/{id}", ReminderCtrl::delete, Roles(Role.Explicit("ROLE_REMINDER_CREATE")) ) } path("/vehicle") { post("", VehicleCtrl::create, Roles(Role.Explicit("ROLE_FLEET_CREATE"))) get( - "/{id}", - VehicleCtrl::get, - Roles(Role.Explicit("ROLE_FLEET_VIEW", "ROLE_FLEET_CREATE")) + "/{id}", VehicleCtrl::get, Roles(Role.Explicit("ROLE_FLEET_VIEW", "ROLE_FLEET_CREATE")) ) put("/{id}", VehicleCtrl::update, Roles(Role.Explicit("ROLE_FLEET_CREATE"))) post( - "/getAll", - VehicleCtrl::getAll, - Roles(Role.Explicit("ROLE_FLEET_CREATE", "ROLE_FLEET_VIEW")) + "/getAll", VehicleCtrl::getAll, Roles(Role.Explicit("ROLE_FLEET_CREATE", "ROLE_FLEET_VIEW")) ) } path("/fleetType") { post("", FleetTypeCtrl::create, Roles(Role.Explicit("ROLE_FLEET_CREATE"))) get( - "/{id}", - FleetTypeCtrl::get, - Roles(Role.Explicit("ROLE_FLEET_VIEW", "ROLE_FLEET_CREATE")) + "/{id}", FleetTypeCtrl::get, Roles(Role.Explicit("ROLE_FLEET_VIEW", "ROLE_FLEET_CREATE")) ) put("/{id}", FleetTypeCtrl::update, Roles(Role.Explicit("ROLE_FLEET_CREATE"))) post( - "/getAll", - FleetTypeCtrl::getAll, - Roles(Role.Explicit("ROLE_FLEET_CREATE", "ROLE_FLEET_VIEW")) + "/getAll", FleetTypeCtrl::getAll, Roles(Role.Explicit("ROLE_FLEET_CREATE", "ROLE_FLEET_VIEW")) ) } path("/po") { @@ -283,14 +236,10 @@ fun main(args: Array) { post("", PurchaseOrderCtrl::create, Roles(Role.Explicit("ROLE_PO_CREATE"))) post("/batch", PurchaseOrderCtrl::createBatch, Roles(Role.Explicit("ROLE_PO_CREATE"))) post( - "/getAll", - PurchaseOrderCtrl::getAll, - Roles(Role.Explicit("ROLE_PO_CREATE", "ROLE_PO_VIEW", "ROLE_VENDOR_CREATE")) + "/getAll", PurchaseOrderCtrl::getAll, Roles(Role.Explicit("ROLE_PO_CREATE", "ROLE_PO_VIEW", "ROLE_VENDOR_CREATE")) ) get( - "/{id}", - PurchaseOrderCtrl::get, - Roles(Role.Explicit("ROLE_PO_CREATE", "ROLE_PO_VIEW", "ROLE_QUOTE_CREATE")) + "/{id}", PurchaseOrderCtrl::get, Roles(Role.Explicit("ROLE_PO_CREATE", "ROLE_PO_VIEW", "ROLE_QUOTE_CREATE")) ) put("/{id}", PurchaseOrderCtrl::update, Roles(Role.Explicit("ROLE_PO_CREATE"))) put("/approve/{id}", PurchaseOrderCtrl::approve, Roles(Role.Explicit())) @@ -302,9 +251,7 @@ fun main(args: Array) { post("", QuotationCtrl::create, Roles(Role.Explicit("ROLE_QUOTE_CREATE"))) post("/batch", QuotationCtrl::createBatch, Roles(Role.Explicit("ROLE_QUOTE_CREATE"))) post( - "/getAll", - QuotationCtrl::getAll, - Roles(Role.Explicit("ROLE_QUOTE_CREATE", "ROLE_QUOTE_VIEW")) + "/getAll", QuotationCtrl::getAll, Roles(Role.Explicit("ROLE_QUOTE_CREATE", "ROLE_QUOTE_VIEW")) ) get("/{id}", QuotationCtrl::get, Roles(Role.Explicit("ROLE_QUOTE_VIEW", "ROLE_QUOTE_CREATE"))) put("/{id}", QuotationCtrl::update, Roles(Role.Explicit("ROLE_QUOTE_CREATE"))) @@ -322,33 +269,23 @@ fun main(args: Array) { post("", DocumentCtrl::create, Roles(Role.Explicit("ROLE_DOC_CREATE"))) //why type and refid are clubbed ?? get( - "/{type}/{refId}", - DocumentCtrl::getWithRefId, - Roles(Role.Explicit("ROLE_DOC_VIEW", "ROLE_PRODUCT_CREATE")) + "/{type}/{refId}", DocumentCtrl::getWithRefId, Roles(Role.Explicit("ROLE_DOC_VIEW", "ROLE_PRODUCT_CREATE")) ) get("/{id}", DocumentCtrl::get, Roles(Role.Explicit("ROLE_DOC_VIEW", "ROLE_PRODUCT_CREATE"))) get( - "/print/{id}", - DocumentCtrl::print, - Roles(Role.Explicit("ROLE_DOC_CREATE", "ROLE_DOC_VIEW")) + "/print/{id}", DocumentCtrl::print, Roles(Role.Explicit("ROLE_DOC_CREATE", "ROLE_DOC_VIEW")) ) delete("/{id}", DocumentCtrl::delete, Roles(Role.Explicit("ROLE_DOC_CREATE"))) } path("/reqForQuote") { post( - "", - RequestForQuote::create, - Roles(Role.Explicit("ROLE_QUOTE_CREATE", "ROLE_PO_CREATE", "ROLE_RFQ_CREATE")) + "", RequestForQuote::create, Roles(Role.Explicit("ROLE_QUOTE_CREATE", "ROLE_PO_CREATE", "ROLE_RFQ_CREATE")) ) get( - "/{id}", - RequestForQuote::get, - Roles(Role.Explicit("ROLE_RFQ_CREATE", "ROLE_RFQ_VIEW", "ROLE_QUOTE_VIEW", "ROLE_PO_VIEW")) + "/{id}", RequestForQuote::get, Roles(Role.Explicit("ROLE_RFQ_CREATE", "ROLE_RFQ_VIEW", "ROLE_QUOTE_VIEW", "ROLE_PO_VIEW")) ) put( - "/{id}", - RequestForQuote::update, - Roles(Role.Explicit("ROLE_QUOTE_CREATE", "ROLE_PO_CREATE", "ROLE_RFQ_CREATE")) + "/{id}", RequestForQuote::update, Roles(Role.Explicit("ROLE_QUOTE_CREATE", "ROLE_PO_CREATE", "ROLE_RFQ_CREATE")) ) } } @@ -368,18 +305,11 @@ fun main(args: Array) { patch("/{entity}/{id}", Entities::patch, Roles(adminRole, updateRole)) delete("/{entity}/{id}", Entities::delete, Roles(adminRole, Role.Standard(Action.DELETE))) } - } - .exception(DuplicateKeyException::class.java, Exceptions.dupKeyExceptionHandler) - .exception(DataIntegrityException::class.java, Exceptions.dataIntegrityException) - .exception(DataNotFoundException::class.java, Exceptions.dataNotFoundException) - .exception(IllegalArgumentException::class.java, Exceptions.illegalArgumentException) - .exception(JsonMappingException::class.java, Exceptions.jsonMappingException) - .exception(InvalidJwtException::class.java, Exceptions.invalidJwtException) + }.exception(DuplicateKeyException::class.java, Exceptions.dupKeyExceptionHandler).exception(DataIntegrityException::class.java, Exceptions.dataIntegrityException) + .exception(DataNotFoundException::class.java, Exceptions.dataNotFoundException).exception(IllegalArgumentException::class.java, Exceptions.illegalArgumentException) + .exception(JsonMappingException::class.java, Exceptions.jsonMappingException).exception(InvalidJwtException::class.java, Exceptions.invalidJwtException) .start(appConfig.portNumber()) } -private fun Context.getAuthHeader() = header("Authorization") - ?.replace("Bearer ", "") - ?.replace("Bearer: ", "") - ?.trim() +private fun Context.getAuthHeader() = header("Authorization")?.replace("Bearer ", "")?.replace("Bearer: ", "")?.trim() diff --git a/src/main/kotlin/com/restapi/config/AppConfig.kt b/src/main/kotlin/com/restapi/config/AppConfig.kt index f26b2ca..f4409e8 100644 --- a/src/main/kotlin/com/restapi/config/AppConfig.kt +++ b/src/main/kotlin/com/restapi/config/AppConfig.kt @@ -58,6 +58,10 @@ interface AppConfig { @Key("app.iam.client_secret") fun iamClientSecret(): Optional + @Key("app.integration.rmc") + @Default("https://www.readymixerp.com") + fun integrationRmc(): String + @Key("app.cache.redis_uri") fun redisUri(): Optional diff --git a/src/main/kotlin/com/restapi/config/Auth.kt b/src/main/kotlin/com/restapi/config/Auth.kt index a48201b..953080b 100644 --- a/src/main/kotlin/com/restapi/config/Auth.kt +++ b/src/main/kotlin/com/restapi/config/Auth.kt @@ -2,6 +2,7 @@ package com.restapi.config import com.fasterxml.jackson.module.kotlin.readValue import com.restapi.config.AppConfig.Companion.appConfig +import com.restapi.domain.Plant import com.restapi.domain.Session import com.restapi.domain.Session.objectMapper import io.javalin.http.BadRequestResponse @@ -9,6 +10,9 @@ import io.javalin.http.ContentType import io.javalin.http.Context import io.javalin.http.UnauthorizedResponse import io.javalin.security.RouteRole +import org.apache.http.client.methods.HttpGet +import org.apache.http.impl.client.HttpClients +import org.apache.http.util.EntityUtils import org.jose4j.jwk.HttpsJwks import org.jose4j.jwt.consumer.JwtConsumerBuilder import org.jose4j.keys.resolvers.HttpsJwksVerificationKeyResolver @@ -43,13 +47,10 @@ object Auth { fun getAuthEndpoint(): AuthEndpoint { return authCache.computeIfAbsent("AUTH") { - val wellKnown = - "${appConfig.iamUrl()}/realms/${appConfig.iamRealm()}/.well-known/openid-configuration" + val wellKnown = "${appConfig.iamUrl()}/realms/${appConfig.iamRealm()}/.well-known/openid-configuration" val client = HttpClient.newHttpClient() - val req = HttpRequest.newBuilder() - .uri(URI.create(wellKnown)) - .GET().build() + val req = HttpRequest.newBuilder().uri(URI.create(wellKnown)).GET().build() objectMapper.readValue( client.send(req, HttpResponse.BodyHandlers.ofString()).body() @@ -58,19 +59,11 @@ object Auth { } - private val jwtConsumer = JwtConsumerBuilder() - .setRequireExpirationTime() - .setAllowedClockSkewInSeconds(30) - .setRequireSubject() - .setExpectedAudience("account") - .setExpectedIssuer(getAuthEndpoint().issuer) - .setVerificationKeyResolver(HttpsJwksVerificationKeyResolver(HttpsJwks(getAuthEndpoint().jwksUri))) - .build() + private val jwtConsumer = JwtConsumerBuilder().setRequireExpirationTime().setAllowedClockSkewInSeconds(30).setRequireSubject().setExpectedAudience("account") + .setExpectedIssuer(getAuthEndpoint().issuer).setVerificationKeyResolver(HttpsJwksVerificationKeyResolver(HttpsJwks(getAuthEndpoint().jwksUri))).build() - private val jwtConsumerSkipValidate = JwtConsumerBuilder() - .setSkipAllValidators() - .setVerificationKeyResolver(HttpsJwksVerificationKeyResolver(HttpsJwks(getAuthEndpoint().jwksUri))) - .build() + private val jwtConsumerSkipValidate = + JwtConsumerBuilder().setSkipAllValidators().setVerificationKeyResolver(HttpsJwksVerificationKeyResolver(HttpsJwks(getAuthEndpoint().jwksUri))).build() fun validateAuthToken(authToken: String, skipValidate: Boolean = false): AuthUser { @@ -78,14 +71,29 @@ object Auth { val jwtClaims = if (skipValidate) jwtConsumerSkipValidate.process(authToken) else jwtConsumer.process(authToken) val userId = jwtClaims.jwtClaims.claimsMap["preferred_username"] as String val tenant = jwtClaims.jwtClaims.claimsMap["tenant"] as String + val plantIds = jwtClaims.jwtClaims.claimsMap["plantIds"] as List val roles = ((jwtClaims.jwtClaims.claimsMap["realm_access"] as Map)["roles"]) as List val date = Date(jwtClaims.jwtClaims.expirationTime.valueInMillis) + + HttpClients.createDefault().use { h -> + //sync plant's from rmc to here, just name and id + for (plantId in plantIds) { + + val existing = Session.database.find(Plant::class.java).where().eq("plantId", plantId).findOne() + if (existing == null) { + h.execute(HttpGet("${appConfig.integrationRmc()}/plant?id=${plantId}")).use { r -> + if (r.statusLine.statusCode == 200) { + Session.database.save(Plant().apply { + this.plantId = plantId + this.plantName = EntityUtils.toString(r.entity) + }) + } + } + } + } + } return AuthUser( - userName = userId, - tenant = tenant, - roles = roles, - token = authToken, - expiry = LocalDateTime.from(date.toInstant().atZone(ZoneId.systemDefault())) + userName = userId, tenant = tenant, roles = roles, token = authToken, expiry = LocalDateTime.from(date.toInstant().atZone(ZoneId.systemDefault())), plantIds = plantIds ) } @@ -100,8 +108,7 @@ object Auth { fun init(ctx: Context) { val endpoint = getAuthEndpoint().authorizationEndpoint - val redirectUrl = - "$endpoint?response_type=code&client_id=${appConfig.iamClient()}&redirect_uri=${appConfig.iamClientRedirectUri()}&scope=profile&state=1234zyx" + val redirectUrl = "$endpoint?response_type=code&client_id=${appConfig.iamClient()}&redirect_uri=${appConfig.iamClientRedirectUri()}&scope=profile&state=1234zyx" ctx.redirect(redirectUrl) } @@ -112,22 +119,18 @@ object Auth { val ep = getAuthEndpoint().tokenEndpoint val httpClient = HttpClient.newHttpClient() - val req = HttpRequest.newBuilder() - .uri(URI.create(ep)) - .POST( - HttpRequest.BodyPublishers.ofString( - getFormDataAsString( - mapOf( - "code" to code, - "redirect_uri" to redirectUri, - "client_id" to iamClient, - "grant_type" to "authorization_code", - ) + val req = HttpRequest.newBuilder().uri(URI.create(ep)).POST( + HttpRequest.BodyPublishers.ofString( + getFormDataAsString( + mapOf( + "code" to code, + "redirect_uri" to redirectUri, + "client_id" to iamClient, + "grant_type" to "authorization_code", ) ) ) - .header("Content-Type", "application/x-www-form-urlencoded") - .build() + ).header("Content-Type", "application/x-www-form-urlencoded").build() val message = httpClient.send(req, HttpResponse.BodyHandlers.ofString()).body() val atResponse = objectMapper.readValue(message) @@ -135,8 +138,7 @@ object Auth { //keep track of this for renewal when asked by client Session.redis.lpush( - "$AUTH_TOKEN${parsed.userName}", - objectMapper.writeValueAsString( + "$AUTH_TOKEN${parsed.userName}", objectMapper.writeValueAsString( atResponse.copy( createdAt = LocalDateTime.now() ) @@ -147,10 +149,7 @@ object Auth { fun refreshToken(ctx: Context) { //refresh authToken - val authToken = ctx.header("Authorization") - ?.replace("Bearer ", "") - ?.replace("Bearer: ", "") - ?.trim() ?: throw UnauthorizedResponse() + val authToken = ctx.header("Authorization")?.replace("Bearer ", "")?.replace("Bearer: ", "")?.trim() ?: throw UnauthorizedResponse() val authUser = validateAuthToken(authToken, skipValidate = true) val client = ctx.queryParam("client") ?: throw BadRequestResponse("client not sent") @@ -159,10 +158,7 @@ object Auth { val key = "$AUTH_TOKEN${authUser.userName}" val found = Session.redis.llen(key) logger.warn("for user ${authUser.userName}, found from redis, $key => $found entries") - val foundOldAt = (0..found) - .mapNotNull { Session.redis.lindex(key, it) } - .map { objectMapper.readValue(it) } - .firstOrNull { it.accessToken == authToken } + val foundOldAt = (0..found).mapNotNull { Session.redis.lindex(key, it) }.map { objectMapper.readValue(it) }.firstOrNull { it.accessToken == authToken } ?: throw BadRequestResponse("authToken not found in cache") val createdAt = foundOldAt.createdAt ?: throw BadRequestResponse("created at is missing") @@ -177,29 +173,24 @@ object Auth { logger.warn("We can refresh the token for ${authUser.userName}, expires = $expiresAt, refresh Till = $rtExpiresAt") val ep = getAuthEndpoint().tokenEndpoint val httpClient = HttpClient.newHttpClient() - val req = HttpRequest.newBuilder() - .uri(URI.create(ep)) - .POST( - HttpRequest.BodyPublishers.ofString( - getFormDataAsString( - mapOf( - "refresh_token" to foundOldAt.refreshToken, - "redirect_uri" to redirectUri, - "client_id" to client, - "grant_type" to "refresh_token", - ) + val req = HttpRequest.newBuilder().uri(URI.create(ep)).POST( + HttpRequest.BodyPublishers.ofString( + getFormDataAsString( + mapOf( + "refresh_token" to foundOldAt.refreshToken, + "redirect_uri" to redirectUri, + "client_id" to client, + "grant_type" to "refresh_token", ) ) ) - .header("Content-Type", "application/x-www-form-urlencoded") - .build() + ).header("Content-Type", "application/x-www-form-urlencoded").build() val message = httpClient.send(req, HttpResponse.BodyHandlers.ofString()).body() val atResponse = objectMapper.readValue(message) val parsed = validateAuthToken(atResponse.accessToken) Session.redis.lpush( - "AUTH_TOKEN_${parsed.userName}", - objectMapper.writeValueAsString( + "AUTH_TOKEN_${parsed.userName}", objectMapper.writeValueAsString( atResponse.copy(createdAt = LocalDateTime.now()) ) ) @@ -221,11 +212,7 @@ object Auth { } data class AuthUser( - val userName: String, - val tenant: String, - val roles: List, - val token: String, - val expiry: LocalDateTime + val userName: String, val tenant: String, val roles: List, val token: String, val expiry: LocalDateTime, val plantIds: List ) enum class Action { diff --git a/src/main/kotlin/com/restapi/domain/db.kt b/src/main/kotlin/com/restapi/domain/db.kt index e5a1b09..d4b4456 100644 --- a/src/main/kotlin/com/restapi/domain/db.kt +++ b/src/main/kotlin/com/restapi/domain/db.kt @@ -39,7 +39,7 @@ object Session { private val logger = LoggerFactory.getLogger("session") private val currentUser = object : ThreadLocal() { override fun initialValue(): AuthUser { - return AuthUser("", "", emptyList(), "", LocalDateTime.now()) + return AuthUser("", "", emptyList(), "", LocalDateTime.now(), emptyList()) } } @@ -182,6 +182,13 @@ object Session { fun currentTenant() = currentUser.get().tenant fun currentRoles() = currentUser.get().roles fun currentToken() = currentUser.get().token + fun currentUserPlants() = currentUser.get().plantIds.map { + Session.database.find(Plant::class.java) + .where() + .eq("plantId", it) + .findOne() + }.filterNotNull() + fun jwk() = keypair.toParams(JsonWebKey.OutputControlLevel.PUBLIC_ONLY) fun Database.findDataModelByEntityAndUniqId(entity: String, uniqId: String): DataModel { diff --git a/src/main/kotlin/com/restapi/domain/models.kt b/src/main/kotlin/com/restapi/domain/models.kt index dde69e4..3788545 100644 --- a/src/main/kotlin/com/restapi/domain/models.kt +++ b/src/main/kotlin/com/restapi/domain/models.kt @@ -14,11 +14,7 @@ import javax.persistence.* data class Comments(val text: String = "", val by: String = "", val at: LocalDateTime = LocalDateTime.now()) data class POProducts( - val productId: String = "", - val productName: String = "", - val unitPrice: Double = 0.0, - val quantity: Double = 0.0, - val description: String = "" + val productId: String = "", val productName: String = "", val unitPrice: Double = 0.0, val quantity: Double = 0.0, val description: String = "" ) @@ -112,15 +108,13 @@ open class AuditLog : BaseTenantModel() { @DbJsonB @Index( - definition = "create index audit_log_values_idx on audit_log using GIN (data)", - platforms = [Platform.POSTGRES] + definition = "create index audit_log_values_idx on audit_log using GIN (data)", platforms = [Platform.POSTGRES] ) var data: Map = hashMapOf() @DbJsonB @Index( - definition = "create index audit_log_changes_idx on audit_log using GIN (changes)", - platforms = [Platform.POSTGRES] + definition = "create index audit_log_changes_idx on audit_log using GIN (changes)", platforms = [Platform.POSTGRES] ) var changes: Map = hashMapOf() } @@ -545,7 +539,7 @@ open class Fleet : BaseTenantModel() { var name: String = "" var type: String = "" var regNumber: String = "" - var regDate: LocalDate?=null + var regDate: LocalDate? = null var model: String = "" var make: String = "" var driver: String = "" @@ -635,4 +629,11 @@ open class ReminderLog : BaseTenantModel() { @DbArray var documents: List? = null +} + +@Entity +@Index(name="plantid_idx", columnNames = ["plant_id"], unique = true) +open class Plant : BaseModel() { + var plantId: String = "" + var plantName: String = "" } \ No newline at end of file