Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
444b586
feat(calm-hub): add new profile to run calmhub behind sidecar proxy a…
willosborne May 15, 2026
ff70fd8
docs(calm-hub): improve README for proxy mode
willosborne May 18, 2026
d930acb
Merge branch 'main' into calmhub-proxy-auth-mode
willosborne May 18, 2026
94bcef2
feat(calm-hub): update default header to more standard Remote-User
willosborne May 18, 2026
5642442
Merge branch 'main' into calmhub-proxy-auth-mode
rocketstack-matt May 18, 2026
3f4eb83
fix(calm-hub): minor pr fixes
willosborne May 19, 2026
9c5df42
feat(calm-hub): temp commit of auth writeups
willosborne May 20, 2026
e12436d
feat(calm-hub): auth spike work
willosborne May 20, 2026
dacfd33
feat(calm-hub): more experiments
willosborne May 21, 2026
a27ec38
fix(calm-hub): fix auth issues
willosborne May 22, 2026
cceb9ab
feat(calm-hub): fix up roles
willosborne May 22, 2026
eec272e
feat(calm-hub): do some annotations work
willosborne May 22, 2026
f7b4d37
feat(calm-hub): rework entitlements to be across all resource types
willosborne May 26, 2026
7d85c21
feat(calm-hub): add annotations to mcp tools
willosborne May 28, 2026
586820a
feat(calm-hub): public read setting to make read default
willosborne May 28, 2026
95c9b4b
feat(calm-hub): remove resource type from user access db model
willosborne May 28, 2026
abadffb
feat(calm-hub): delete proposal documents and rewrite PERMISSIONS.md
willosborne May 28, 2026
321cbbb
feat(calm-hub): expand entitilements model to work with domains for c…
willosborne May 28, 2026
c8646d6
feat(calm-hub): fix tests
willosborne May 28, 2026
f60c73c
Merge branch 'main' into calmhub-auth-rewrite
willosborne May 28, 2026
d544e95
feat(calm-hub): fix fix annotations from conflicts
willosborne May 28, 2026
f790235
feat(calm-hub): fix tests
willosborne May 28, 2026
2b5e6be
feat(calm-hub): tweaks to integration tests (needs more work)
willosborne May 28, 2026
f5e56ac
feat(calm-hub): add no-auth mechanism so there's a user in no-auth mode
willosborne May 29, 2026
5085ceb
feat(calm-hub): fix integration tests and rework secure profile to no…
willosborne May 29, 2026
91ce54a
feat(cli): improve testing and move useraction to domain
willosborne May 29, 2026
dfd2c96
fix(calm-hub): testing
willosborne May 29, 2026
1c56182
feat(cli): fix mcp tests
willosborne May 29, 2026
8631fb4
feat(calm-hub): test for proxy auth profile
willosborne May 29, 2026
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
34 changes: 34 additions & 0 deletions calm-hub/PERMISSIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Structure of CalmHub permissions system

CalmHub drives its permission system from the in-memory database.
Entitlements are stored as `UserAccess` records.

## Structure of entitlements model

Entitlements are applied at a per-namespace level, at domain level for control requirements and configurations.

The available actions are the following.

| Action | Description |
|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `read` | Can read any documents of that type in the namespace. |
| `write` | Can write any documents of that type in the namespace. This includes deleting them. Note that by default resources in CalmHub are immutable, so this usually means 'create' only.
| `admin` | Can do anything to all resource types, and also grant entitlements to other users in the namespace. |

For example, `read` means the user can read all resources in that NS.

Please note that each entitlement implies all previous levels - i.e. `write` implies `read`.
`admin` implies `read` and `write` on all resource types.

## Global admin

Some resources aren't tied to any one namespace.
Creating namespaces and managing core schemas requires the `admin` role, with the namespace `GLOBAL` in the database.

## Global READ mode

It's possible to configure CalmHub to grant `read` to all users by default.

To do this, set the property `calm.hub.allow.public.read=true`.
By default this property is `false`.

41 changes: 41 additions & 0 deletions calm-hub/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ mvn -P integration verify

Development mode is designed to provide a great developer experience from using modern tools and build systems.

### Skipping `npm` build

CalmHub will install and build the frontend every time you run it by default.
This takes a while and can be rather tedious.
To disable this, set `skip.npm`, which disables the NPM commands of the frontend Maven plugin:

```shell
../mvnw quarkus:dev -Dskip.npm
```

### Storage Modes

Calm Hub supports two different storage modes:
Expand Down Expand Up @@ -128,6 +138,37 @@ From the `calm-hub` directory

### Secure profile

There are two secure profiles, `secure` and `proxy`.
Using either will enable entitlements driven by the database.

The two modes are slightly different:
- `secure`: Enables JWT-based authentication using Quarkus' OAuth 2 libraries.
- User identities will be extracted from the provided JWT which will be validated against the Authorization Server's Json Web Key Set (JWKS.)
- `proxy-auth`: Assumes CalmHub will be deployed behind an additional proxy component, such as `nginx` or `apache`, that performs OAuth 2 (or other) authentication for you.
- User identity is expected to be passed via a header; by default this is `Remote-User`.

#### Proxy profile

To launch CalmHub with the proxy auth mode enabled:

```bash
../mvnw quarkus:dev -Dquarkus.profile=proxy-auth
```

**Important notes**:
- It is strongly recommended that the sidecar runs in the same pod or container as CalmHub, and that **CalmHub should only be accessible via this proxy.**

- This is because if users can directly call CalmHub, they can simply set the header to trivially impersonate any identity.

#### Secure profile

The secure profile requires an Identity Provider (IdP) to authenticate users.
The IdP will most likely be managed by your organisation in an enterprise environment, or by your Cloud Service Provider if you're deploying on public cloud.

However, for local testing and development purposes, CalmHub includes a simple pre-configured IdP, Keycloak, that you can spin up locally to simulate a real IdP.

The following sections descibe how to start Keycloak, and how to configure CalmHub to use it correctly.

#### Launch keycloak

From the `keycloak-dev` directory in `calm-hub`
Expand Down
22 changes: 8 additions & 14 deletions calm-hub/mongo/init-mongo.js
Original file line number Diff line number Diff line change
Expand Up @@ -2500,44 +2500,38 @@ if (db.userAccess.countDocuments() === 0) {
{
"userAccessId": NumberInt(1),
"username": "demo_admin",
"permission": "write",
"namespace": "finos",
"resourceType": "all"
"permission": "admin",
"namespace": "finos"
},
{
"userAccessId": NumberInt(2),
"username": "demo_admin",
"permission": "write",
"namespace": "workshop",
"resourceType": "patterns"
"permission": "admin",
"namespace": "workshop"
},
{
"userAccessId": NumberInt(3),
"username": "demo_admin",
"permission": "read",
"namespace": "traderx",
"resourceType": "all"
"namespace": "traderx"
},
{
"userAccessId": NumberInt(4),
"username": "demo",
"permission": "read",
"namespace": "finos",
"resourceType": "all"
"namespace": "finos"
},
{
"userAccessId": NumberInt(5),
"username": "demo",
"permission": "read",
"namespace": "traderx",
"resourceType": "all"
"namespace": "traderx"
},
{
"userAccessId": NumberInt(6),
"username": "demo",
"permission": "read",
"namespace": "workshop",
"resourceType": "all"
"namespace": "workshop"
}
]);
logSuccess("Initialized user access for demo_admin and demo users");
Expand Down
5 changes: 5 additions & 0 deletions calm-hub/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@
<artifactId>quarkus-junit5-mockito</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-security</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public Map<String, String> getConfigOverrides() {
// equivalent REST PUT endpoint can be exercised by the integration suite.
return Map.of(
"allow.put.operations", "true",
"calm.mcp.enabled", "true"
"calm.mcp.enabled", "true",
"calm.hub.no.auth.enabled","true"
);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package integration;

import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTestProfile;

import java.util.Map;
import java.util.Set;

@QuarkusTestResource(EndToEndResource.class)
public class IntegrationTestProxyAuthProfile implements QuarkusTestProfile {

@Override
public Set<Class<?>> getEnabledAlternatives() {
return Set.of();
}

@Override
public String getConfigProfile() {
return "proxy-auth";
}

@Override
public Map<String, String> getConfigOverrides() {
return Map.of("allow.put.operations", "true");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import io.quarkiverse.mcp.server.ToolResponse;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.TestProfile;
import io.quarkus.test.security.TestSecurity;
import jakarta.inject.Inject;
import org.bson.Document;
import org.eclipse.microprofile.config.ConfigProvider;
Expand Down Expand Up @@ -40,6 +41,7 @@
@QuarkusTest
@TestProfile(IntegrationTestProfile.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestSecurity(authorizationEnabled = false)
public class MongoMcpIntegration {

private static final Logger logger = LoggerFactory.getLogger(MongoMcpIntegration.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.quarkiverse.mcp.server.ToolResponse;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.TestProfile;
import io.quarkus.test.security.TestSecurity;
import jakarta.inject.Inject;
import org.finos.calm.mcp.tools.ArchitectureTools;
import org.finos.calm.mcp.tools.ControlTools;
Expand Down Expand Up @@ -34,6 +35,7 @@
@QuarkusTest
@TestProfile(NitriteIntegrationTestProfile.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestSecurity(authorizationEnabled = false)
public class NitriteMcpIntegration {

private static final Logger logger = LoggerFactory.getLogger(NitriteMcpIntegration.class);
Expand Down
Loading
Loading