Only allow C-S device scopes when the C-S API scope has been requested (#5215)

This commit is contained in:
reivilibre
2025-11-05 15:47:03 +00:00
committed by GitHub
4 changed files with 60 additions and 14 deletions

View File

@@ -72,6 +72,7 @@ Make sure your code adheres to our Rust and TypeScript code style by running:
- `cargo +nightly fmt` (with the nightly toolchain installed)
- `npm run format` in the `frontend` directory
- `make fmt` in the `policies` directory (if changed)
When updating SQL queries in the `crates/storage-pg/` crate, you may need to update the `sqlx` introspection data. To do this, make sure to install `cargo-sqlx` (`cargo install sqlx-cli`) and:
@@ -86,6 +87,7 @@ While you're developing and before submitting a patch, you'll want to test your
- Run `cargo clippy --workspace` to lint the Rust code.
- Run `npm run lint` in the `frontend` directory to lint the frontend code.
- Run `make fmt` and `make lint` in the `policies` directory to format and lint the included policy.
### Run the tests
@@ -93,6 +95,10 @@ If you haven't already, install [Cargo-Nextest](https://nexte.st/docs/installati
- Run the tests to the backend by running `cargo nextest run --workspace`. This requires a connection to a PostgreSQL database, set via the `DATABASE_URL` environment variable.
- Run the tests to the frontend by running `npm run test` in the `frontend` directory.
- To run the tests for the included policy, change to the `policies` directory and run one of:
- `make test` (needs Open Policy Agent installed)
- `make PODMAN=1 test` (runs inside a container; needs Podman installed)
- `make DOCKER=1 test` (runs inside a container; needs Docker installed)
## 8. Submit a pull request

View File

@@ -24,9 +24,9 @@ ifeq ($(DOCKER), 1)
REGAL := docker run -i -v $(shell pwd):/policies:ro -w /policies --rm $(REGAL_DOCKER_IMAGE)
else ifeq ($(PODMAN), 1)
# When running rootless, the volume directory may need to be given global write permissions on the host
OPA := podman run -i -v $(shell pwd):/policies:ro:Z -w /policies --rm $(OPA_DOCKER_IMAGE)
OPA := podman run -i -v $(shell pwd):/policies:ro,Z -w /policies --rm $(OPA_DOCKER_IMAGE)
OPA_RW := podman run -i -v $(shell pwd):/policies:Z -w /policies --rm $(OPA_DOCKER_IMAGE)
REGAL := podman run -i -v $(shell pwd):/policies:ro:Z -w /policies --rm $(REGAL_DOCKER_IMAGE)
REGAL := podman run -i -v $(shell pwd):/policies:ro,Z -w /policies --rm $(REGAL_DOCKER_IMAGE)
else
OPA := opa
OPA_RW := opa

View File

@@ -98,6 +98,26 @@ uses_stable_scopes if {
count({scope | some scope in scope_list; startswith(scope, "urn:matrix:client:")}) > 0
}
has_device_scope if {
scope_list := split(input.scope, " ")
count({scope | some scope in scope_list; startswith(scope, "urn:matrix:client:device:")}) > 0
}
has_device_scope if {
scope_list := split(input.scope, " ")
count({scope | some scope in scope_list; startswith(scope, "urn:matrix:org.matrix.msc2967.client:device:")}) > 0
}
has_cs_api_scope if {
scope_list := split(input.scope, " ")
count({scope | some scope in scope_list; startswith(scope, "urn:matrix:client:api:")}) > 0
}
has_cs_api_scope if {
scope_list := split(input.scope, " ")
count({scope | some scope in scope_list; startswith(scope, "urn:matrix:org.matrix.msc2967.client:api:")}) > 0
}
# METADATA
# entrypoint: true
violation contains {"msg": msg} if {
@@ -116,6 +136,12 @@ violation contains {"msg": "only one device scope is allowed at a time"} if {
count({scope | some scope in scope_list; startswith(scope, "urn:matrix:client:device:")}) > 1
}
# Prevent the creation of C-S API devices for sessions that don't have C-S API access.
violation contains {"msg": "device scopes are only allowed when the client-server API scope is requested"} if {
has_device_scope
not has_cs_api_scope
}
violation contains {"msg": "request cannot mix unstable and stable scopes"} if {
uses_stable_scopes
uses_unstable_scopes

View File

@@ -78,70 +78,84 @@ test_unstable_device_scopes if {
authorization_grant.allow with input.user as user
with input.client as client
with input.grant_type as "authorization_code"
with input.scope as "urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01"
with input.scope as "urn:matrix:org.matrix.msc2967.client:api:* urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01"
authorization_grant.allow with input.user as user
with input.client as client
with input.grant_type as "authorization_code"
with input.scope as "urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01-asdasdsa1-2313"
with input.scope as "urn:matrix:org.matrix.msc2967.client:api:* urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01-asdasdsa1-2313"
# Too short
not authorization_grant.allow with input.user as user
with input.client as client
with input.grant_type as "authorization_code"
with input.scope as "urn:matrix:org.matrix.msc2967.client:device:abcd"
with input.scope as "urn:matrix:org.matrix.msc2967.client:api:* urn:matrix:org.matrix.msc2967.client:device:abcd"
# Multiple device scope
not authorization_grant.allow with input.user as user
with input.client as client
with input.grant_type as "authorization_code"
with input.scope as "urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01 urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd02"
with input.scope as "urn:matrix:org.matrix.msc2967.client:api:* urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01 urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd02"
# Allowed with the device code grant
authorization_grant.allow with input.user as user
with input.client as client
with input.grant_type as "urn:ietf:params:oauth:grant-type:device_code"
with input.scope as "urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01"
with input.scope as "urn:matrix:org.matrix.msc2967.client:api:* urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01"
# Not authorization_grant.allowed for the client credentials grant
not authorization_grant.allow with input.client as client
with input.grant_type as "client_credentials"
with input.scope as "urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01"
with input.scope as "urn:matrix:org.matrix.msc2967.client:api:* urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01"
}
test_stable_device_scopes if {
authorization_grant.allow with input.user as user
with input.client as client
with input.grant_type as "authorization_code"
with input.scope as "urn:matrix:client:device:AAbbCCdd01"
with input.scope as "urn:matrix:client:api:* urn:matrix:client:device:AAbbCCdd01"
authorization_grant.allow with input.user as user
with input.client as client
with input.grant_type as "authorization_code"
with input.scope as "urn:matrix:client:device:AAbbCCdd01-asdasdsa1-2313"
with input.scope as "urn:matrix:client:api:* urn:matrix:client:device:AAbbCCdd01-asdasdsa1-2313"
# Too short
not authorization_grant.allow with input.user as user
with input.client as client
with input.grant_type as "authorization_code"
with input.scope as "urn:matrix:client:device:abcd"
with input.scope as "urn:matrix:client:api:* urn:matrix:client:device:abcd"
# Multiple device scope
not authorization_grant.allow with input.user as user
with input.client as client
with input.grant_type as "authorization_code"
with input.scope as "urn:matrix:client:device:AAbbCCdd01 urn:matrix:client:device:AAbbCCdd02"
with input.scope as "urn:matrix:client:api:* urn:matrix:client:device:AAbbCCdd01 urn:matrix:client:device:AAbbCCdd02"
# Allowed with the device code grant
authorization_grant.allow with input.user as user
with input.client as client
with input.grant_type as "urn:ietf:params:oauth:grant-type:device_code"
with input.scope as "urn:matrix:client:device:AAbbCCdd01"
with input.scope as "urn:matrix:client:api:* urn:matrix:client:device:AAbbCCdd01"
# Not authorization_grant.allowed for the client credentials grant
not authorization_grant.allow with input.client as client
with input.grant_type as "client_credentials"
with input.scope as "urn:matrix:client:device:AAbbCCdd01"
with input.scope as "urn:matrix:client:api:* urn:matrix:client:device:AAbbCCdd01"
}
test_device_scope_only_with_cs_api_scope if {
not authorization_grant.allow with input.user as user
with input.client as client
with input.grant_type as "authorization_code"
# Requested a device scope but no C-S API scope:
with input.scope as "urn:matrix:client:device:AAbbCCdd01"
not authorization_grant.allow with input.user as user
with input.client as client
with input.grant_type as "authorization_code"
# Requested a device scope but no C-S API scope:
with input.scope as "urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01"
}
test_mix_stable_and_unstable_scopes if {