Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import io.ktor.server.auth.principal
import io.ktor.server.request.header
import io.ktor.server.routing.Route
import io.ktor.util.pipeline.PipelineContext
import io.ktor.server.routing.RoutingContext
import org.modelix.authorization.permissions.PermissionEvaluator
import org.modelix.authorization.permissions.PermissionInstanceReference
import org.modelix.authorization.permissions.PermissionParts
Expand All @@ -35,7 +35,7 @@
}
}

fun PipelineContext<*, ApplicationCall>.checkPermission(permissionParts: PermissionParts) {
fun RoutingContext.checkPermission(permissionParts: PermissionParts) {

Check warning

Code scanning / detekt

The function checkPermission is missing documentation. Warning

The function checkPermission is missing documentation.
call.checkPermission(permissionParts)
}

Expand Down Expand Up @@ -83,7 +83,7 @@

fun ApplicationCall.jwt() = principal<AccessTokenPrincipal>()?.jwt ?: jwtFromHeaders()

fun PipelineContext<Unit, ApplicationCall>.getUserName(): String? {
fun RoutingContext.getUserName(): String? {

Check warning

Code scanning / detekt

The function getUserName is missing documentation. Warning

The function getUserName is missing documentation.
return call.getUserName()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
import io.ktor.server.response.respond
import io.ktor.server.response.respondRedirect
import io.ktor.server.routing.Route
import io.ktor.server.routing.RoutingContext
import io.ktor.server.routing.application
import io.ktor.server.routing.get
import io.ktor.server.routing.post
import io.ktor.server.routing.route
import io.ktor.util.pipeline.PipelineContext
import kotlinx.html.FlowContent
import kotlinx.html.a
import kotlinx.html.body
Expand Down Expand Up @@ -83,7 +84,7 @@
}
}
route("{resourceId}") {
fun PipelineContext<*, ApplicationCall>.resourceId(): String = call.parameters["resourceId"]!!
fun RoutingContext.resourceId(): String = call.parameters["resourceId"]!!

Check warning

Code scanning / detekt

Calling !! on a nullable type will throw a NullPointerException at runtime in case the value is null. It should be avoided. Warning

Calling !! on a nullable type will throw a NullPointerException at runtime in case the value is null. It should be avoided.
get("/") {
val resourceId = resourceId()
val plugin = call.application.plugin(ModelixAuthorization)
Expand Down Expand Up @@ -240,8 +241,8 @@
}
route("permissions") {
route("{permissionName}") {
fun PipelineContext<*, ApplicationCall>.permissionName(): String = call.parameters["permissionName"]!!
fun PipelineContext<*, ApplicationCall>.permissionId(): String = "${resourceId()}/${permissionName()}"
fun RoutingContext.permissionName(): String = call.parameters["permissionName"]!!

Check warning

Code scanning / detekt

Calling !! on a nullable type will throw a NullPointerException at runtime in case the value is null. It should be avoided. Warning

Calling !! on a nullable type will throw a NullPointerException at runtime in case the value is null. It should be avoided.
fun RoutingContext.permissionId(): String = "${resourceId()}/${permissionName()}"
post("grant") {
val formParameters = call.receiveParameters()
grant(formParameters["userId"], formParameters["roleId"], permissionId())
Expand Down Expand Up @@ -300,34 +301,34 @@
}
}

private fun PipelineContext<*, ApplicationCall>.grant(userId: String?, roleId: String?, permissionId: String) {
private fun RoutingContext.grant(userId: String?, roleId: String?, permissionId: String) {
val userId = userId?.takeIf { it.isNotBlank() }
val roleId = roleId?.takeIf { it.isNotBlank() }
require(userId != null || roleId != null) { "userId or roleId required" }
call.checkCanGranPermission(permissionId)

if (userId != null) {
application.plugin(ModelixAuthorization).config.accessControlPersistence.update {
call.application.plugin(ModelixAuthorization).config.accessControlPersistence.update {
it.withGrantToUser(userId, permissionId)
}
}
if (roleId != null) {
application.plugin(ModelixAuthorization).config.accessControlPersistence.update {
call.application.plugin(ModelixAuthorization).config.accessControlPersistence.update {
it.withGrantToRole(roleId, permissionId)
}
}
}

private fun PipelineContext<*, ApplicationCall>.removeGrant(userId: String?, roleId: String?, permissionId: String) {
private fun RoutingContext.removeGrant(userId: String?, roleId: String?, permissionId: String) {
require(userId != null || roleId != null) { "userId or roleId required" }
call.checkCanGranPermission(permissionId)
if (userId != null) {
application.plugin(ModelixAuthorization).config.accessControlPersistence.update {
call.application.plugin(ModelixAuthorization).config.accessControlPersistence.update {
it.withoutGrantToUser(userId, permissionId)
}
}
if (roleId != null) {
application.plugin(ModelixAuthorization).config.accessControlPersistence.update {
call.application.plugin(ModelixAuthorization).config.accessControlPersistence.update {
it.withoutGrantToUser(roleId, permissionId)
}
}
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ docker-compose = { id = "com.avast.gradle.docker-compose" , version = "0.17.12"
[versions]
kotlin = "2.1.0"
kotlinCoroutines="1.9.0"
ktor="2.3.12"
ktor="3.0.3"
kotlinHtml="0.8.0"
kotlinSerialization="1.7.3"
ignite="2.16.0"
Expand Down
44 changes: 1 addition & 43 deletions kotlin-js-store/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,6 @@ abab@^2.0.6:
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==

[email protected]:
version "3.0.0"
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
dependencies:
event-target-shim "^5.0.0"

acorn-globals@^7.0.0:
version "7.0.1"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3"
Expand Down Expand Up @@ -336,11 +329,6 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==

event-target-shim@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==

fast-levenshtein@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
Expand Down Expand Up @@ -650,13 +638,6 @@ ms@^2.1.3:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==

[email protected]:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"

normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
Expand Down Expand Up @@ -862,11 +843,6 @@ tr46@^3.0.0:
dependencies:
punycode "^2.1.1"

tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==

tslib@^1.11.1:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
Expand Down Expand Up @@ -914,11 +890,6 @@ w3c-xmlserializer@^3.0.0:
dependencies:
xml-name-validator "^4.0.0"

webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==

webidl-conversions@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
Expand All @@ -944,14 +915,6 @@ whatwg-url@^11.0.0:
tr46 "^3.0.0"
webidl-conversions "^7.0.0"

whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"

word-wrap@~1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
Expand All @@ -976,12 +939,7 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==

[email protected]:
version "8.5.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f"
integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==

ws@^8.17.1:
[email protected], ws@^8.17.1:
version "8.18.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ type ReplicatedModelJS = org.modelix.model.client2.ReplicatedModelJS
let client: ClientJS | undefined;
let replicatedModel: ReplicatedModelJS | undefined;

jest.setTimeout(60000)

beforeEach(async () => {
const repositoryId = randomUUID()
client = await connectClient(MODEL_SERVER_URL)
Expand All @@ -33,7 +35,7 @@ test("replicated model uses user set in client", async () => {
const versionInformation = await replicatedModel!.getCurrentVersionInformation()
const lastAuthor = versionInformation.author
expect(lastAuthor).toBe(userId);
});
}, );

test("replicated model returns user set in client", async () => {
const versionInformation = await replicatedModel!.getCurrentVersionInformation()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import io.ktor.util.reflect.TypeInfo
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.charsets.Charset
import io.ktor.utils.io.core.readText
import io.ktor.utils.io.readRemaining
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -53,25 +54,6 @@ import java.net.URLEncoder
import java.nio.charset.StandardCharsets
import java.util.LinkedList
import java.util.concurrent.atomic.AtomicInteger
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.collections.Iterable
import kotlin.collections.LinkedHashMap
import kotlin.collections.List
import kotlin.collections.Map
import kotlin.collections.MutableList
import kotlin.collections.MutableMap
import kotlin.collections.Set
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.emptyList
import kotlin.collections.emptySet
import kotlin.collections.filter
import kotlin.collections.forEach
import kotlin.collections.iterator
import kotlin.collections.minus
import kotlin.collections.plus
import kotlin.collections.set
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
Expand Down Expand Up @@ -157,8 +139,8 @@ class RestWebModelClient @JvmOverloads constructor(
contentType: ContentType,
charset: Charset,
typeInfo: TypeInfo,
value: Any,
): OutgoingContent {
value: Any?,
): OutgoingContent? {
return TextContent(value.toString(), contentType)
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package org.modelix.model.server.api.v2

import io.ktor.util.cio.use
import io.ktor.util.toByteArray
import io.ktor.utils.io.ByteChannel
import io.ktor.utils.io.toByteArray
import io.ktor.utils.io.writeFully
import io.ktor.utils.io.writeStringUtf8
import kotlinx.coroutines.flow.emptyFlow
Expand Down
24 changes: 10 additions & 14 deletions model-server/src/main/kotlin/org/modelix/model/server/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import io.ktor.http.HttpStatusCode
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.Application
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.call
import io.ktor.server.application.install
import io.ktor.server.engine.connector
import io.ktor.server.engine.embeddedServer
import io.ktor.server.http.content.staticResources
import io.ktor.server.netty.Netty
import io.ktor.server.netty.NettyApplicationEngine
import io.ktor.server.plugins.callloging.CallLogging
import io.ktor.server.plugins.callloging.processingTimeMillis
import io.ktor.server.plugins.calllogging.CallLogging
import io.ktor.server.plugins.calllogging.processingTimeMillis
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.plugins.cors.routing.CORS
import io.ktor.server.plugins.forwardedheaders.ForwardedHeaders
Expand All @@ -27,7 +26,6 @@ import io.ktor.server.request.path
import io.ktor.server.resources.Resources
import io.ktor.server.response.respondText
import io.ktor.server.routing.IgnoreTrailingSlash
import io.ktor.server.routing.Routing
import io.ktor.server.routing.get
import io.ktor.server.routing.routing
import io.ktor.server.websocket.WebSockets
Expand Down Expand Up @@ -69,9 +67,9 @@ import java.io.File
import java.io.FileReader
import java.io.IOException
import java.nio.charset.StandardCharsets
import java.time.Duration
import java.util.Properties
import javax.sql.DataSource
import kotlin.time.Duration.Companion.seconds

object Main {
private val LOG = LoggerFactory.getLogger(Main::class.java)
Expand Down Expand Up @@ -173,12 +171,10 @@ object Main {
val modelReplicationServer = ModelReplicationServer(repositoriesManager)
val metricsApi = MetricsApiImpl()

val configureNetty: NettyApplicationEngine.Configuration.() -> Unit = {
this.responseWriteTimeoutSeconds = cmdLineArgs.responseWriteTimeoutSeconds
}

val ktorServer: NettyApplicationEngine = embeddedServer(Netty, port = port, configure = configureNetty) {
install(Routing)
val ktorServer = embeddedServer(Netty, configure = {
connector { this.port = port }
responseWriteTimeoutSeconds = cmdLineArgs.responseWriteTimeoutSeconds
}) {
install(ModelixAuthorization) {
permissionSchema = ModelServerPermissionSchema.SCHEMA
installStatusPages = false
Expand All @@ -205,8 +201,8 @@ object Main {
// https://opensource.zalando.com/restful-api-guidelines/#136
install(IgnoreTrailingSlash)
install(WebSockets) {
pingPeriod = Duration.ofSeconds(30)
timeout = Duration.ofSeconds(30)
pingPeriod = 30.seconds
timeout = 30.seconds
maxFrameSize = Long.MAX_VALUE
masking = false
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package org.modelix.model.server.handlers

import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.call
import io.ktor.server.response.respond
import io.ktor.util.pipeline.PipelineContext
import io.ktor.server.routing.RoutingContext
import org.modelix.model.server.MODELIX_VERSION

/**
* Responding information about the model server.
*/
object AboutApiImpl : AboutApi() {
override suspend fun PipelineContext<Unit, ApplicationCall>.getAboutInformationV1() {
override suspend fun RoutingContext.getAboutInformationV1() {
val about = AboutV1(MODELIX_VERSION)
call.respond(about)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ package org.modelix.model.server.handlers

import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.call
import io.ktor.server.response.respondText
import io.ktor.util.pipeline.PipelineContext
import io.ktor.server.routing.RoutingContext
import org.modelix.model.server.handlers.KeyValueLikeModelServer.Companion.PROTECTED_PREFIX
import org.modelix.model.server.store.RequiresTransaction
import org.modelix.model.server.store.StoreManager
Expand All @@ -16,7 +14,7 @@ class HealthApiImpl(

private val stores: StoreManager get() = repositoriesManager.stores

override suspend fun PipelineContext<Unit, ApplicationCall>.getHealth() {
override suspend fun RoutingContext.getHealth() {
if (isHealthy()) {
call.respondText(text = "healthy", contentType = ContentType.Text.Plain, status = HttpStatusCode.OK)
} else {
Expand Down
Loading
Loading