You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-31 09:24:31 +03:00
Run OPA policies during registration
This commit is contained in:
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
#![allow(clippy::trait_duplication_in_bounds)]
|
#![allow(clippy::trait_duplication_in_bounds)]
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::{str::FromStr, sync::Arc};
|
||||||
|
|
||||||
use argon2::Argon2;
|
use argon2::Argon2;
|
||||||
use axum::{
|
use axum::{
|
||||||
@ -29,6 +29,7 @@ use mas_axum_utils::{
|
|||||||
};
|
};
|
||||||
use mas_config::Encrypter;
|
use mas_config::Encrypter;
|
||||||
use mas_email::Mailer;
|
use mas_email::Mailer;
|
||||||
|
use mas_policy::PolicyFactory;
|
||||||
use mas_router::Route;
|
use mas_router::Route;
|
||||||
use mas_storage::user::{
|
use mas_storage::user::{
|
||||||
add_user_email, add_user_email_verification_code, register_user, start_session, username_exists,
|
add_user_email, add_user_email_verification_code, register_user, start_session, username_exists,
|
||||||
@ -87,6 +88,7 @@ pub(crate) async fn get(
|
|||||||
|
|
||||||
pub(crate) async fn post(
|
pub(crate) async fn post(
|
||||||
Extension(mailer): Extension<Mailer>,
|
Extension(mailer): Extension<Mailer>,
|
||||||
|
Extension(policy_factory): Extension<Arc<PolicyFactory>>,
|
||||||
Extension(templates): Extension<Templates>,
|
Extension(templates): Extension<Templates>,
|
||||||
Extension(pool): Extension<PgPool>,
|
Extension(pool): Extension<PgPool>,
|
||||||
Query(query): Query<OptionalPostAuthAction>,
|
Query(query): Query<OptionalPostAuthAction>,
|
||||||
@ -129,6 +131,15 @@ pub(crate) async fn post(
|
|||||||
state.add_error_on_field(RegisterFormField::PasswordConfirm, FieldError::Unspecified);
|
state.add_error_on_field(RegisterFormField::PasswordConfirm, FieldError::Unspecified);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut policy = policy_factory.instanciate().await?;
|
||||||
|
let res = policy
|
||||||
|
.evaluate_register(&form.username, &form.email)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if !res {
|
||||||
|
state.add_error_on_form(FormError::Policy);
|
||||||
|
}
|
||||||
|
|
||||||
state
|
state
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -52,6 +52,9 @@ pub enum FormError {
|
|||||||
|
|
||||||
/// There was an internal error
|
/// There was an internal error
|
||||||
Internal,
|
Internal,
|
||||||
|
|
||||||
|
/// Denied by the policy
|
||||||
|
Policy,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize)]
|
#[derive(Debug, Default, Serialize)]
|
||||||
|
Binary file not shown.
@ -1,3 +1,41 @@
|
|||||||
package register
|
package register
|
||||||
|
|
||||||
allow := true
|
import future.keywords.in
|
||||||
|
|
||||||
|
default allow := false
|
||||||
|
allow := true {
|
||||||
|
count(violation) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
violation[{"field": "username", "msg": "username too short"}] {
|
||||||
|
count(input.user.username) <= 2
|
||||||
|
}
|
||||||
|
|
||||||
|
violation[{"field": "username", "msg": "username too long"}] {
|
||||||
|
count(input.user.username) >= 15
|
||||||
|
}
|
||||||
|
|
||||||
|
# Allow any domains if the data.allowed_domains array is not set
|
||||||
|
email_domain_allowed {
|
||||||
|
not data.allowed_domains
|
||||||
|
}
|
||||||
|
|
||||||
|
# Allow an email only if its domain is in the list of allowed domains
|
||||||
|
email_domain_allowed {
|
||||||
|
[_, domain] := split(input.user.email, "@")
|
||||||
|
some allowed_domain in data.allowed_domains
|
||||||
|
glob.match(allowed_domain, ["."], domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
violation[{"field": "email", "msg": "email domain not allowed"}] {
|
||||||
|
not email_domain_allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
# Deny emails with their domain in the domains banlist
|
||||||
|
violation[{"field": "email", "msg": "email domain not allowed"}] {
|
||||||
|
[_, domain] := split(input.user.email, "@")
|
||||||
|
some banned_domain in data.banned_domains
|
||||||
|
glob.match(banned_domain, ["."], domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
32
policies/register_test.rego
Normal file
32
policies/register_test.rego
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package register
|
||||||
|
|
||||||
|
mock_user := {"username": "hello", "email": "hello@staging.element.io"}
|
||||||
|
|
||||||
|
test_allow_all_domains {
|
||||||
|
allow with input.user as mock_user
|
||||||
|
}
|
||||||
|
|
||||||
|
test_allowed_domain {
|
||||||
|
allow
|
||||||
|
with input.user as mock_user
|
||||||
|
with data.allowed_domains as ["*.element.io"]
|
||||||
|
}
|
||||||
|
|
||||||
|
test_not_allowed_domain {
|
||||||
|
not allow
|
||||||
|
with input.user as mock_user
|
||||||
|
with data.allowed_domains as ["example.com"]
|
||||||
|
}
|
||||||
|
|
||||||
|
test_banned_domain {
|
||||||
|
not allow
|
||||||
|
with input.user as mock_user
|
||||||
|
with data.banned_domains as ["*.element.io"]
|
||||||
|
}
|
||||||
|
|
||||||
|
test_banned_subdomain {
|
||||||
|
not allow
|
||||||
|
with input.user as mock_user
|
||||||
|
with data.allowed_domains as ["*.element.io"]
|
||||||
|
with data.banned_domains as ["staging.element.io"]
|
||||||
|
}
|
Reference in New Issue
Block a user