Skip to content

Commit 586513b

Browse files
committed
feat(model-client): added IModelClientV2.loadVersion(RepositoryId ...)
Reading a version should always be done in the context of a repository so that we can check the permissions. If the version hash is leaked somewhere it still shouldn't be possible to access the content. The permission check is not implemented yet, but introducing the method now makes it easier to enfore the permission in the future.
1 parent f62380f commit 586513b

File tree

3 files changed

+27
-42
lines changed

3 files changed

+27
-42
lines changed

model-client/src/commonMain/kotlin/org/modelix/model/client2/IModelClientV2.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,11 @@ interface IModelClientV2 {
4343

4444
suspend fun listBranches(repository: RepositoryId): List<BranchReference>
4545

46+
@Deprecated("repository ID is required for permission checks")
4647
suspend fun loadVersion(versionHash: String, baseVersion: IVersion?): IVersion
4748

49+
suspend fun loadVersion(repositoryId: RepositoryId, versionHash: String, baseVersion: IVersion?): IVersion
50+
4851
/**
4952
* The pushed version is merged automatically by the server with the current head.
5053
* The merge result is returned.

model-client/src/commonMain/kotlin/org/modelix/model/client2/ModelClientV2.kt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ class ModelClientV2(
122122
}.bodyAsText().lines().map { repository.getBranchReference(it) }
123123
}
124124

125+
@Deprecated("repository ID is required for permission checks")
125126
override suspend fun loadVersion(versionHash: String, baseVersion: IVersion?): IVersion {
126127
val response = httpClient.post {
127128
url {
@@ -133,7 +134,25 @@ class ModelClientV2(
133134
}
134135
}
135136
val delta = Json.decodeFromString<VersionDelta>(response.bodyAsText())
136-
return createVersion(null, delta)
137+
return createVersion(baseVersion as CLVersion?, delta)
138+
}
139+
140+
override suspend fun loadVersion(
141+
repositoryId: RepositoryId,
142+
versionHash: String,
143+
baseVersion: IVersion?,
144+
): IVersion {
145+
val response = httpClient.post {
146+
url {
147+
takeFrom(baseUrl)
148+
appendPathSegments("repositories", repositoryId.id, "versions", versionHash)
149+
if (baseVersion != null) {
150+
parameters["lastKnown"] = (baseVersion as CLVersion).getContentHash()
151+
}
152+
}
153+
}
154+
val delta = Json.decodeFromString<VersionDelta>(response.bodyAsText())
155+
return createVersion(baseVersion as CLVersion?, delta)
137156
}
138157

139158
override suspend fun push(branch: BranchReference, version: IVersion, baseVersion: IVersion?): IVersion {

model-server/src/main/kotlin/org/modelix/model/server/handlers/ModelReplicationServer.kt

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ class ModelReplicationServer(val repositoriesManager: RepositoriesManager) {
215215
}
216216
route("versions") {
217217
get("{versionHash}") {
218+
// TODO versions should be stored inside a repository with permission checks.
219+
// Knowing a version hash should not give you access to the content.
220+
// This handler was already moved to the 'repositories' route. Removing it here would be a breaking
221+
// change, but should be done in some future version.
218222
val baseVersionHash = call.request.queryParameters["lastKnown"]
219223
val versionHash = call.parameters["versionHash"]!!
220224
if (storeClient[versionHash] == null) {
@@ -230,47 +234,6 @@ class ModelReplicationServer(val repositoriesManager: RepositoriesManager) {
230234
TODO()
231235
}
232236
}
233-
route("objects") {
234-
post {
235-
val values = call.receive<List<String>>()
236-
storeClient.putAll(values.associateBy { HashUtil.sha256(it) }, true)
237-
call.respondText("OK")
238-
}
239-
get("{hash}") {
240-
val key = call.parameters["hash"]!!
241-
val value = storeClient[key]
242-
if (value == null) {
243-
call.respondText("object '$key' not found", status = HttpStatusCode.NotFound)
244-
} else {
245-
call.respondText(value)
246-
}
247-
}
248-
}
249-
route("modelql") {
250-
put {
251-
val params = call.receiveParameters()
252-
val queryFromClient = params["query"]
253-
if (queryFromClient == null) {
254-
call.respondText(text = "'query' is missing", status = HttpStatusCode.BadRequest)
255-
return@put
256-
}
257-
val query = ModelQuery.fromJson(queryFromClient)
258-
val json = query.toJson()
259-
val hash = HashUtil.sha256(json)
260-
storeClient.put(hash, json)
261-
call.respondText(text = hash)
262-
}
263-
get("{hash}") {
264-
val hash = call.parameters["hash"]!!
265-
val json = storeClient[hash]
266-
if (json == null) {
267-
call.respondText(status = HttpStatusCode.NotFound, text = "ModelQL with hash '$hash' doesn't exist")
268-
return@get
269-
}
270-
ModelQuery.fromJson(json) // ensure it's a valid ModelQuery
271-
call.respondText(json, ContentType.Application.Json)
272-
}
273-
}
274237
}
275238

276239
private suspend fun ApplicationCall.respondDelta(versionHash: String, baseVersionHash: String?) {

0 commit comments

Comments
 (0)