You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-08-07 17:03:01 +03:00
policy: prepare for the client credentials grant
This commit is contained in:
@@ -20,7 +20,7 @@
|
|||||||
pub mod model;
|
pub mod model;
|
||||||
|
|
||||||
use mas_data_model::{AuthorizationGrant, Client, User};
|
use mas_data_model::{AuthorizationGrant, Client, User};
|
||||||
use oauth2_types::registration::VerifiedClientMetadata;
|
use oauth2_types::{registration::VerifiedClientMetadata, scope::Scope};
|
||||||
use opa_wasm::Runtime;
|
use opa_wasm::Runtime;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::io::{AsyncRead, AsyncReadExt};
|
use tokio::io::{AsyncRead, AsyncReadExt};
|
||||||
@@ -30,6 +30,7 @@ use self::model::{
|
|||||||
AuthorizationGrantInput, ClientRegistrationInput, EmailInput, PasswordInput, RegisterInput,
|
AuthorizationGrantInput, ClientRegistrationInput, EmailInput, PasswordInput, RegisterInput,
|
||||||
};
|
};
|
||||||
pub use self::model::{EvaluationResult, Violation};
|
pub use self::model::{EvaluationResult, Violation};
|
||||||
|
use crate::model::GrantType;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum LoadError {
|
pub enum LoadError {
|
||||||
@@ -300,6 +301,7 @@ impl Policy {
|
|||||||
skip_all,
|
skip_all,
|
||||||
fields(
|
fields(
|
||||||
input.authorization_grant.id = %authorization_grant.id,
|
input.authorization_grant.id = %authorization_grant.id,
|
||||||
|
input.scope = %authorization_grant.scope,
|
||||||
input.client.id = %client.id,
|
input.client.id = %client.id,
|
||||||
input.user.id = %user.id,
|
input.user.id = %user.id,
|
||||||
),
|
),
|
||||||
@@ -314,7 +316,43 @@ impl Policy {
|
|||||||
let input = AuthorizationGrantInput {
|
let input = AuthorizationGrantInput {
|
||||||
user,
|
user,
|
||||||
client,
|
client,
|
||||||
authorization_grant,
|
scope: &authorization_grant.scope,
|
||||||
|
grant_type: GrantType::AuthorizationCode,
|
||||||
|
};
|
||||||
|
|
||||||
|
let [res]: [EvaluationResult; 1] = self
|
||||||
|
.instance
|
||||||
|
.evaluate(
|
||||||
|
&mut self.store,
|
||||||
|
&self.entrypoints.authorization_grant,
|
||||||
|
&input,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(
|
||||||
|
name = "policy.evaluate.client_credentials_grant",
|
||||||
|
skip_all,
|
||||||
|
fields(
|
||||||
|
input.scope = %scope,
|
||||||
|
input.client.id = %client.id,
|
||||||
|
input.user.id = %user.id,
|
||||||
|
),
|
||||||
|
err,
|
||||||
|
)]
|
||||||
|
pub async fn evaluate_client_credentials_grant(
|
||||||
|
&mut self,
|
||||||
|
scope: &Scope,
|
||||||
|
client: &Client,
|
||||||
|
user: &User,
|
||||||
|
) -> Result<EvaluationResult, EvaluationError> {
|
||||||
|
let input = AuthorizationGrantInput {
|
||||||
|
user,
|
||||||
|
client,
|
||||||
|
scope,
|
||||||
|
grant_type: GrantType::ClientCredentials,
|
||||||
};
|
};
|
||||||
|
|
||||||
let [res]: [EvaluationResult; 1] = self
|
let [res]: [EvaluationResult; 1] = self
|
||||||
|
@@ -12,8 +12,13 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use mas_data_model::{AuthorizationGrant, Client, User};
|
//! Input and output types for policy evaluation.
|
||||||
use oauth2_types::registration::VerifiedClientMetadata;
|
//!
|
||||||
|
//! This is useful to generate JSON schemas for each input type, which can then
|
||||||
|
//! be type-checked by Open Policy Agent.
|
||||||
|
|
||||||
|
use mas_data_model::{Client, User};
|
||||||
|
use oauth2_types::{registration::VerifiedClientMetadata, scope::Scope};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// A single violation of a policy.
|
/// A single violation of a policy.
|
||||||
@@ -87,6 +92,14 @@ pub struct ClientRegistrationInput<'a> {
|
|||||||
pub client_metadata: &'a VerifiedClientMetadata,
|
pub client_metadata: &'a VerifiedClientMetadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
|
||||||
|
pub enum GrantType {
|
||||||
|
AuthorizationCode,
|
||||||
|
ClientCredentials,
|
||||||
|
}
|
||||||
|
|
||||||
/// Input for the authorization grant policy.
|
/// Input for the authorization grant policy.
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
@@ -104,11 +117,10 @@ pub struct AuthorizationGrantInput<'a> {
|
|||||||
)]
|
)]
|
||||||
pub client: &'a Client,
|
pub client: &'a Client,
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(feature = "jsonschema", schemars(with = "String"))]
|
||||||
feature = "jsonschema",
|
pub scope: &'a Scope,
|
||||||
schemars(with = "std::collections::HashMap<String, serde_json::Value>")
|
|
||||||
)]
|
pub grant_type: GrantType,
|
||||||
pub authorization_grant: &'a AuthorizationGrant,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Input for the email add policy.
|
/// Input for the email add policy.
|
||||||
|
@@ -20,6 +20,7 @@ allowed_scope("email") = true
|
|||||||
|
|
||||||
# This grants access to Synapse's admin API endpoints
|
# This grants access to Synapse's admin API endpoints
|
||||||
allowed_scope("urn:synapse:admin:*") {
|
allowed_scope("urn:synapse:admin:*") {
|
||||||
|
input.grant_type == "authorization_code"
|
||||||
some user in data.admin_users
|
some user in data.admin_users
|
||||||
input.user.username == user
|
input.user.username == user
|
||||||
}
|
}
|
||||||
@@ -29,23 +30,36 @@ allowed_scope("urn:mas:graphql:*") = true
|
|||||||
|
|
||||||
# This makes it possible to query and do anything in the GraphQL API as an admin
|
# This makes it possible to query and do anything in the GraphQL API as an admin
|
||||||
allowed_scope("urn:mas:admin") {
|
allowed_scope("urn:mas:admin") {
|
||||||
|
input.grant_type == "authorization_code"
|
||||||
some user in data.admin_users
|
some user in data.admin_users
|
||||||
input.user.username == user
|
input.user.username == user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# This makes it possible to get the admin scope for clients that are allowed
|
||||||
|
allowed_scope("urn:mas:admin") {
|
||||||
|
input.grant_type == "client_credentials"
|
||||||
|
some client in data.admin_clients
|
||||||
|
input.client.id == client
|
||||||
|
}
|
||||||
|
|
||||||
allowed_scope(scope) {
|
allowed_scope(scope) {
|
||||||
|
# Grant access to the C-S API only if there is a user
|
||||||
|
input.grant_type == "authorization_code"
|
||||||
regex.match("urn:matrix:org.matrix.msc2967.client:device:[A-Za-z0-9-]{10,}", scope)
|
regex.match("urn:matrix:org.matrix.msc2967.client:device:[A-Za-z0-9-]{10,}", scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
allowed_scope("urn:matrix:org.matrix.msc2967.client:api:*") = true
|
allowed_scope("urn:matrix:org.matrix.msc2967.client:api:*") {
|
||||||
|
# Grant access to the C-S API only if there is a user
|
||||||
|
input.grant_type == "authorization_code"
|
||||||
|
}
|
||||||
|
|
||||||
violation[{"msg": msg}] {
|
violation[{"msg": msg}] {
|
||||||
some scope in split(input.authorization_grant.scope, " ")
|
some scope in split(input.scope, " ")
|
||||||
not allowed_scope(scope)
|
not allowed_scope(scope)
|
||||||
msg := sprintf("scope '%s' not allowed", [scope])
|
msg := sprintf("scope '%s' not allowed", [scope])
|
||||||
}
|
}
|
||||||
|
|
||||||
violation[{"msg": "only one device scope is allowed at a time"}] {
|
violation[{"msg": "only one device scope is allowed at a time"}] {
|
||||||
scope_list := split(input.authorization_grant.scope, " ")
|
scope_list := split(input.scope, " ")
|
||||||
count({key | scope_list[key]; startswith(scope_list[key], "urn:matrix:org.matrix.msc2967.client:device:")}) > 1
|
count({key | scope_list[key]; startswith(scope_list[key], "urn:matrix:org.matrix.msc2967.client:device:")}) > 1
|
||||||
}
|
}
|
||||||
|
@@ -2,78 +2,116 @@ package authorization_grant
|
|||||||
|
|
||||||
user := {"username": "john"}
|
user := {"username": "john"}
|
||||||
|
|
||||||
|
client := {"client_id": "client"}
|
||||||
|
|
||||||
test_standard_scopes {
|
test_standard_scopes {
|
||||||
allow with input.user as user
|
allow with input.user as user
|
||||||
with input.authorization_grant as {"scope": ""}
|
with input.client as client
|
||||||
|
with input.scope as ""
|
||||||
|
|
||||||
allow with input.user as user
|
allow with input.user as user
|
||||||
with input.authorization_grant as {"scope": "openid"}
|
with input.client as client
|
||||||
|
with input.scope as "openid"
|
||||||
|
|
||||||
allow with input.user as user
|
allow with input.user as user
|
||||||
with input.authorization_grant as {"scope": "email"}
|
with input.client as client
|
||||||
|
with input.scope as "email"
|
||||||
|
|
||||||
allow with input.user as user
|
allow with input.user as user
|
||||||
with input.authorization_grant as {"scope": "openid email"}
|
with input.client as client
|
||||||
|
with input.scope as "openid email"
|
||||||
|
|
||||||
# Not supported yet
|
# Not supported yet
|
||||||
not allow with input.user as user
|
not allow with input.user as user
|
||||||
with input.authorization_grant as {"scope": "phone"}
|
with input.client as client
|
||||||
|
with input.scope as "phone"
|
||||||
|
|
||||||
# Not supported yet
|
# Not supported yet
|
||||||
not allow with input.user as user
|
not allow with input.user as user
|
||||||
with input.authorization_grant as {"scope": "profile"}
|
with input.client as client
|
||||||
|
with input.scope as "profile"
|
||||||
}
|
}
|
||||||
|
|
||||||
test_matrix_scopes {
|
test_matrix_scopes {
|
||||||
allow with input.user as user
|
allow with input.user as user
|
||||||
with input.authorization_grant as {"scope": "urn:matrix:org.matrix.msc2967.client:api:*"}
|
with input.client as client
|
||||||
|
with input.grant_type as "authorization_code"
|
||||||
|
with input.scope as "urn:matrix:org.matrix.msc2967.client:api:*"
|
||||||
}
|
}
|
||||||
|
|
||||||
test_device_scopes {
|
test_device_scopes {
|
||||||
allow with input.user as user
|
allow with input.user as user
|
||||||
with input.authorization_grant as {"scope": "urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01"}
|
with input.client as client
|
||||||
|
with input.grant_type as "authorization_code"
|
||||||
|
with input.scope as "urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01"
|
||||||
|
|
||||||
allow with input.user as user
|
allow with input.user as user
|
||||||
with input.authorization_grant as {"scope": "urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01-asdasdsa1-2313"}
|
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"
|
||||||
|
|
||||||
# Invalid characters
|
# Invalid characters
|
||||||
not allow with input.user as user
|
not allow with input.user as user
|
||||||
with input.authorization_grant as {"scope": "urn:matrix:org.matrix.msc2967.client:device:AABB:CCDDEE"}
|
with input.client as client
|
||||||
|
with input.grant_type as "authorization_code"
|
||||||
|
with input.scope as "urn:matrix:org.matrix.msc2967.client:device:AABB:CCDDEE"
|
||||||
|
|
||||||
not allow with input.user as user
|
not allow with input.user as user
|
||||||
with input.authorization_grant as {"scope": "urn:matrix:org.matrix.msc2967.client:device:AABB*CCDDEE"}
|
with input.client as client
|
||||||
|
with input.grant_type as "authorization_code"
|
||||||
|
with input.scope as "urn:matrix:org.matrix.msc2967.client:device:AABB*CCDDEE"
|
||||||
|
|
||||||
not allow with input.user as user
|
not allow with input.user as user
|
||||||
with input.authorization_grant as {"scope": "urn:matrix:org.matrix.msc2967.client:device:AABB!CCDDEE"}
|
with input.client as client
|
||||||
|
with input.grant_type as "authorization_code"
|
||||||
|
with input.scope as "urn:matrix:org.matrix.msc2967.client:device:AABB!CCDDEE"
|
||||||
|
|
||||||
# Too short
|
# Too short
|
||||||
not allow with input.user as user
|
not allow with input.user as user
|
||||||
with input.authorization_grant as {"scope": "urn:matrix:org.matrix.msc2967.client:device:abcd"}
|
with input.client as client
|
||||||
|
with input.grant_type as "authorization_code"
|
||||||
|
with input.scope as "urn:matrix:org.matrix.msc2967.client:device:abcd"
|
||||||
|
|
||||||
# Multiple device scope
|
# Multiple device scope
|
||||||
not allow with input.user as user
|
not allow with input.user as user
|
||||||
with input.authorization_grant as {"scope": "urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd01 urn:matrix:org.matrix.msc2967.client:device:AAbbCCdd02"}
|
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"
|
||||||
|
|
||||||
|
# Not allowed for the client credentials grant
|
||||||
|
not 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"
|
||||||
}
|
}
|
||||||
|
|
||||||
test_synapse_admin_scopes {
|
test_synapse_admin_scopes {
|
||||||
allow with input.user as user
|
allow with input.user as user
|
||||||
|
with input.client as client
|
||||||
with data.admin_users as ["john"]
|
with data.admin_users as ["john"]
|
||||||
with input.authorization_grant as {"scope": "urn:synapse:admin:*"}
|
with input.grant_type as "authorization_code"
|
||||||
|
with input.scope as "urn:synapse:admin:*"
|
||||||
|
|
||||||
not allow with input.user as user
|
not allow with input.user as user
|
||||||
|
with input.client as client
|
||||||
with data.admin_users as []
|
with data.admin_users as []
|
||||||
with input.authorization_grant as {"scope": "urn:synapse:admin:*"}
|
with input.grant_type as "authorization_code"
|
||||||
|
with input.scope as "urn:synapse:admin:*"
|
||||||
}
|
}
|
||||||
|
|
||||||
test_mas_scopes {
|
test_mas_scopes {
|
||||||
allow with input.user as user
|
allow with input.user as user
|
||||||
with input.authorization_grant as {"scope": "urn:mas:graphql:*"}
|
with input.client as client
|
||||||
|
with input.scope as "urn:mas:graphql:*"
|
||||||
|
|
||||||
allow with input.user as user
|
allow with input.user as user
|
||||||
|
with input.client as client
|
||||||
with data.admin_users as ["john"]
|
with data.admin_users as ["john"]
|
||||||
with input.authorization_grant as {"scope": "urn:mas:admin"}
|
with input.grant_type as "authorization_code"
|
||||||
|
with input.scope as "urn:mas:admin"
|
||||||
|
|
||||||
not allow with input.user as user
|
not allow with input.user as user
|
||||||
|
with input.client as client
|
||||||
with data.admin_users as []
|
with data.admin_users as []
|
||||||
with input.authorization_grant as {"scope": "urn:mas:admin"}
|
with input.grant_type as "authorization_code"
|
||||||
|
with input.scope as "urn:mas:admin"
|
||||||
}
|
}
|
||||||
|
@@ -4,22 +4,34 @@
|
|||||||
"description": "Input for the authorization grant policy.",
|
"description": "Input for the authorization grant policy.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"authorization_grant",
|
|
||||||
"client",
|
"client",
|
||||||
|
"grant_type",
|
||||||
|
"scope",
|
||||||
"user"
|
"user"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"authorization_grant": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": true
|
|
||||||
},
|
|
||||||
"client": {
|
"client": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": true
|
"additionalProperties": true
|
||||||
},
|
},
|
||||||
|
"grant_type": {
|
||||||
|
"$ref": "#/definitions/GrantType"
|
||||||
|
},
|
||||||
|
"scope": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": true
|
"additionalProperties": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"GrantType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"authorization_code",
|
||||||
|
"client_credentials"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user