revet-iam
Revet IAM Library
Identity and access management for Kotlin/Quarkus applications. Provides permission evaluation, user/group management, service accounts, and SCIM 2.0 provisioning.
Dependency Coordinates
Group ID: com.revethq.iam
Version: 0.1.16
Modules
| Artifact | Purpose |
|---|---|
revet-permission |
Policy/Statement model, URN parsing, policy evaluation, permission discovery |
revet-permission-web |
JAX-RS REST API for policy management, /.well-known/revet-permissions discovery endpoint |
revet-permission-persistence-runtime |
Hibernate Panache persistence for policies |
revet-user |
User/Group/Profile domain models |
revet-user-web |
JAX-RS REST API for user/group management |
revet-user-persistence-runtime |
Hibernate Panache persistence for users/groups |
revet-service-account |
ServiceAccount domain model |
revet-service-account-persistence |
Hibernate Panache persistence for service accounts |
revet-service-account-web |
JAX-RS REST API for service account management |
revet-scim |
SCIM 2.0 User/Group provisioning endpoints |
Gradle
implementation("com.revethq.iam:revet-permission:0.1.16")
implementation("com.revethq.iam:revet-user:0.1.16")
implementation("com.revethq.iam:revet-service-account:0.1.16")
implementation("com.revethq.iam:revet-scim:0.1.16")
Maven
<dependency>
<groupId>com.revethq.iam</groupId>
<artifactId>revet-permission</artifactId>
<version>0.1.16</version>
</dependency>
Core Concepts
URN Format
Resources are identified by URNs:
urn:{namespace}:{service}:{tenant}:{resourceType}/{resourceId}
Examples:
urn:revet:iam:acme-corp:user/aliceurn:revet:iam:acme-corp:service-account/550e8400-e29b-41d4-a716-446655440000
Components:
namespace- Organization namespace (e.g.,revet)service- Service identifier (e.g.,iam,documents)tenant- Tenant/organization identifier (empty for global)resourceType- Resource category (e.g.,user,group,policy)resourceId- Unique resource identifier
Policy Model
Policies contain statements that grant or deny permissions:
val policy = Policy(
id = UUID.randomUUID(),
name = "user-management-policy",
version = "2026-01-15",
statements = listOf(
Statement(
effect = Effect.ALLOW,
actions = listOf("iam:CreateUser", "iam:UpdateUser"),
resources = listOf("urn:revet:iam:acme-corp:user/*")
)
),
tenantId = "acme-corp"
)
Authorization Decision Rules
- Explicit DENY - Any matching Deny statement → DENY (highest precedence)
- Allow - Any matching Allow statement (no Deny) → ALLOW
- Implicit DENY - No statements match → DENY (default)
Related Documentation
- permissions.md - URN format, Policy/Statement classes, condition evaluation
- users.md - User/Group/Profile data classes, service interfaces
- service-accounts.md - ServiceAccount domain, persistence, REST API
- scim.md - SCIM 2.0 DTOs, endpoint contracts, filter grammar
Permission Discovery
Downstream applications can advertise all permissions they understand at a well-known endpoint. This aggregates permissions declared by the application itself and all of its library dependencies.
Declaring Permissions
Implement PermissionProvider as an @ApplicationScoped CDI bean to declare your service's permissions. The revet-permission module contains the interface, so any module can implement it without depending on revet-permission-web.
import com.revethq.iam.permission.discovery.PermissionDeclaration
import com.revethq.iam.permission.discovery.PermissionManifest
import com.revethq.iam.permission.discovery.PermissionProvider
import jakarta.enterprise.context.ApplicationScoped
@ApplicationScoped
class BillingPermissionProvider : PermissionProvider {
override fun manifest() = PermissionManifest(
service = "billing",
permissions = listOf(
PermissionDeclaration(
action = "billing:CreateInvoice",
description = "Create an invoice",
resourceType = "urn:revet:billing:{tenantId}:invoice/{invoiceId}",
),
PermissionDeclaration(
action = "billing:GetInvoice",
description = "Retrieve an invoice by ID",
resourceType = "urn:revet:billing:{tenantId}:invoice/{invoiceId}",
),
),
)
}
Each PermissionDeclaration has:
action(required) — follows the{service}:{action}format (e.g.,billing:CreateInvoice)description(optional) — human-readable explanationresourceType(optional) — URN template showing the applicable resource pattern
How Aggregation Works
When the application starts, CDI discovers all PermissionProvider beans on the classpath — including those from transitive library dependencies (via Jandex indexes). The PermissionRegistry collects them automatically. No manual registration is needed.
For example, if your app depends on revet-permission-web (which brings in IAM permissions) and also declares its own BillingPermissionProvider, both sets of permissions are aggregated.
Discovery Endpoint
Applications that include revet-permission-web automatically expose:
GET /.well-known/revet-permissions
This returns all aggregated permissions grouped by service:
{
"manifests": [
{
"service": "iam",
"permissions": [
{
"action": "iam:CreateUser",
"description": "Create a new user",
"resourceType": "urn:revet:iam:{tenantId}:user/{userId}"
}
]
},
{
"service": "billing",
"permissions": [
{
"action": "billing:CreateInvoice",
"description": "Create an invoice",
"resourceType": "urn:revet:billing:{tenantId}:invoice/{invoiceId}"
}
]
}
]
}
Programmatic Access
Inject PermissionRegistry to access declared permissions in code:
@Inject
lateinit var permissionRegistry: PermissionRegistry
// All manifests grouped by service
val manifests = permissionRegistry.allManifests()
// Flat list of all permission declarations
val allPermissions = permissionRegistry.allPermissions()
Extension Points
PolicyCollector
Implement to customize policy retrieval:
@ApplicationScoped
class CustomPolicyCollector : PolicyCollector {
override fun collectPolicies(principalUrn: String): List<Policy> {
// Fetch from external IAM, apply caching, filter by tenant
}
}
PolicyEvaluator
Implement for custom authorization logic:
@ApplicationScoped
class CustomPolicyEvaluator : PolicyEvaluator {
override fun evaluate(request: AuthorizationRequest): AuthorizationResult {
// Custom ABAC, audit logging, external service integration
}
}
Key Constraints
- Policies must have at least one statement
- Statements must have at least one action and one resource
- Policy names are unique per tenant
tenantId == nullindicates global policies- Wildcard
*matches single path segment;**matches hierarchical paths - Action format:
{service}:{action}(e.g.,iam:CreateUser)