Skip to content

Commit 4868775

Browse files
authored
Merge branch 'main' into dependabot/gradle/kotlinCoroutines-1.7.1
2 parents 6d8a9f3 + 5f2db42 commit 4868775

File tree

174 files changed

+3192
-51442
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

174 files changed

+3192
-51442
lines changed

authorization/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ tasks.getByName<Test>("test") {
2222
useJUnitPlatform()
2323
}
2424

25+
java {
26+
toolchain {
27+
languageVersion.set(JavaLanguageVersion.of(11))
28+
}
29+
}
30+
2531
publishing {
2632
publications {
2733
create<MavenPublication>("maven") {

authorization/src/main/kotlin/org/modelix/authorization/KeycloakUtils.kt

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import com.auth0.jwk.JwkProviderBuilder
1818
import com.auth0.jwt.JWT
1919
import com.auth0.jwt.interfaces.DecodedJWT
2020
import com.google.common.cache.CacheBuilder
21+
import org.keycloak.authorization.client.AuthorizationDeniedException
2122
import org.keycloak.authorization.client.AuthzClient
2223
import org.keycloak.authorization.client.Configuration
2324
import org.keycloak.authorization.client.resource.ProtectedResource
@@ -56,7 +57,7 @@ object KeycloakUtils {
5657

5758
private val permissionCache = CacheBuilder.newBuilder()
5859
.expireAfterWrite(30, TimeUnit.SECONDS)
59-
.build<String, List<Permission>>()
60+
.build<Pair<Pair<DecodedJWT, KeycloakResource>, KeycloakScope>, Boolean>()
6061
private val existingResources = CacheBuilder.newBuilder()
6162
.expireAfterWrite(3, TimeUnit.MINUTES)
6263
.build<String, ResourceRepresentation>()
@@ -90,44 +91,61 @@ object KeycloakUtils {
9091
}
9192
}
9293

93-
@Synchronized
94-
fun getPermissions(accessToken: DecodedJWT): List<Permission> {
95-
return permissionCache.get(accessToken.token) { queryPermissions(accessToken) }
96-
}
97-
9894
fun getServiceAccountToken(): DecodedJWT {
9995
return JWT.decode(authzClient.obtainAccessToken().token)
10096
}
10197

102-
private fun queryPermissions(token: DecodedJWT): List<Permission> {
98+
private fun isAccessToken(token: DecodedJWT): Boolean {
99+
val authClaim = token.getClaim("authorization")
100+
return !(authClaim.isNull || authClaim.isMissing)
101+
}
102+
103+
private fun readPermissions(token: DecodedJWT): List<Permission> {
104+
require(isAccessToken(token)) { "Not an access token: ${token.token}" }
103105
try {
104-
val rpt = if (token.getClaim("authorization").isNull) {
105-
// The token was probably created by the OAuth proxy after login.
106-
// The token contains no permission information yet. Query them from keycloak.
107-
authzClient.authorization(token.token).authorize(AuthorizationRequest()).token
108-
} else {
109-
// The token was probably created using KeycloakUtils.createToken
110-
token.token
111-
}
106+
val rpt = token.token
112107
val introspect = authzClient.protection().introspectRequestingPartyToken(rpt)
113-
return introspect.permissions
108+
return introspect.permissions ?: emptyList()
114109
} catch (e: Exception) {
115110
throw RuntimeException("Can't get permissions for token: ${token.token}", e)
116111
}
117112
}
118113

114+
private fun createAccessToken(identityToken: DecodedJWT, permissions: List<Pair<String, List<String>>>): DecodedJWT {
115+
return JWT.decode(authzClient.authorization(identityToken.token).authorize(AuthorizationRequest().also {
116+
for (permission in permissions) {
117+
it.addPermission(permission.first, permission.second)
118+
}
119+
}).token)
120+
}
121+
119122
@Synchronized
120-
fun hasPermission(accessToken: DecodedJWT, resourceSpec: KeycloakResource, scope: KeycloakScope): Boolean {
121-
ensureResourcesExists(resourceSpec, accessToken)
122-
val allPermissions = getPermissions(accessToken)
123-
val forResource = allPermissions.filter { it.resourceName == resourceSpec.name }
124-
if (forResource.isEmpty()) return false
125-
val scopes: Set<String> = forResource.mapNotNull { it.scopes }.flatten().toSet()
126-
if (scopes.isEmpty()) {
127-
// If the permissions are not restricted to any scope we assume they are valid for all scopes.
128-
return true
123+
fun hasPermission(identityOrAccessToken: DecodedJWT, resourceSpec: KeycloakResource, scope: KeycloakScope): Boolean {
124+
val key = identityOrAccessToken to resourceSpec to scope
125+
return permissionCache.get(key) { checkPermission(identityOrAccessToken, resourceSpec, scope) }
126+
}
127+
128+
private fun checkPermission(identityOrAccessToken: DecodedJWT, resourceSpec: KeycloakResource, scope: KeycloakScope): Boolean {
129+
ensureResourcesExists(resourceSpec, identityOrAccessToken)
130+
131+
if (isAccessToken(identityOrAccessToken)) {
132+
val grantedPermissions = readPermissions(identityOrAccessToken)
133+
val forResource = grantedPermissions.filter { it.resourceName == resourceSpec.name }
134+
if (forResource.isEmpty()) return false
135+
val scopes: Set<String> = forResource.mapNotNull { it.scopes }.flatten().toSet()
136+
if (scopes.isEmpty()) {
137+
// If the permissions are not restricted to any scope we assume they are valid for all scopes.
138+
return true
139+
}
140+
return scopes.contains(scope.name)
141+
} else {
142+
return try {
143+
createAccessToken(identityOrAccessToken, listOf(resourceSpec.name to listOf(scope.name)))
144+
true
145+
} catch (_: AuthorizationDeniedException) {
146+
false
147+
}
129148
}
130-
return scopes.contains(scope.name)
131149
}
132150

133151
@Synchronized

authorization/src/main/kotlin/org/modelix/authorization/KtorAuthUtils.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,6 @@ fun Application.installAuthentication(unitTestMode: Boolean = false) {
114114
|Validation result: $validationError
115115
|
116116
|$claims
117-
|
118-
|Permissions:
119-
|${KeycloakUtils.getPermissions(jwt).joinToString("\n") { " $it" }}
120117
|""".trimMargin())
121118
}
122119
}

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ plugins {
1919
alias(libs.plugins.ktlint) apply false
2020
alias(libs.plugins.spotless) apply false
2121
alias(libs.plugins.tasktree)
22-
id("org.jetbrains.dokka") version "1.8.10"
22+
id("org.jetbrains.dokka") version "1.8.20"
2323
}
2424

2525
repositories {

docker-build-model.sh

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@
33
set -e
44

55
TAG=$( ./modelix-version.sh )
6-
MODELIX_TARGET_PLATFORM="${MODELIX_TARGET_PLATFORM:=linux/amd64}"
76

87
(
98
cd model-server
109
if [ "${CI}" = "true" ]; then
11-
docker buildx build --platform linux/amd64,linux/arm64 --push --no-cache \
10+
docker buildx build --platform linux/amd64,linux/arm64 --push \
1211
-t modelix/modelix-model:latest -t "modelix/modelix-model:${TAG}" .
1312
else
14-
docker build --platform "${MODELIX_TARGET_PLATFORM}" --no-cache \
15-
-t modelix/modelix-model:latest -t "modelix/modelix-model:${TAG}" .
13+
docker build -t modelix/modelix-model:latest -t "modelix/modelix-model:${TAG}" .
1614
fi
1715
)
1816

165 KB
Loading
165 KB
Loading
97.5 KB
Loading

docs/global/modules/core/pages/reference/component-metamodel-export.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ https://github.com/modelix/modelix.core[Repository^] | https://github.com/modeli
1111

1212
In order to export a meta-model (i.e. the structure aspect of a langauge) from MPS, modelix uses the MPS solution `metamodel-export`.
1313
This solution maps the structure of a language to corresponding serializable data classes of the https://api.modelix.org/2.3.0/model-api/org.modelix.model.data/index.html[`model-api`] (e.g. `LanguageData` and `ConcepData`).
14-
Further, the `metamodel-expor` provides a command line interface to export these data classes to a JSON file from outside of MPS.
14+
Further, the `metamodel-export` provides a command line interface to export these data classes to a JSON file from outside of MPS.
1515

1616

1717

docs/global/modules/core/pages/reference/component-model-api-gen-gradle.adoc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
= Gradle Plugin for the Model API Generator
22
:navtitle: `model-api-gen-gradle`
33

4-
54
:tip-caption: 🔗 Quick Links
65
[TIP]
76
--
@@ -176,6 +175,10 @@ Inside the `metamodel` block a `names` block can be declared to configure `prefi
176175
|"\_C_TypedImpl_"
177176
|""
178177

178+
|`conceptTypeAlias`
179+
|"CN_"
180+
|""
181+
179182
|===
180183

181184
== Example

0 commit comments

Comments
 (0)