Showing preview only (8,901K chars total). Download the full file or copy to clipboard to get everything.
Repository: element-hq/matrix-authentication-service
Branch: main
Commit: 07c352ee2e2f
Files: 1410
Total size: 8.1 MB
Directory structure:
gitextract_fztawn0h/
├── .cargo/
│ └── config.toml
├── .codecov.yml
├── .config/
│ └── nextest.toml
├── .dockerignore
├── .editorconfig
├── .github/
│ ├── CODEOWNERS
│ ├── actions/
│ │ ├── build-frontend/
│ │ │ └── action.yml
│ │ └── build-policies/
│ │ └── action.yml
│ ├── dependabot.yml
│ ├── release.yml
│ ├── scripts/
│ │ ├── .gitignore
│ │ ├── cleanup-pr.cjs
│ │ ├── commit-and-tag.cjs
│ │ ├── create-release-branch.cjs
│ │ ├── create-version-tag.cjs
│ │ ├── merge-back.cjs
│ │ ├── package.json
│ │ ├── update-release-branch.cjs
│ │ └── update-unstable-tag.cjs
│ └── workflows/
│ ├── build.yaml
│ ├── ci.yaml
│ ├── coverage.yaml
│ ├── docs.yaml
│ ├── merge-back.yaml
│ ├── release-branch.yaml
│ ├── release-bump.yaml
│ ├── tag.yaml
│ ├── translations-download.yaml
│ └── translations-upload.yaml
├── .gitignore
├── .rustfmt.toml
├── CONTRIBUTING.md
├── Cargo.toml
├── Dockerfile
├── LICENSE
├── LICENSE-COMMERCIAL
├── README.md
├── biome.json
├── book.toml
├── clippy.toml
├── crates/
│ ├── axum-utils/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── client_authorization.rs
│ │ ├── cookies.rs
│ │ ├── csrf.rs
│ │ ├── error_wrapper.rs
│ │ ├── fancy_error.rs
│ │ ├── jwt.rs
│ │ ├── language_detection.rs
│ │ ├── lib.rs
│ │ ├── sentry.rs
│ │ ├── session.rs
│ │ └── user_authorization.rs
│ ├── cli/
│ │ ├── Cargo.toml
│ │ ├── build.rs
│ │ └── src/
│ │ ├── app_state.rs
│ │ ├── commands/
│ │ │ ├── config.rs
│ │ │ ├── database.rs
│ │ │ ├── debug.rs
│ │ │ ├── doctor.rs
│ │ │ ├── manage.rs
│ │ │ ├── mod.rs
│ │ │ ├── server.rs
│ │ │ ├── syn2mas.rs
│ │ │ ├── templates.rs
│ │ │ └── worker.rs
│ │ ├── lifecycle.rs
│ │ ├── main.rs
│ │ ├── server.rs
│ │ ├── sync.rs
│ │ ├── telemetry.rs
│ │ └── util.rs
│ ├── config/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── bin/
│ │ │ └── schema.rs
│ │ ├── lib.rs
│ │ ├── schema.rs
│ │ ├── sections/
│ │ │ ├── account.rs
│ │ │ ├── branding.rs
│ │ │ ├── captcha.rs
│ │ │ ├── clients.rs
│ │ │ ├── database.rs
│ │ │ ├── email.rs
│ │ │ ├── experimental.rs
│ │ │ ├── http.rs
│ │ │ ├── matrix.rs
│ │ │ ├── mod.rs
│ │ │ ├── oauth.rs
│ │ │ ├── passwords.rs
│ │ │ ├── policy.rs
│ │ │ ├── rate_limiting.rs
│ │ │ ├── secrets.rs
│ │ │ ├── telemetry.rs
│ │ │ ├── templates.rs
│ │ │ └── upstream_oauth2.rs
│ │ └── util.rs
│ ├── context/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── fmt.rs
│ │ ├── future.rs
│ │ ├── layer.rs
│ │ ├── lib.rs
│ │ └── service.rs
│ ├── data-model/
│ │ ├── Cargo.toml
│ │ ├── examples/
│ │ │ └── ua-parser.rs
│ │ └── src/
│ │ ├── clock.rs
│ │ ├── compat/
│ │ │ ├── device.rs
│ │ │ ├── mod.rs
│ │ │ ├── session.rs
│ │ │ └── sso_login.rs
│ │ ├── lib.rs
│ │ ├── oauth2/
│ │ │ ├── authorization_grant.rs
│ │ │ ├── client.rs
│ │ │ ├── device_code_grant.rs
│ │ │ ├── mod.rs
│ │ │ └── session.rs
│ │ ├── personal/
│ │ │ ├── mod.rs
│ │ │ └── session.rs
│ │ ├── policy_data.rs
│ │ ├── site_config.rs
│ │ ├── tokens.rs
│ │ ├── upstream_oauth2/
│ │ │ ├── link.rs
│ │ │ ├── mod.rs
│ │ │ ├── provider.rs
│ │ │ └── session.rs
│ │ ├── user_agent.rs
│ │ ├── users.rs
│ │ ├── utils.rs
│ │ └── version.rs
│ ├── email/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── mailer.rs
│ │ └── transport.rs
│ ├── handlers/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── activity_tracker/
│ │ │ ├── bound.rs
│ │ │ ├── mod.rs
│ │ │ └── worker.rs
│ │ ├── admin/
│ │ │ ├── call_context.rs
│ │ │ ├── mod.rs
│ │ │ ├── model.rs
│ │ │ ├── params.rs
│ │ │ ├── response.rs
│ │ │ ├── schema.rs
│ │ │ └── v1/
│ │ │ ├── compat_sessions/
│ │ │ │ ├── finish.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ └── mod.rs
│ │ │ ├── mod.rs
│ │ │ ├── oauth2_sessions/
│ │ │ │ ├── finish.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ └── mod.rs
│ │ │ ├── personal_sessions/
│ │ │ │ ├── add.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── regenerate.rs
│ │ │ │ └── revoke.rs
│ │ │ ├── policy_data/
│ │ │ │ ├── get.rs
│ │ │ │ ├── get_latest.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── set.rs
│ │ │ ├── site_config.rs
│ │ │ ├── upstream_oauth_links/
│ │ │ │ ├── add.rs
│ │ │ │ ├── delete.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ └── mod.rs
│ │ │ ├── upstream_oauth_providers/
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ └── mod.rs
│ │ │ ├── user_emails/
│ │ │ │ ├── add.rs
│ │ │ │ ├── delete.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ └── mod.rs
│ │ │ ├── user_registration_tokens/
│ │ │ │ ├── add.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── revoke.rs
│ │ │ │ ├── unrevoke.rs
│ │ │ │ └── update.rs
│ │ │ ├── user_sessions/
│ │ │ │ ├── finish.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ └── mod.rs
│ │ │ ├── users/
│ │ │ │ ├── add.rs
│ │ │ │ ├── by_username.rs
│ │ │ │ ├── deactivate.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ ├── lock.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── reactivate.rs
│ │ │ │ ├── set_admin.rs
│ │ │ │ ├── set_password.rs
│ │ │ │ └── unlock.rs
│ │ │ └── version.rs
│ │ ├── bin/
│ │ │ ├── api-schema.rs
│ │ │ └── graphql-schema.rs
│ │ ├── captcha.rs
│ │ ├── cleanup_tests.rs
│ │ ├── compat/
│ │ │ ├── login.rs
│ │ │ ├── login_sso_complete.rs
│ │ │ ├── login_sso_redirect.rs
│ │ │ ├── logout.rs
│ │ │ ├── logout_all.rs
│ │ │ ├── mod.rs
│ │ │ ├── refresh.rs
│ │ │ └── tests.rs
│ │ ├── graphql/
│ │ │ ├── mod.rs
│ │ │ ├── model/
│ │ │ │ ├── browser_sessions.rs
│ │ │ │ ├── compat_sessions.rs
│ │ │ │ ├── cursor.rs
│ │ │ │ ├── matrix.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── node.rs
│ │ │ │ ├── oauth.rs
│ │ │ │ ├── site_config.rs
│ │ │ │ ├── upstream_oauth.rs
│ │ │ │ ├── users.rs
│ │ │ │ └── viewer/
│ │ │ │ ├── anonymous.rs
│ │ │ │ └── mod.rs
│ │ │ ├── mutations/
│ │ │ │ ├── browser_session.rs
│ │ │ │ ├── compat_session.rs
│ │ │ │ ├── matrix.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── oauth2_session.rs
│ │ │ │ ├── user.rs
│ │ │ │ └── user_email.rs
│ │ │ ├── query/
│ │ │ │ ├── mod.rs
│ │ │ │ ├── session.rs
│ │ │ │ ├── upstream_oauth.rs
│ │ │ │ ├── user.rs
│ │ │ │ └── viewer.rs
│ │ │ ├── state.rs
│ │ │ └── tests.rs
│ │ ├── health.rs
│ │ ├── lib.rs
│ │ ├── oauth2/
│ │ │ ├── authorization/
│ │ │ │ ├── callback.rs
│ │ │ │ ├── consent.rs
│ │ │ │ └── mod.rs
│ │ │ ├── device/
│ │ │ │ ├── authorize.rs
│ │ │ │ ├── consent.rs
│ │ │ │ ├── link.rs
│ │ │ │ └── mod.rs
│ │ │ ├── discovery.rs
│ │ │ ├── introspection.rs
│ │ │ ├── keys.rs
│ │ │ ├── mod.rs
│ │ │ ├── registration.rs
│ │ │ ├── revoke.rs
│ │ │ ├── token.rs
│ │ │ ├── userinfo.rs
│ │ │ └── webfinger.rs
│ │ ├── passwords.rs
│ │ ├── preferred_language.rs
│ │ ├── rate_limit.rs
│ │ ├── session.rs
│ │ ├── snapshots/
│ │ │ ├── mas_handlers__passwords__tests__hash_verify_and_upgrade-2.snap
│ │ │ ├── mas_handlers__passwords__tests__hash_verify_and_upgrade-3.snap
│ │ │ ├── mas_handlers__passwords__tests__hash_verify_and_upgrade.snap
│ │ │ ├── mas_handlers__passwords__tests__hashing_argon2id-2.snap
│ │ │ ├── mas_handlers__passwords__tests__hashing_argon2id.snap
│ │ │ ├── mas_handlers__passwords__tests__hashing_bcrypt-2.snap
│ │ │ ├── mas_handlers__passwords__tests__hashing_bcrypt.snap
│ │ │ ├── mas_handlers__passwords__tests__hashing_pbkdf2-2.snap
│ │ │ └── mas_handlers__passwords__tests__hashing_pbkdf2.snap
│ │ ├── test_utils.rs
│ │ ├── upstream_oauth2/
│ │ │ ├── authorize.rs
│ │ │ ├── backchannel_logout.rs
│ │ │ ├── cache.rs
│ │ │ ├── callback.rs
│ │ │ ├── cookie.rs
│ │ │ ├── link.rs
│ │ │ ├── mod.rs
│ │ │ └── template.rs
│ │ └── views/
│ │ ├── app.rs
│ │ ├── index.rs
│ │ ├── login.rs
│ │ ├── logout.rs
│ │ ├── mod.rs
│ │ ├── recovery/
│ │ │ ├── mod.rs
│ │ │ ├── progress.rs
│ │ │ └── start.rs
│ │ ├── register/
│ │ │ ├── cookie.rs
│ │ │ ├── mod.rs
│ │ │ ├── password.rs
│ │ │ └── steps/
│ │ │ ├── display_name.rs
│ │ │ ├── finish.rs
│ │ │ ├── mod.rs
│ │ │ ├── registration_token.rs
│ │ │ └── verify_email.rs
│ │ └── shared.rs
│ ├── http/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── ext.rs
│ │ ├── lib.rs
│ │ └── reqwest.rs
│ ├── i18n/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── lib.rs
│ │ │ ├── sprintf/
│ │ │ │ ├── argument.rs
│ │ │ │ ├── formatter.rs
│ │ │ │ ├── grammar.pest
│ │ │ │ ├── message.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── parser.rs
│ │ │ ├── translations.rs
│ │ │ └── translator.rs
│ │ └── test_data/
│ │ ├── en-US.json
│ │ ├── en.json
│ │ └── fr.json
│ ├── i18n-scan/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── key.rs
│ │ ├── main.rs
│ │ └── minijinja.rs
│ ├── iana/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── jose.rs
│ │ ├── lib.rs
│ │ └── oauth.rs
│ ├── iana-codegen/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── generation.rs
│ │ ├── jose.rs
│ │ ├── main.rs
│ │ ├── oauth.rs
│ │ └── traits.rs
│ ├── jose/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── base64.rs
│ │ │ ├── claims.rs
│ │ │ ├── constraints.rs
│ │ │ ├── jwa/
│ │ │ │ ├── asymmetric.rs
│ │ │ │ ├── hmac.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── signature.rs
│ │ │ │ └── symmetric.rs
│ │ │ ├── jwk/
│ │ │ │ ├── mod.rs
│ │ │ │ ├── private_parameters.rs
│ │ │ │ └── public_parameters.rs
│ │ │ ├── jwt/
│ │ │ │ ├── header.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── raw.rs
│ │ │ │ └── signed.rs
│ │ │ └── lib.rs
│ │ └── tests/
│ │ ├── generate.py
│ │ ├── jws.rs
│ │ ├── jwts/
│ │ │ ├── eddsa-ed25519.jwt
│ │ │ ├── eddsa-ed448.jwt
│ │ │ ├── es256.jwt
│ │ │ ├── es256k.jwt
│ │ │ ├── es384.jwt
│ │ │ ├── es512.jwt
│ │ │ ├── hs256.jwt
│ │ │ ├── hs384.jwt
│ │ │ ├── hs512.jwt
│ │ │ ├── ps256.jwt
│ │ │ ├── ps384.jwt
│ │ │ ├── ps512.jwt
│ │ │ ├── rs256.jwt
│ │ │ ├── rs384.jwt
│ │ │ └── rs512.jwt
│ │ ├── keys/
│ │ │ ├── ed25519.priv.pem
│ │ │ ├── ed25519.pub.pem
│ │ │ ├── ed448.priv.pem
│ │ │ ├── ed448.pub.pem
│ │ │ ├── jwks.priv.json
│ │ │ ├── jwks.pub.json
│ │ │ ├── k256.priv.pem
│ │ │ ├── k256.pub.pem
│ │ │ ├── p256.priv.pem
│ │ │ ├── p256.pub.pem
│ │ │ ├── p384.priv.pem
│ │ │ ├── p384.pub.pem
│ │ │ ├── p521.priv.pem
│ │ │ ├── p521.pub.pem
│ │ │ ├── rsa.priv.pem
│ │ │ └── rsa.pub.pem
│ │ └── snapshots/
│ │ ├── jws__es256__sign_jwt.snap
│ │ ├── jws__es256k__sign_jwt.snap
│ │ ├── jws__es384__sign_jwt.snap
│ │ ├── jws__ps256__sign_jwt.snap
│ │ ├── jws__ps384__sign_jwt.snap
│ │ ├── jws__ps512__sign_jwt.snap
│ │ ├── jws__rs256__sign_jwt.snap
│ │ ├── jws__rs384__sign_jwt.snap
│ │ └── jws__rs512__sign_jwt.snap
│ ├── keystore/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── encrypter.rs
│ │ │ └── lib.rs
│ │ └── tests/
│ │ ├── generate.sh
│ │ ├── keys/
│ │ │ ├── ec-k256.pkcs8.der
│ │ │ ├── ec-k256.pkcs8.encrypted.der
│ │ │ ├── ec-k256.pkcs8.encrypted.pem
│ │ │ ├── ec-k256.pkcs8.pem
│ │ │ ├── ec-k256.sec1.der
│ │ │ ├── ec-k256.sec1.pem
│ │ │ ├── ec-p256.pkcs8.der
│ │ │ ├── ec-p256.pkcs8.encrypted.der
│ │ │ ├── ec-p256.pkcs8.encrypted.pem
│ │ │ ├── ec-p256.pkcs8.pem
│ │ │ ├── ec-p256.sec1.der
│ │ │ ├── ec-p256.sec1.pem
│ │ │ ├── ec-p384.pkcs8.der
│ │ │ ├── ec-p384.pkcs8.encrypted.der
│ │ │ ├── ec-p384.pkcs8.encrypted.pem
│ │ │ ├── ec-p384.pkcs8.pem
│ │ │ ├── ec-p384.sec1.der
│ │ │ ├── ec-p384.sec1.pem
│ │ │ ├── ec256.pkcs8.encrypted.pem
│ │ │ ├── rsa.pkcs1.der
│ │ │ ├── rsa.pkcs1.pem
│ │ │ ├── rsa.pkcs8.der
│ │ │ ├── rsa.pkcs8.encrypted.der
│ │ │ ├── rsa.pkcs8.encrypted.pem
│ │ │ └── rsa.pkcs8.pem
│ │ ├── keystore.rs
│ │ └── snapshots/
│ │ ├── keystore__generate_sign_and_verify-2.snap
│ │ ├── keystore__generate_sign_and_verify-3.snap
│ │ ├── keystore__generate_sign_and_verify-4.snap
│ │ ├── keystore__generate_sign_and_verify-5.snap
│ │ ├── keystore__generate_sign_and_verify.snap
│ │ ├── keystore__jwt_ES256.snap
│ │ ├── keystore__jwt_ES256K.snap
│ │ ├── keystore__jwt_ES384.snap
│ │ ├── keystore__jwt_PS256.snap
│ │ ├── keystore__jwt_PS384.snap
│ │ ├── keystore__jwt_PS512.snap
│ │ ├── keystore__jwt_RS256.snap
│ │ ├── keystore__jwt_RS384.snap
│ │ └── keystore__jwt_RS512.snap
│ ├── listener/
│ │ ├── Cargo.toml
│ │ ├── examples/
│ │ │ └── demo/
│ │ │ ├── certs/
│ │ │ │ ├── ca-key.pem
│ │ │ │ ├── ca.csr
│ │ │ │ ├── ca.json
│ │ │ │ ├── ca.pem
│ │ │ │ ├── client-key.pem
│ │ │ │ ├── client.csr
│ │ │ │ ├── client.json
│ │ │ │ ├── client.pem
│ │ │ │ ├── config.json
│ │ │ │ ├── gen.sh
│ │ │ │ ├── server-key.pem
│ │ │ │ ├── server.csr
│ │ │ │ ├── server.json
│ │ │ │ └── server.pem
│ │ │ └── main.rs
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── maybe_tls.rs
│ │ ├── proxy_protocol/
│ │ │ ├── acceptor.rs
│ │ │ ├── maybe.rs
│ │ │ ├── mod.rs
│ │ │ └── v1.rs
│ │ ├── rewind.rs
│ │ ├── server.rs
│ │ └── unix_or_tcp.rs
│ ├── matrix/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── mock.rs
│ │ └── readonly.rs
│ ├── matrix-synapse/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── error.rs
│ │ ├── legacy.rs
│ │ ├── lib.rs
│ │ └── modern.rs
│ ├── oauth2-types/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── errors.rs
│ │ ├── lib.rs
│ │ ├── oidc.rs
│ │ ├── pkce.rs
│ │ ├── registration/
│ │ │ ├── client_metadata_serde.rs
│ │ │ └── mod.rs
│ │ ├── requests.rs
│ │ ├── response_type.rs
│ │ ├── scope.rs
│ │ ├── test_utils.rs
│ │ └── webfinger.rs
│ ├── oidc-client/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── error.rs
│ │ │ ├── lib.rs
│ │ │ ├── requests/
│ │ │ │ ├── authorization_code.rs
│ │ │ │ ├── client_credentials.rs
│ │ │ │ ├── discovery.rs
│ │ │ │ ├── jose.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── refresh_token.rs
│ │ │ │ ├── token.rs
│ │ │ │ └── userinfo.rs
│ │ │ └── types/
│ │ │ ├── client_credentials.rs
│ │ │ └── mod.rs
│ │ └── tests/
│ │ └── it/
│ │ ├── main.rs
│ │ ├── requests/
│ │ │ ├── authorization_code.rs
│ │ │ ├── client_credentials.rs
│ │ │ ├── discovery.rs
│ │ │ ├── jose.rs
│ │ │ ├── mod.rs
│ │ │ ├── refresh_token.rs
│ │ │ └── userinfo.rs
│ │ └── types/
│ │ ├── client_credentials.rs
│ │ └── mod.rs
│ ├── policy/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── bin/
│ │ │ └── schema.rs
│ │ ├── lib.rs
│ │ └── model.rs
│ ├── router/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── endpoints.rs
│ │ ├── lib.rs
│ │ ├── traits.rs
│ │ └── url_builder.rs
│ ├── spa/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ └── vite.rs
│ ├── storage/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── app_session.rs
│ │ ├── compat/
│ │ │ ├── access_token.rs
│ │ │ ├── mod.rs
│ │ │ ├── refresh_token.rs
│ │ │ ├── session.rs
│ │ │ └── sso_login.rs
│ │ ├── lib.rs
│ │ ├── oauth2/
│ │ │ ├── access_token.rs
│ │ │ ├── authorization_grant.rs
│ │ │ ├── client.rs
│ │ │ ├── device_code_grant.rs
│ │ │ ├── mod.rs
│ │ │ ├── refresh_token.rs
│ │ │ └── session.rs
│ │ ├── pagination.rs
│ │ ├── personal/
│ │ │ ├── access_token.rs
│ │ │ ├── mod.rs
│ │ │ └── session.rs
│ │ ├── policy_data.rs
│ │ ├── queue/
│ │ │ ├── job.rs
│ │ │ ├── mod.rs
│ │ │ ├── schedule.rs
│ │ │ ├── tasks.rs
│ │ │ └── worker.rs
│ │ ├── repository.rs
│ │ ├── upstream_oauth2/
│ │ │ ├── link.rs
│ │ │ ├── mod.rs
│ │ │ ├── provider.rs
│ │ │ └── session.rs
│ │ ├── user/
│ │ │ ├── email.rs
│ │ │ ├── mod.rs
│ │ │ ├── password.rs
│ │ │ ├── recovery.rs
│ │ │ ├── registration.rs
│ │ │ ├── registration_token.rs
│ │ │ ├── session.rs
│ │ │ └── terms.rs
│ │ └── utils.rs
│ ├── storage-pg/
│ │ ├── .sqlx/
│ │ │ ├── query-015f7ad7c8d5403ce4dfb71d598fd9af472689d5aef7c1c4b1c594ca57c02237.json
│ │ │ ├── query-037fae6964130343453ef607791c4c3deaa01b5aaa091d3a3487caf3e2634daf.json
│ │ │ ├── query-03eee34f05df9c79f8ca5bfb1af339b3fcea95ba59395106318366a6ef432d85.json
│ │ │ ├── query-047990a99794b565c2cad396946299db5b617f52f6c24bcca0a24c0c185c4478.json
│ │ │ ├── query-048eec775f4af3ffd805e830e8286c6a5745e523b76e1083d6bfced0035c2f76.json
│ │ │ ├── query-05b4dd39521eaf4e8e3c21654df67c00c8781f54054a84b3f3005b65cbc2a14a.json
│ │ │ ├── query-06d67595eeef23d5f2773632e0956577d98074e244a35c0d3be24bc18d9d0daa.json
│ │ │ ├── query-07cd2da428f0984513b4ce58e526c35c9c236ea8beb6696e5740fa45655e59f3.json
│ │ │ ├── query-093d42238578771b4183b48c1680ba438b6b18306dfe1454fa4124c0207b3deb.json
│ │ │ ├── query-0e1bce56e15751d82a622d532b279bfc50e22cb12ddf7495c7b0fedca61f9421.json
│ │ │ ├── query-0e45995714e60b71e0f0158500a63aa46225245a04d1c7bc24b5275c44a6d58d.json
│ │ │ ├── query-0f2ea548e00b080502edc04ee97ea304d43c336ce80723789ff3e66c0dd4d86c.json
│ │ │ ├── query-109f0c859e123966462f1001aef550e4e12d1778474aba72762d9aa093d21ee2.json
│ │ │ ├── query-12c4577701416a9dc23708c46700f3f086e4e62c6de9d6864a6a11a2470ebe62.json
│ │ │ ├── query-1764715e59f879f6b917ca30f8e3c1de5910c7a46e7fe52d1fb3bfd5561ac320.json
│ │ │ ├── query-188a4aeef5a8b4bf3230c7176ded64d52804848df378dc74f8f54ec4404e094e.json
│ │ │ ├── query-1919d402fd6f148d14417f633be3353004f458c85f7b4f361802f86651900fbc.json
│ │ │ ├── query-1b547552eed4128f2227c681ff2d45586cdb0c20b98393f89036fbf0f1d2dee2.json
│ │ │ ├── query-1dbc50cdab36da307c569891ab7b1ab4aaf128fed6be67ca0f139d697614c63b.json
│ │ │ ├── query-1eb829460407fca22b717b88a1a0a9b7b920d807a4b6c235e1bee524cd73b266.json
│ │ │ ├── query-21b9e39ffd89de288305765c339a991d2471667cf2981770447cde6fd025fbb7.json
│ │ │ ├── query-22896e8f2a002f307089c3e0f9ee561e6521c45ce07d3a42411984c9a6b75fdc.json
│ │ │ ├── query-23d5fcd8bf611dc7279bef0d66ce05461c3c1f43f966fee3a80ae42540783f08.json
│ │ │ ├── query-245cab1cf7d9cf4e94cdec91ecb4dc8e678278121efbe1f66bcdc24144d684d0.json
│ │ │ ├── query-2564bf6366eb59268c41fb25bb40d0e4e9e1fd1f9ea53b7a359c9025d7304223.json
│ │ │ ├── query-29148548d592046f7d711676911e3847e376e443ccd841f76b17a81f53fafc3a.json
│ │ │ ├── query-2a0d8d70d21afa9a2c9c1c432853361bb85911c48f7db6c3873b0f5abf35940b.json
│ │ │ ├── query-2a61003da3655158e6a261d91fdff670f1b4ba3c56605c53e2b905d7ec38c8be.json
│ │ │ ├── query-2d249684e0e4db0e3bc189f821521657559d9b77fd931f972ce4d9f03a57f97a.json
│ │ │ ├── query-2ee26886c56f04cd53d4c0968f5cf0963f92b6d15e6af0e69378a6447dee677c.json
│ │ │ ├── query-2f66991d7b9ba58f011d9aef0eb6a38f3b244c2f46444c0ab345de7feff54aba.json
│ │ │ ├── query-2f7aba76cd7df75d6a9a6d91d5ddebaedf37437f3bd4f796f5581fab997587d7.json
│ │ │ ├── query-2f8d402b7217aef47a5c45d4f7cfddbaeedcbbc6963ee573409bfc98e57de6ed.json
│ │ │ ├── query-31e8bf68ff70a436fd0b6787ac8e2777f9327708b450d048638a162343478cc6.json
│ │ │ ├── query-3312f901f70c3b69e0d315206c31ffe11da64835ae297c9277271b8971d5de81.json
│ │ │ ├── query-38d0608b7d8ba30927f939491c1d43cfd962c729298ad07ee1ade2f2880c0eb3.json
│ │ │ ├── query-38eb6b635d30ca78ff78b926b414cbd866cfc2918ca4b1741b5687f21cfe273b.json
│ │ │ ├── query-399e261027fe6c9167511636157ab747a469404533f59ff6fbd56e9eb5ad38e1.json
│ │ │ ├── query-3c7960a2eb2edd71bc71177fc0fb2e83858c9944893b8f3a0f0131e8a9b7a494.json
│ │ │ ├── query-3c7fc3e386ce51187f6344ad65e1d78a7f026e8311bdc7d5ccc2f39d962e898f.json
│ │ │ ├── query-3d66f3121b11ce923b9c60609b510a8ca899640e78cc8f5b03168622928ffe94.json
│ │ │ ├── query-3e6e3aad53b22fc53eb3ee881b29bb249b18ced57d6a4809dffc23972b3e9423.json
│ │ │ ├── query-3ed73cfce8ef6a1108f454e18b1668f64b76975dba07e67d04ed7a52e2e8107f.json
│ │ │ ├── query-3f9d76f442c82a1631da931950b83b80c9620e1825ab07ab6c52f3f1a32d2527.json
│ │ │ ├── query-432e199b0d47fe299d840c91159726c0a4f89f65b4dc3e33ddad58aabf6b148b.json
│ │ │ ├── query-446a8d7bd8532a751810401adfab924dc20785c91770ed43d62df2e590e8da71.json
│ │ │ ├── query-45d7e962d91fcdcf8284d81d04bc0737c0d20799b497089a566e2ff704d56b67.json
│ │ │ ├── query-494ca16f0f00f977a3031924a15318aa7346917e5c8a37bb0f5b2b3067588009.json
│ │ │ ├── query-4968c60adef69c7215a7efe2021baffb050b2f475ae106155c2e2f210a81191a.json
│ │ │ ├── query-4c2064fed8fa464ea3d2a1258fb0544dbf1493cad31a21c0cd7ddb57ed12de16.json
│ │ │ ├── query-4c37988dacca5a83c8b64209042d5f1a8ec44ec8ccccad2d7fce9ac855209883.json
│ │ │ ├── query-4d0386ad2fe47f1aded46917abe6141752ba90d36467693a68318573171d57b0.json
│ │ │ ├── query-4dad1838536c10ba723adc0fb6da0f24afb3d6a1925a80a1b6d35b9a8258a0ce.json
│ │ │ ├── query-4e64540bbffe5f4b9c4a6589012cf69eb67adaa4d40fc1910dfcd2640e32ab37.json
│ │ │ ├── query-5006c3e60c98c91a0b0fbb3205373e81d9b75e90929af80961f8b5910873a43e.json
│ │ │ ├── query-5133f9c5ba06201433be4ec784034d222975d084d0a9ebe7f1b6b865ab2e09ef.json
│ │ │ ├── query-535225206622b9190ccf42f7d66268818dc84c37b168ab45e582e0a727796a06.json
│ │ │ ├── query-53ad718642644b47a2d49f768d81bd993088526923769a9147281686c2d47591.json
│ │ │ ├── query-5402b8ddb674d05319830477eb3e72ecb536092b46c92a7dda01598962842323.json
│ │ │ ├── query-55bc51efddf7a1cf06610fdb20d46beca29964733338ea4fec2a29393f031c4f.json
│ │ │ ├── query-572ead41d62cfbe40e6f0c8edf6928e8eebd99036255b62d688ac02b5bd74b40.json
│ │ │ ├── query-5a6b91660e4c12b4a1fe2cad08e727a305cbe4029cd4cebd5ecc274e3e32f533.json
│ │ │ ├── query-5b21644dd3c094b0f2f8babb2c730554dc067d0a6cad963dd7e0c66a80b342bf.json
│ │ │ ├── query-5b697dd7834d33ec55972d3ba43d25fe794bc0b69c5938275711faa7a80b811f.json
│ │ │ ├── query-5d0d4699aa82b3976c6c1fcb0d77559da26def223b8954cf32959cce777577d7.json
│ │ │ ├── query-5da7a197e0008f100ad4daa78f4aa6515f0fc9eb54075e8d6d15520d25b75172.json
│ │ │ ├── query-5eea2f4c3e82ae606b09b8a81332594c97ba0afe972f0fee145b6094789fb6c7.json
│ │ │ ├── query-5f2199865fae3a969bb37429dd70dc74505b22c681322bd99b62c2a540c6cd35.json
│ │ │ ├── query-5fe1bb569d13a7d3ff22887b3fc5b76ff901c183b314f8ccb5018d70c516abf6.json
│ │ │ ├── query-607262ccf28b672df51e4e5d371e5cc5119a7d6e7fe784112703c0406f28300f.json
│ │ │ ├── query-608366f45ecaf392ab69cddb12252b5efcc103c3383fa68b552295e2289d1f55.json
│ │ │ ├── query-623097fc45ffa5d6e09fedfbdbe5e42662e9854430bcd9e53598debf99c9ca37.json
│ │ │ ├── query-64b6e274e2bed6814f5ae41ddf57093589f7d1b2b8458521b635546b8012041e.json
│ │ │ ├── query-6589987e88fa9dbbd2bd48acd910e08bab57721007c64ef2597cb09a62100792.json
│ │ │ ├── query-66693f31eff5673e88ca516ee727a709b06455e08b9fd75cc08f142070f330b3.json
│ │ │ ├── query-67cd4880d84b38f20c3960789934d55cbfb01492985ac2af5a1ad4af9b3ccc77.json
│ │ │ ├── query-6b8d28b76d7ab33178b46dbb28c11e41d86f22b3fa899a952cad00129e59bee6.json
│ │ │ ├── query-6bd38759f569fcf972924d12f565b531b9873f4139eadcbf1450e726b9a27379.json
│ │ │ ├── query-6d71188dffc492ddc8f7f21476516d3b08fd5d736ecf36845e6fd4bfc515b2cf.json
│ │ │ ├── query-6db23fc9c39c2c7d9224d4e1233205f636568c990ccb05cf9208750ad1330b9b.json
│ │ │ ├── query-6e21e7d816f806da9bb5176931bdb550dee05c44c9d93f53df95fe3b4a840347.json
│ │ │ ├── query-6ecad60e565367a6cfa539b4c32dabe674ea853e0d47eb5c713705cb0130c758.json
│ │ │ ├── query-6f97b5f9ad0d4d15387150bea3839fb7f81015f7ceef61ecaadba64521895cff.json
│ │ │ ├── query-707d78340069627aba9f18bbe5ac1388d6723f82549d88d704d9c939b9d35c49.json
│ │ │ ├── query-7189b6136fd08ac9ae7c51bff06fb2254d1bf9e8a97cd7d32ba789c740e0fbdb.json
│ │ │ ├── query-755f62d0a3a40acc90037371339a8459736fdd4bbffd932f7930d847f2c3ef5d.json
│ │ │ ├── query-75a62d170e4c959a14c5698f1da983113e7d1bc565d01e85c158856abb17ddc6.json
│ │ │ ├── query-77dfa9fae1a9c77b70476d7da19d3313a02886994cfff0690451229fb5ae2f77.json
│ │ │ ├── query-785e6bceed803cb1caccc373cde0c999d601f3a9730e6bbb40cfc43c04195c61.json
│ │ │ ├── query-7a0641df5058927c5cd67d4cdaa59fe609112afbabcbfcc0e7f96c1e531b6567.json
│ │ │ ├── query-7b06e6f21c69056b526538f06f06268efd13d7af3cecb452168d514a379fec30.json
│ │ │ ├── query-7ce387b1b0aaf10e72adde667b19521b66eaafa51f73bf2f95e38b8f3b64a229.json
│ │ │ ├── query-7e367e416d18fcf9b227bf053421410b4b7b4af441f0a138c5421d1111cb9f79.json
│ │ │ ├── query-7e414c29745cf5c85fa4e7cb5d661b07f43ab168956470d120166ed7eab631d9.json
│ │ │ ├── query-7f4c4634ada4dc2745530dcca8eee92abf78dfbdf1a25e58a2bc9c14be8035f0.json
│ │ │ ├── query-7f8335cc94347bc3a15afe7051658659347a1bf71dd62335df046708f19c967e.json
│ │ │ ├── query-8275a440640ea28fd8f82e7df672e45a6eba981a0d621665ed8f8b60354b3389.json
│ │ │ ├── query-83d1b0720dfde3209d77f1142aa19359913b8a934ca8a642b7bb43c9a7a58a6d.json
│ │ │ ├── query-860e01cd660b450439d63c5ee31ade59f478b0b096b4bc90c89fb9c26b467dd2.json
│ │ │ ├── query-875294dc5cf87bcf302fb9e87933745cc1c57bbe3c3c69110592a07400116c7f.json
│ │ │ ├── query-89041298e272d15c21e2b7127bd16c5a4f48e2be87dc26e9d0e3a932c9c49dfb.json
│ │ │ ├── query-89edaec8661e435c3b71bb9b995cd711eb78a4d39608e897432d6124cd135938.json
│ │ │ ├── query-8acbdc892d44efb53529da1c2df65bea6b799a43cf4c9264a37d392847e6eff0.json
│ │ │ ├── query-8afada5220fefb0d01ed6f87d3d0ee8fca86b5cdce9320e190e3d3b8fd9f63bc.json
│ │ │ ├── query-8d240d72d651f59d53bed7380710038e9d00492b1e282237c0ec0e03bc36a9c0.json
│ │ │ ├── query-8ef27901b96b73826a431ad6c5fabecc18c36d8cdba8db3b47953855fa5c9035.json
│ │ │ ├── query-8ef977487429f84c557dc62272c47e411b96b2376288a90c242034295e1a147e.json
│ │ │ ├── query-8f4f071f844281fb14ecd99db3261540441b14c8206038fdc4a4336bbae3f382.json
│ │ │ ├── query-8f5ce493e8b8473ba03d5263915a8b231f9e7c211ab83487536008e48316c269.json
│ │ │ ├── query-90875bdd2f75cdf0dc3f48dc2516f5c701411387c939f6b8a3478b41b3de4f20.json
│ │ │ ├── query-90fe32cb9c88a262a682c0db700fef7d69d6ce0be1f930d9f16c50b921a8b819.json
│ │ │ ├── query-91a3ee5ad64a947b7807a590f6b014c6856229918b972b98946f98b75686ab6c.json
│ │ │ ├── query-926cb81dc7931890a02c5a372aef79832e5d0748dad18ab44c6671f3196d6f60.json
│ │ │ ├── query-92c8eb526fcc5de6874eb0fab1d71fb1ed3dafe2bd1a49aa72e4f4862931c6c2.json
│ │ │ ├── query-933d2bed9c00eb9b37bfe757266ead15df5e0a4209ff47dcf4a5f19d35154e89.json
│ │ │ ├── query-966ca0f7eebd2896c007b2fd6e9327d03b29fe413d57cce21c67b6d539f59e7d.json
│ │ │ ├── query-98a5491eb5f10997ac1f3718c835903ac99d9bb8ca4d79c908b25a6d1209b9b1.json
│ │ │ ├── query-99394fbd9c07d6d24429934b3f7344dfab024b42e47ddc7bd9e551897ba6e9b8.json
│ │ │ ├── query-9b7363000017fa3dee46441bc0679cb16f9f8df08fa258cc907007fb9bcd0bc7.json
│ │ │ ├── query-9c9c65d4ca6847761d8f999253590082672b3782875cf3f5ba0b2f9d26e3a507.json
│ │ │ ├── query-9e8152d445f9996b221ad3690ba982ad01035296bf4539ca5620a043924a7292.json
│ │ │ ├── query-9eaf35f045aaca8473efc4a1f529afe24f01d9ec34609f373db5c535ccb58516.json
│ │ │ ├── query-9f7bdc034c618e47e49c467d0d7f5b8c297d055abe248cc876dbc12c5a7dc920.json
│ │ │ ├── query-9fe87eeaf4b7d0ba09b59ddad3476eb57ccb6e4053ab8f4450dd4a9d1f6ba108.json
│ │ │ ├── query-a0be6c56e470382b9470df414497e260ba8911123744980e24a52bc9b95bd056.json
│ │ │ ├── query-a2f7433f06fb4f6a7ad5ac6c1db18705276bce41e9b19d5d7e910ad4b767fb5e.json
│ │ │ ├── query-a50eb326c3522f971f6ee7e13dff61efbeb1ec24e2c694e1673347bae993762d.json
│ │ │ ├── query-a63a217981b97448ddcc96b2489ddd9d3bc8c99b5b8b1d373939fc3ae9715c27.json
│ │ │ ├── query-a7094d84d313602729fde155cfbe63041fca7cbab407f98452462ec45e3cfd16.json
│ │ │ ├── query-a75a6a08c9639053cfc3cffa9d4a009785f358b334f5c586c2e358f0d0b4d856.json
│ │ │ ├── query-a7f780528882a2ae66c45435215763eed0582264861436eab3f862e3eb12cab1.json
│ │ │ ├── query-ab34912b42a48a8b5c8d63e271b99b7d0b690a2471873c6654b1b6cf2079b95c.json
│ │ │ ├── query-ae6bf8958c4d9837d63f56574e91f91acc6076a8521adc3e30a83bf70e2121a0.json
│ │ │ ├── query-afa86e79e3de2a83265cb0db8549d378a2f11b2a27bbd86d60558318c87eb698.json
│ │ │ ├── query-b3568613352efae1125a88565d886157d96866f7ef9b09b03a45ba4322664bd0.json
│ │ │ ├── query-b60d34f4d250c12f75dba10491c1337d69aebad12be6fbfbdde91e34083ba4ed.json
│ │ │ ├── query-b6c4f4a23968cba2a82c2b7cfffc05a7ed582c9e5c1f65d27b0686f843ccfe42.json
│ │ │ ├── query-b700dc3f7d0f86f4904725d8357e34b7e457f857ed37c467c314142877fd5367.json
│ │ │ ├── query-b74e4d620bed4832a4e8e713a346691f260a7eca4bf494d6fb11c7cf699adaad.json
│ │ │ ├── query-b91cc2458e1a530e7cadbd1ca3e2eaf93e1c44108b6770a24c9a24ac29db37d3.json
│ │ │ ├── query-b992283a9b43cbb8f86149f3f55cb47fb628dabd8fadc50e6a5772903f851e1c.json
│ │ │ ├── query-bb0f782756c274c06c1b63af6fc3ac2a7cedfd4247b57f062d348b4b1b36bef1.json
│ │ │ ├── query-bb141d28c0c82244f31d542038c314d05ceb3a7b8f35397c0faef3b36d2d14a7.json
│ │ │ ├── query-bbf62633c561706a762089bbab2f76a9ba3e2ed3539ef16accb601fb609c2ec9.json
│ │ │ ├── query-c09e0bb0378d9dfb15de7f2f1209fab6ea87589819128e6fc9ed5da11dfc2770.json
│ │ │ ├── query-c29fa41743811a6ac3a9b952b6ea75d18e914f823902587b63c9f295407144b1.json
│ │ │ ├── query-c5e7dbb22488aca427b85b3415bd1f1a1766ff865f2e08a5daa095d2a1ccbd56.json
│ │ │ ├── query-c960f4f5571ee68816c49898125979f3c78c2caca52cb4b8dc9880e669a1f23e.json
│ │ │ ├── query-c984ae0496d0bd7520ee3d6761ce6a4f61a6a2001b597e4c63ba4588ec5cf530.json
│ │ │ ├── query-ca093cab5143bb3dded2eda9e82473215f4d3c549ea2c5a4f860a102cc46a667.json
│ │ │ ├── query-cc60ad934d347fb4546205d1fe07e9d2f127cb15b1bb650d1ea3805a4c55b196.json
│ │ │ ├── query-ce36eb8d3e4478a4e8520919ff41f1a5e6470cef581b1638f5578546dd28c4df.json
│ │ │ ├── query-cf2eeca6d8dbc2cc72160a26e81f6e963096edb610183ba13cbbbd3d95c4134b.json
│ │ │ ├── query-cf654533cfed946e9ac52dbcea1f50be3dfdac0fbfb1e8a0204c0c9c103ba5b0.json
│ │ │ ├── query-d02248136aa6b27636814dee4e0bc38395ab6c6fdf979616fa16fc490897cee3.json
│ │ │ ├── query-d0355d4e98bec6120f17d8cf81ac8c30ed19e9cebd0c8e7c7918b1c3ca0e3cba.json
│ │ │ ├── query-d26e42d9fd2b2ee3cf9702c1666d83e7cffa26b320ae1442c7f3e22376c4a4ee.json
│ │ │ ├── query-d4bc51c30f1119ea9d117fb565ec554d63c8773040679a77e99ac3fa24cec71d.json
│ │ │ ├── query-d7a0e4fa2f168976505405c7e7800847f3379f7b57c0972659a35bfb68b0f6cd.json
│ │ │ ├── query-d8f0b02952e786dd4309eac9de04a359aea3a46e5d4e07764cec56ce5d6609c0.json
│ │ │ ├── query-d95cd1b4bcfa1d7bb236d49e1956fcc9a684609956972fe4f95aac13f30b2530.json
│ │ │ ├── query-da02f93d7346992a9795f12b900f91ac0b326dd751c0d374d6ef4d19f671d22e.json
│ │ │ ├── query-dbf4be84eeff9ea51b00185faae2d453ab449017ed492bf6711dc7fceb630880.json
│ │ │ ├── query-dca9b361c4409b14498b85f192b0034201575a49e0240ac6715b55ad8d381d0e.json
│ │ │ ├── query-dd02cc4a48123c28b34da8501060096c33df9e30611ef89d01bf0502119cbbe1.json
│ │ │ ├── query-dda97742d389ffeeaab33d352d05767e2150f7da3cf384a7f44741c769f44144.json
│ │ │ ├── query-e02ea83d195cb58fa8525e66a6ac1dddae3f1dfb1ef48494f6aee3fd03abe6f6.json
│ │ │ ├── query-e1746b33c2f0d10f26332195f78e1ef2f192ca66f8000d1385626154e5ce4f7e.json
│ │ │ ├── query-e291be0434ab9c346dee777e50f8e601f12c8003fe93a5ecb110d02642d14c3c.json
│ │ │ ├── query-e35d56de7136d43d0803ec825b0612e4185cef838f105d66f18cb24865e45140.json
│ │ │ ├── query-e62d043f86e7232e6e9433631f8273e7ed0770c81071cf1f17516d3a45881ae9.json
│ │ │ ├── query-e68a7084d44462d19f30902d7e6c1bd60bb771c6f075df15ab0137a7ffc896da.json
│ │ │ ├── query-e8e48db74ac1ab5baa1e4b121643cfa33a0bf3328df6e869464fe7f31429b81e.json
│ │ │ ├── query-e99ab37ab3e03ad9c48792772b09bac77b09f67e623d5371ab4dadbe2d41fa1c.json
│ │ │ ├── query-eb095f64bec5ac885683a8c6708320760971317c4519fae7af9d44e2be50985d.json
│ │ │ ├── query-f0b4af5a9d6f1cc707a935fd5f34526a54ebbed8eef8f885f3a6723bc8490908.json
│ │ │ ├── query-f41f76c94cd68fca2285b1cc60f426603c84df4ef1c6ce5dc441a63d2dc46f6e.json
│ │ │ ├── query-f46e87bbb149b35e1d13b2b3cd2bdeab3c28a56a395f52f001a7bb013a5dfece.json
│ │ │ ├── query-f50b7fb5a2c09e7b7e89e2addb0ca42c790c101a3fc9442862b5885d5116325a.json
│ │ │ ├── query-f5c2ec9b7038d7ed36091e670f9bf34f8aa9ea8ed50929731845e32dc3176e39.json
│ │ │ ├── query-f8182fd162ffb018d4f102fa7ddbc9991135065e81af8f77b5beef9405607577.json
│ │ │ ├── query-fbf926f630df5d588df4f1c9c0dc0f594332be5829d5d7c6b66183ac25b3d166.json
│ │ │ ├── query-fc9925e19000d79c0bb020ea44e13cbb364b3505626d34550e38f6f7397b9d42.json
│ │ │ ├── query-fca331753aeccddbad96d06fc9d066dcefebe978a7af477bb6b55faa1d31e9b1.json
│ │ │ ├── query-fcd8b4b9e003d1540357c6bf1ff9c715560d011d4c01112703a9c046170c84f1.json
│ │ │ ├── query-fd32368fa6cd16a9704cdea54f7729681d450669563dd1178c492ffce51e5ff2.json
│ │ │ ├── query-fd8f3e7ff02d4d1f465aad32edcb06a842cabc787279ba7d690f69b59ad3eb50.json
│ │ │ ├── query-fe7bd146523e4bb321cb234d6bf9f3005b55c654897a8e46dc933c7fd2263c7c.json
│ │ │ └── query-ffbfef8b7e72ec4bae02b6bbe862980b5fe575ae8432a000e9c4e4307caa2d9b.json
│ │ ├── Cargo.toml
│ │ ├── build.rs
│ │ ├── migrations/
│ │ │ ├── 20220530084123_jobs_workers.sql
│ │ │ ├── 20221018142001_init.sql
│ │ │ ├── 20221121151402_upstream_oauth.sql
│ │ │ ├── 20221213145242_password_schemes.sql
│ │ │ ├── 20230408234928_add_get_jobs_fn_.sql
│ │ │ ├── 20230616093555_compat_admin_flag.sql
│ │ │ ├── 20230621140528_upstream_oauth_claims_imports.sql
│ │ │ ├── 20230626130338_oauth_clients_static.sql
│ │ │ ├── 20230728154304_user_lock.sql
│ │ │ ├── 20230823125247_drop_apalis_push_job.sql
│ │ │ ├── 20230828085439_oauth2_clients_more_fields.sql
│ │ │ ├── 20230828143553_user_session_authentication_source.sql
│ │ │ ├── 20230829092920_oauth2_sessions_user_id_scope_list.sql
│ │ │ ├── 20230829141928_user_session_user_agent.sql
│ │ │ ├── 20230904135550_oauth2_client_credentials_grant.sql
│ │ │ ├── 20230911091636_oauth2_token_expiration.sql
│ │ │ ├── 20230919155444_record_session_last_activity.sql
│ │ │ ├── 20231009142904_user_can_request_admin.sql
│ │ │ ├── 20231116104353_upstream_oauth_overrides.sql
│ │ │ ├── 20231120110559_upstream_oauth_branding.sql
│ │ │ ├── 20231207090532_oauth_device_code_grant.sql
│ │ │ ├── 20231208155602_oauth_clients_device_code_grant.sql
│ │ │ ├── 20240207100003_user_terms.sql
│ │ │ ├── 20240220141353_nonunique_compat_device_id.sql
│ │ │ ├── 20240220150201_compat_sessions_user_sessions_link.sql
│ │ │ ├── 20240221164945_sessions_user_agent.sql
│ │ │ ├── 20240301091201_upstream_oauth_additional_parameters.sql
│ │ │ ├── 20240402084854_upstream_oauth_disabled_at.sql
│ │ │ ├── 20240621080509_user_recovery.sql
│ │ │ ├── 20240718075125_sessions_active_index.sql
│ │ │ ├── 20241004075132_queue_worker.sql
│ │ │ ├── 20241004121132_queue_job.sql
│ │ │ ├── 20241007160050_oidc_login_hint.sql
│ │ │ ├── 20241115163340_upstream_oauth2_response_mode.sql
│ │ │ ├── 20241118115314_upstream_oauth2_extra_query_params.sql
│ │ │ ├── 20241120163320_queue_job_failures.sql
│ │ │ ├── 20241122130349_queue_job_scheduled.sql
│ │ │ ├── 20241122133435_queue_job_scheduled_index.sql
│ │ │ ├── 20241124145741_upstream_oauth_userinfo.sql
│ │ │ ├── 20241125110803_queue_job_recurrent.sql
│ │ │ ├── 20241129091057_upstream_oauth2_link_account_name.sql
│ │ │ ├── 20241202123523_upstream_oauth_responses_alg.sql
│ │ │ ├── 20241210115428_oauth_refresh_token_track_next.sql
│ │ │ ├── 20241210133651_oauth2_access_token_first_used.sql
│ │ │ ├── 20241212154426_oauth2_response_mode_null.sql
│ │ │ ├── 20241213180524_upstream_oauth_optional_issuer.sql
│ │ │ ├── 20250109105709_user_email_authentication_codes.sql
│ │ │ ├── 20250113102144_user_registrations.sql
│ │ │ ├── 20250114135939_allow_deviceless_compat_sessions.sql
│ │ │ ├── 20250115155255_cleanup_unverified_emails.sql
│ │ │ ├── 20250124151529_unsupported_threepids_table.sql
│ │ │ ├── 20250129154003_compat_sessions_device_name.sql
│ │ │ ├── 20250130170011_user_is_guest.sql
│ │ │ ├── 20250225091000_dynamic_policy_data.sql
│ │ │ ├── 20250311093145_user_deactivated_at.sql
│ │ │ ├── 20250312094013_upstream_oauth2_providers_order.sql
│ │ │ ├── 20250317151803_upstream_oauth_session_unlinked_at.sql
│ │ │ ├── 20250325102310_oauth2_clients_hash.sql
│ │ │ ├── 20250404105103_compat_sso_login_browser_session.sql
│ │ │ ├── 20250410000000_idx_compat_access_tokens_session_fk.sql
│ │ │ ├── 20250410000001_idx_compat_refresh_tokens_session_fk.sql
│ │ │ ├── 20250410000002_idx_compat_refresh_tokens_access_token_fk.sql
│ │ │ ├── 20250410000003_idx_compat_sessions_user_fk.sql
│ │ │ ├── 20250410000004_idx_compat_sessions_user_session_fk.sql
│ │ │ ├── 20250410000005_drop_compat_sessions_user_id_last_active_at.sql
│ │ │ ├── 20250410000006_idx_compat_sso_logins_session_fk.sql
│ │ │ ├── 20250410000007_idx_oauth2_access_tokens_session_fk.sql
│ │ │ ├── 20250410000008_idx_oauth2_authorization_grants_session_fk.sql
│ │ │ ├── 20250410000009_idx_oauth2_authorization_grants_client_fk.sql
│ │ │ ├── 20250410000010_idx_oauth2_consents_client_fk.sql
│ │ │ ├── 20250410000011_idx_oauth2_consents_user_fk.sql
│ │ │ ├── 20250410000012_idx_oauth2_device_code_grants_client_fk.sql
│ │ │ ├── 20250410000013_idx_oauth2_device_code_grants_session_fk.sql
│ │ │ ├── 20250410000014_idx_oauth2_device_code_grants_user_session_fk.sql
│ │ │ ├── 20250410000015_idx_oauth2_refresh_tokens_session_fk.sql
│ │ │ ├── 20250410000016_idx_oauth2_refresh_tokens_access_token_fk.sql
│ │ │ ├── 20250410000017_idx_oauth2_refresh_tokens_next_refresh_token_fk.sql
│ │ │ ├── 20250410000018_idx_oauth2_sessions_user_session_fk.sql
│ │ │ ├── 20250410000019_idx_oauth2_sessions_client_fk.sql
│ │ │ ├── 20250410000020_idx_oauth2_sessions_user_fk.sql
│ │ │ ├── 20250410000021_drop_oauth2_sessions_user_id_last_active_at.sql
│ │ │ ├── 20250410000022_idx_queue_jobs_started_by_fk.sql
│ │ │ ├── 20250410000023_idx_queue_jobs_next_attempt_fk.sql
│ │ │ ├── 20250410000024_idx_queue_jobs_schedule_name_fk.sql
│ │ │ ├── 20250410000025_idx_upstream_oauth_authorization_sessions_provider_fk.sql
│ │ │ ├── 20250410000026_idx_upstream_oauth_authorization_sessions_link_fk.sql
│ │ │ ├── 20250410000027_idx_upstream_oauth_links_provider_fk.sql
│ │ │ ├── 20250410000028_idx_upstream_oauth_links_user_fk.sql
│ │ │ ├── 20250410000029_idx_user_email_authentication_codes_authentication_fk.sql
│ │ │ ├── 20250410000030_idx_user_email_authentications_user_session_fk.sql
│ │ │ ├── 20250410000031_idx_user_email_authentications_user_registration_fk.sql
│ │ │ ├── 20250410000032_idx_user_emails_user_fk.sql
│ │ │ ├── 20250410000033_idx_user_emails_email_idx.sql
│ │ │ ├── 20250410000034_idx_user_passwords_user_fk.sql
│ │ │ ├── 20250410000035_idx_user_recovery_tickets_session_fk.sql
│ │ │ ├── 20250410000036_idx_user_recovery_tickets_user_email_fk.sql
│ │ │ ├── 20250410000037_idx_user_registrations_email_authentication_fk.sql
│ │ │ ├── 20250410000038_idx_user_session_authentications_user_session_fk.sql
│ │ │ ├── 20250410000039_idx_user_session_authentications_user_password_fk.sql
│ │ │ ├── 20250410000040_idx_user_session_authentications_upstream_oauth_session_fk.sql
│ │ │ ├── 20250410000041_idx_user_sessions_user_fk.sql
│ │ │ ├── 20250410000042_drop_user_sessions_user_id_last_active_at.sql
│ │ │ ├── 20250410000043_idx_user_terms_user_fk.sql
│ │ │ ├── 20250410000044_idx_users_primary_email_fk.sql
│ │ │ ├── 20250410000045_idx_user_recovery_tickets_ticket_idx.sql
│ │ │ ├── 20250410121612_users_lower_username_idx.sql
│ │ │ ├── 20250410174306_oauth2_authorization_default_requires_consent.sql
│ │ │ ├── 20250424150930_oauth2_grants_locale.sql
│ │ │ ├── 20250425113717_oauth2_session_human_name.sql
│ │ │ ├── 20250506161158_upstream_oauth2_forward_login_hint.sql
│ │ │ ├── 20250507131948_upstream_oauth_session_optional_nonce.sql
│ │ │ ├── 20250602212100_user_registration_tokens.sql
│ │ │ ├── 20250602212101_idx_user_registration_token.sql
│ │ │ ├── 20250602212102_upstream_oauth2_id_token_claims.sql
│ │ │ ├── 20250602212103_upstream_oauth2_id_token_claims_sub_sid_index.sql
│ │ │ ├── 20250602212104_upstream_oauth2_id_token_claims_sid_sub_index.sql
│ │ │ ├── 20250630120643_upstream_oauth_on_backchannel_logout.sql
│ │ │ ├── 20250708155857_idx_user_emails_lower_email.sql
│ │ │ ├── 20250709142230_id_token_claims_trigger.sql
│ │ │ ├── 20250709142240_backfill_id_token_claims.sql
│ │ │ ├── 20250915092000_pgtrgm_extension.sql
│ │ │ ├── 20250915092635_users_username_trgm_idx.sql
│ │ │ ├── 20250924132713_personal_access_tokens.sql
│ │ │ ├── 20251023134634_personal_access_tokens_unique_fix.sql
│ │ │ ├── 20251121145458_user_registration_upstream_oauth_session.sql
│ │ │ ├── 20251127145951_user_registration_upstream_oauth_session_idx.sql
│ │ │ ├── 20260108111542_remove_apalis.sql
│ │ │ ├── 20260108120030_remove_user_emails_old_confirmation.sql
│ │ │ ├── 20260108121127_cleanup_oauth2_consents.sql
│ │ │ ├── 20260108121952_cleanup_id_token_claims_trigger.sql
│ │ │ ├── 20260108144040_remove_deactivated_unsupported_threepids.sql
│ │ │ ├── 20260108145240_drop_oauth2_consents.sql
│ │ │ ├── 20260108175627_oauth_access_tokens_revoked_at_idx.sql
│ │ │ ├── 20260109115009_oauth_access_tokens_expires_at_idx.sql
│ │ │ ├── 20260109172537_oauth_refresh_token_revoked_at.sql
│ │ │ ├── 20260109172950_oauth_refresh_token_next_token_set_null.sql
│ │ │ ├── 20260109172954_oauth_refresh_token_next_token_set_null_validate.sql
│ │ │ ├── 20260112094550_oauth_refresh_token_not_consumed_idx.sql
│ │ │ ├── 20260112094837_oauth_refresh_token_consumed_at_idx.sql
│ │ │ ├── 20260115111313_idx_compat_sessions_finished_at.sql
│ │ │ ├── 20260116000002_idx_upstream_oauth_links_orphaned.sql
│ │ │ ├── 20260116000003_queue_jobs_next_attempt_set_null.sql
│ │ │ ├── 20260116000004_queue_jobs_next_attempt_set_null_validate.sql
│ │ │ ├── 20260121103025_upstream_oauth_track_user_session.sql
│ │ │ ├── 20260121104214_upstream_auth_user_session_fk_idx.sql
│ │ │ ├── 20260121112201_upstream_oauth_sessions_orphan_index.sql
│ │ │ ├── 20260121121140_upstream_oauth_track_user_session_trigger.sql
│ │ │ ├── 20260121121150_upstream_oauth_track_user_session_backfill.sql
│ │ │ ├── 20260122113523_compat_sessions_user_session_no_action.sql
│ │ │ ├── 20260122114353_compat_sessions_user_session_validate.sql
│ │ │ ├── 20260122123211_idx_oauth2_sessions_finished_at.sql
│ │ │ ├── 20260122124231_idx_user_sessions_finished_at.sql
│ │ │ ├── 20260123090000_idx_oauth2_sessions_inactive_ips.sql
│ │ │ ├── 20260123090001_idx_compat_sessions_inactive_ips.sql
│ │ │ ├── 20260123090002_idx_user_sessions_inactive_ips.sql
│ │ │ └── 20260324000001_queue_schedules_last_job_set_null.sql
│ │ └── src/
│ │ ├── app_session.rs
│ │ ├── compat/
│ │ │ ├── access_token.rs
│ │ │ ├── mod.rs
│ │ │ ├── refresh_token.rs
│ │ │ ├── session.rs
│ │ │ └── sso_login.rs
│ │ ├── errors.rs
│ │ ├── filter.rs
│ │ ├── iden.rs
│ │ ├── lib.rs
│ │ ├── oauth2/
│ │ │ ├── access_token.rs
│ │ │ ├── authorization_grant.rs
│ │ │ ├── client.rs
│ │ │ ├── device_code_grant.rs
│ │ │ ├── mod.rs
│ │ │ ├── refresh_token.rs
│ │ │ └── session.rs
│ │ ├── pagination.rs
│ │ ├── personal/
│ │ │ ├── access_token.rs
│ │ │ ├── mod.rs
│ │ │ └── session.rs
│ │ ├── policy_data.rs
│ │ ├── queue/
│ │ │ ├── job.rs
│ │ │ ├── mod.rs
│ │ │ ├── schedule.rs
│ │ │ └── worker.rs
│ │ ├── repository.rs
│ │ ├── telemetry.rs
│ │ ├── tracing.rs
│ │ ├── upstream_oauth2/
│ │ │ ├── link.rs
│ │ │ ├── mod.rs
│ │ │ ├── provider.rs
│ │ │ └── session.rs
│ │ └── user/
│ │ ├── email.rs
│ │ ├── mod.rs
│ │ ├── password.rs
│ │ ├── recovery.rs
│ │ ├── registration.rs
│ │ ├── registration_token.rs
│ │ ├── session.rs
│ │ ├── terms.rs
│ │ └── tests.rs
│ ├── syn2mas/
│ │ ├── .sqlx/
│ │ │ ├── query-026adeffc646b41ebc096bb874d110039b9a4a0425fd566e401f56ea215de0dd.json
│ │ │ ├── query-07ec66733b67a9990cc9d483b564c8d05c577cf8f049d8822746c7d1dbd23752.json
│ │ │ ├── query-09db58b250c20ab9d1701653165233e5c9aabfdae1f0ee9b77c909b2bb2f3e25.json
│ │ │ ├── query-12112011318abc0bdd7f722ed8c5d4a86bf5758f8c32d9d41a22999b2f0698ca.json
│ │ │ ├── query-1d1004d0fb5939fbf30c1986b80b986b1b4864a778525d0b8b0ad6678aef3e9f.json
│ │ │ ├── query-204cf4811150a7fdeafa9373647a9cd62ac3c9e58155882858c6056e2ef6c30d.json
│ │ │ ├── query-207b880ec2dd484ad05a7138ba485277958b66e4534561686c073e282fafaf2a.json
│ │ │ ├── query-24f6ce6280dc6675ab1ebdde0c5e3db8ff7a686180d71052911879f186ed1c8e.json
│ │ │ ├── query-486f3177dcf6117c6b966954a44d9f96a754eba64912566e81a90bd4cbd186f0.json
│ │ │ ├── query-5b4840f42ae00c5dc9f59f2745d664b16ebd813dfa0aa32a6d39dd5c393af299.json
│ │ │ ├── query-69aa96208513c3ea64a446c7739747fcb5e79d7e8c1212b2a679c3bde908ce93.json
│ │ │ ├── query-78ed3bf1032cd678b42230d68fb2b8e3d74161c8b6c5fe1a746b6958ccd2fd84.json
│ │ │ ├── query-86b2b02fbb6350100d794e4d0fa3c67bf00fd3e411f769b9f25dec27428489ed.json
│ │ │ ├── query-979bedd942b4f71c58f3672f2917cee05ac1a628e51fe61ba6dfed253e0c63c2.json
│ │ │ ├── query-b27828d7510d52456b50b4c4b9712878ee329ca72070d849eb61ac9c8f9d1c76.json
│ │ │ └── query-ebf68b70b3e22a04b57b5587b4b099255155193dafbbd185cd8f26d93ff423a7.json
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── lib.rs
│ │ │ ├── mas_writer/
│ │ │ │ ├── checks.rs
│ │ │ │ ├── constraint_pausing.rs
│ │ │ │ ├── fixtures/
│ │ │ │ │ └── upstream_provider.sql
│ │ │ │ ├── locking.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── snapshots/
│ │ │ │ │ ├── syn2mas__mas_writer__test__write_user.snap
│ │ │ │ │ ├── syn2mas__mas_writer__test__write_user_with_access_token.snap
│ │ │ │ │ ├── syn2mas__mas_writer__test__write_user_with_device.snap
│ │ │ │ │ ├── syn2mas__mas_writer__test__write_user_with_email.snap
│ │ │ │ │ ├── syn2mas__mas_writer__test__write_user_with_password.snap
│ │ │ │ │ ├── syn2mas__mas_writer__test__write_user_with_refresh_token.snap
│ │ │ │ │ ├── syn2mas__mas_writer__test__write_user_with_unsupported_threepid.snap
│ │ │ │ │ └── syn2mas__mas_writer__test__write_user_with_upstream_provider_link.snap
│ │ │ │ ├── syn2mas_revert_temporary_tables.sql
│ │ │ │ └── syn2mas_temporary_tables.sql
│ │ │ ├── migration.rs
│ │ │ ├── progress.rs
│ │ │ ├── synapse_reader/
│ │ │ │ ├── checks.rs
│ │ │ │ ├── config/
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ └── oidc.rs
│ │ │ │ ├── fixtures/
│ │ │ │ │ ├── access_token_alice.sql
│ │ │ │ │ ├── access_token_alice_with_puppet.sql
│ │ │ │ │ ├── access_token_alice_with_refresh_token.sql
│ │ │ │ │ ├── access_token_alice_with_unused_refresh_token.sql
│ │ │ │ │ ├── devices_alice.sql
│ │ │ │ │ ├── external_ids_alice.sql
│ │ │ │ │ ├── threepids_alice.sql
│ │ │ │ │ └── user_alice.sql
│ │ │ │ ├── mod.rs
│ │ │ │ └── snapshots/
│ │ │ │ ├── syn2mas__synapse_reader__test__read_access_and_refresh_tokens.snap
│ │ │ │ ├── syn2mas__synapse_reader__test__read_access_and_unused_refresh_tokens.snap
│ │ │ │ ├── syn2mas__synapse_reader__test__read_access_token.snap
│ │ │ │ ├── syn2mas__synapse_reader__test__read_devices.snap
│ │ │ │ ├── syn2mas__synapse_reader__test__read_external_ids.snap
│ │ │ │ ├── syn2mas__synapse_reader__test__read_threepids.snap
│ │ │ │ └── syn2mas__synapse_reader__test__read_users.snap
│ │ │ └── telemetry.rs
│ │ └── test_synapse_migrations/
│ │ ├── 20250117064958_users.sql
│ │ ├── 20250128141011_threepids.sql
│ │ ├── 20250128162513_external_ids.sql
│ │ ├── 20250128201100_access_and_refresh_tokens.sql
│ │ └── 20250129140230_devices.sql
│ ├── tasks/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── cleanup/
│ │ │ ├── misc.rs
│ │ │ ├── mod.rs
│ │ │ ├── oauth.rs
│ │ │ ├── sessions.rs
│ │ │ ├── tokens.rs
│ │ │ └── user.rs
│ │ ├── email.rs
│ │ ├── lib.rs
│ │ ├── matrix.rs
│ │ ├── new_queue.rs
│ │ ├── recovery.rs
│ │ ├── sessions.rs
│ │ └── user.rs
│ ├── templates/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── context/
│ │ │ ├── branding.rs
│ │ │ ├── captcha.rs
│ │ │ ├── ext.rs
│ │ │ └── features.rs
│ │ ├── context.rs
│ │ ├── forms.rs
│ │ ├── functions.rs
│ │ ├── lib.rs
│ │ └── macros.rs
│ └── tower/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ ├── metrics/
│ │ ├── duration.rs
│ │ ├── in_flight.rs
│ │ ├── make_attributes.rs
│ │ └── mod.rs
│ ├── trace_context.rs
│ ├── tracing/
│ │ ├── enrich_span.rs
│ │ ├── future.rs
│ │ ├── layer.rs
│ │ ├── make_span.rs
│ │ ├── mod.rs
│ │ └── service.rs
│ └── utils.rs
├── deny.toml
├── docker-bake.hcl
├── docs/
│ ├── README.md
│ ├── SUMMARY.md
│ ├── api/
│ │ ├── index.html
│ │ ├── oauth2-redirect.html
│ │ └── spec.json
│ ├── as-login.md
│ ├── config.schema.json
│ ├── development/
│ │ ├── architecture.md
│ │ ├── cleanup-jobs.md
│ │ ├── contributing.md
│ │ ├── database.md
│ │ ├── graphql.md
│ │ └── releasing.md
│ ├── reference/
│ │ ├── cli/
│ │ │ ├── README.md
│ │ │ ├── config.md
│ │ │ ├── database.md
│ │ │ ├── doctor.md
│ │ │ ├── manage.md
│ │ │ ├── server.md
│ │ │ ├── syn2mas.md
│ │ │ ├── templates.md
│ │ │ └── worker.md
│ │ ├── configuration.md
│ │ └── scopes.md
│ ├── rustdoc/
│ │ └── mas_handlers/
│ │ └── README.md
│ ├── setup/
│ │ ├── README.md
│ │ ├── database.md
│ │ ├── general.md
│ │ ├── homeserver.md
│ │ ├── installation.md
│ │ ├── migration.md
│ │ ├── reverse-proxy.md
│ │ ├── running.md
│ │ └── sso.md
│ ├── storybook/
│ │ └── README.md
│ └── topics/
│ ├── access-token.md
│ ├── admin-api.md
│ ├── authorization.md
│ └── policy.md
├── frontend/
│ ├── .browserlistrc
│ ├── .gitignore
│ ├── .npmrc
│ ├── .postcssrc.json
│ ├── .storybook/
│ │ ├── locales.ts
│ │ ├── main.ts
│ │ ├── preview-head.html
│ │ ├── preview.tsx
│ │ └── public/
│ │ └── mockServiceWorker.js
│ ├── codegen.ts
│ ├── graphql.config.json
│ ├── i18next.config.ts
│ ├── index.html
│ ├── knip.config.ts
│ ├── locales/
│ │ ├── cs.json
│ │ ├── da.json
│ │ ├── de.json
│ │ ├── en.json
│ │ ├── et.json
│ │ ├── fi.json
│ │ ├── fr.json
│ │ ├── hu.json
│ │ ├── nb-NO.json
│ │ ├── nl.json
│ │ ├── pl.json
│ │ ├── pt-BR.json
│ │ ├── pt.json
│ │ ├── ru.json
│ │ ├── sk.json
│ │ ├── sv.json
│ │ ├── uk.json
│ │ ├── uz.json
│ │ └── zh-Hans.json
│ ├── package.json
│ ├── schema.graphql
│ ├── src/
│ │ ├── @types/
│ │ │ └── i18next.d.ts
│ │ ├── components/
│ │ │ ├── AccountDeleteButton.tsx
│ │ │ ├── AccountManagementPasswordPreview/
│ │ │ │ ├── AccountManagementPasswordPreview.module.css
│ │ │ │ ├── AccountManagementPasswordPreview.tsx
│ │ │ │ └── index.ts
│ │ │ ├── BrowserSession.tsx
│ │ │ ├── ButtonLink.module.css
│ │ │ ├── ButtonLink.tsx
│ │ │ ├── Client/
│ │ │ │ ├── OAuth2ClientDetail.test.tsx
│ │ │ │ ├── OAuth2ClientDetail.tsx
│ │ │ │ └── __snapshots__/
│ │ │ │ └── OAuth2ClientDetail.test.tsx.snap
│ │ │ ├── Collapsible/
│ │ │ │ ├── Collapsible.module.css
│ │ │ │ ├── Collapsible.stories.tsx
│ │ │ │ ├── Collapsible.tsx
│ │ │ │ └── index.ts
│ │ │ ├── CompatSession.test.tsx
│ │ │ ├── CompatSession.tsx
│ │ │ ├── DateTime.stories.tsx
│ │ │ ├── DateTime.tsx
│ │ │ ├── Dialog/
│ │ │ │ ├── Dialog.module.css
│ │ │ │ ├── Dialog.stories.tsx
│ │ │ │ ├── Dialog.tsx
│ │ │ │ └── index.ts
│ │ │ ├── EmptyState/
│ │ │ │ ├── EmptyState.module.css
│ │ │ │ ├── EmptyState.stories.tsx
│ │ │ │ ├── EmptyState.tsx
│ │ │ │ └── index.ts
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── ExternalLink/
│ │ │ │ ├── ExternalLink.module.css
│ │ │ │ └── ExternalLink.tsx
│ │ │ ├── Filter/
│ │ │ │ ├── Filter.module.css
│ │ │ │ ├── Filter.stories.tsx
│ │ │ │ ├── Filter.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Footer/
│ │ │ │ ├── Footer.module.css
│ │ │ │ ├── Footer.stories.tsx
│ │ │ │ ├── Footer.tsx
│ │ │ │ └── index.ts
│ │ │ ├── GenericError.module.css
│ │ │ ├── GenericError.tsx
│ │ │ ├── Layout/
│ │ │ │ ├── Layout.module.css
│ │ │ │ ├── Layout.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Link.tsx
│ │ │ ├── LoadingScreen/
│ │ │ │ ├── LoadingScreen.module.css
│ │ │ │ ├── LoadingScreen.stories.tsx
│ │ │ │ ├── LoadingScreen.test.tsx
│ │ │ │ ├── LoadingScreen.tsx
│ │ │ │ ├── __snapshots__/
│ │ │ │ │ └── LoadingScreen.test.tsx.snap
│ │ │ │ └── index.ts
│ │ │ ├── LoadingSpinner/
│ │ │ │ ├── LoadingSpinner.module.css
│ │ │ │ ├── LoadingSpinner.stories.tsx
│ │ │ │ ├── LoadingSpinner.tsx
│ │ │ │ └── index.ts
│ │ │ ├── NavBar/
│ │ │ │ ├── NavBar.module.css
│ │ │ │ ├── NavBar.stories.tsx
│ │ │ │ ├── NavBar.tsx
│ │ │ │ └── index.ts
│ │ │ ├── NavItem/
│ │ │ │ ├── NavItem.module.css
│ │ │ │ ├── NavItem.tsx
│ │ │ │ ├── NavItemErrorIcon.tsx
│ │ │ │ ├── __snapshots__/
│ │ │ │ │ └── NavItem.test.tsx.snap
│ │ │ │ └── index.ts
│ │ │ ├── NotFound.tsx
│ │ │ ├── OAuth2Session.test.tsx
│ │ │ ├── OAuth2Session.tsx
│ │ │ ├── PageHeading/
│ │ │ │ ├── PageHeading.module.css
│ │ │ │ ├── PageHeading.tsx
│ │ │ │ └── index.ts
│ │ │ ├── PaginationControls.tsx
│ │ │ ├── PasswordConfirmation.tsx
│ │ │ ├── PasswordCreationDoubleInput.tsx
│ │ │ ├── Separator/
│ │ │ │ ├── Separator.module.css
│ │ │ │ ├── Separator.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── Session/
│ │ │ │ ├── ClientAvatar.module.css
│ │ │ │ ├── ClientAvatar.test.tsx
│ │ │ │ ├── ClientAvatar.tsx
│ │ │ │ ├── DeviceTypeIcon.module.css
│ │ │ │ ├── DeviceTypeIcon.stories.tsx
│ │ │ │ ├── DeviceTypeIcon.test.tsx
│ │ │ │ ├── DeviceTypeIcon.tsx
│ │ │ │ ├── EndBrowserSessionButton.tsx
│ │ │ │ ├── EndCompatSessionButton.tsx
│ │ │ │ ├── EndOAuth2SessionButton.tsx
│ │ │ │ ├── EndSessionButton.tsx
│ │ │ │ ├── LastActive.module.css
│ │ │ │ ├── LastActive.stories.tsx
│ │ │ │ ├── LastActive.test.tsx
│ │ │ │ ├── LastActive.tsx
│ │ │ │ └── __snapshots__/
│ │ │ │ ├── ClientAvatar.test.tsx.snap
│ │ │ │ ├── DeviceTypeIcon.test.tsx.snap
│ │ │ │ ├── LastActive.test.tsx.snap
│ │ │ │ └── Session.test.tsx.snap
│ │ │ ├── SessionCard/
│ │ │ │ ├── SessionCard.module.css
│ │ │ │ ├── SessionCard.stories.tsx
│ │ │ │ ├── SessionCard.tsx
│ │ │ │ └── index.ts
│ │ │ ├── SessionDetail/
│ │ │ │ ├── BrowserSessionDetail.tsx
│ │ │ │ ├── CompatSessionDetail.test.tsx
│ │ │ │ ├── CompatSessionDetail.tsx
│ │ │ │ ├── EditSessionName.tsx
│ │ │ │ ├── OAuth2SessionDetail.test.tsx
│ │ │ │ ├── OAuth2SessionDetail.tsx
│ │ │ │ ├── SessionHeader.module.css
│ │ │ │ ├── SessionHeader.stories.tsx
│ │ │ │ ├── SessionHeader.test.tsx
│ │ │ │ ├── SessionHeader.tsx
│ │ │ │ ├── SessionInfo.tsx
│ │ │ │ └── __snapshots__/
│ │ │ │ ├── CompatSessionDetail.test.tsx.snap
│ │ │ │ ├── OAuth2SessionDetail.test.tsx.snap
│ │ │ │ └── SessionHeader.test.tsx.snap
│ │ │ ├── Typography.stories.tsx
│ │ │ ├── Typography.tsx
│ │ │ ├── UserEmail/
│ │ │ │ ├── UserEmail.module.css
│ │ │ │ ├── UserEmail.tsx
│ │ │ │ └── index.ts
│ │ │ ├── UserGreeting/
│ │ │ │ ├── UserGreeting.module.css
│ │ │ │ ├── UserGreeting.stories.tsx
│ │ │ │ ├── UserGreeting.tsx
│ │ │ │ └── index.ts
│ │ │ ├── UserProfile/
│ │ │ │ ├── AddEmailForm.tsx
│ │ │ │ └── UserEmailList.tsx
│ │ │ ├── UserSessionsOverview/
│ │ │ │ ├── BrowserSessionsOverview.module.css
│ │ │ │ ├── BrowserSessionsOverview.stories.tsx
│ │ │ │ ├── BrowserSessionsOverview.test.tsx
│ │ │ │ ├── BrowserSessionsOverview.tsx
│ │ │ │ └── __snapshots__/
│ │ │ │ ├── BrowserSessionsOverview.test.tsx.snap
│ │ │ │ └── UserSessionsOverview.test.tsx.snap
│ │ │ └── __snapshots__/
│ │ │ ├── CompatSession.test.tsx.snap
│ │ │ ├── LoadingScreen.test.tsx.snap
│ │ │ └── OAuth2Session.test.tsx.snap
│ │ ├── config.ts
│ │ ├── entrypoints/
│ │ │ ├── main.tsx
│ │ │ ├── shared.css
│ │ │ ├── swagger.ts
│ │ │ ├── templates.css
│ │ │ └── templates.ts
│ │ ├── gql/
│ │ │ ├── fragment-masking.ts
│ │ │ ├── gql.ts
│ │ │ ├── graphql.ts
│ │ │ └── index.ts
│ │ ├── graphql.ts
│ │ ├── i18n/
│ │ │ └── password_changes.ts
│ │ ├── i18n.ts
│ │ ├── pagination.ts
│ │ ├── routeTree.gen.ts
│ │ ├── router.tsx
│ │ ├── routes/
│ │ │ ├── __root.tsx
│ │ │ ├── _account.index.tsx
│ │ │ ├── _account.plan.index.tsx
│ │ │ ├── _account.sessions.browsers.tsx
│ │ │ ├── _account.sessions.index.tsx
│ │ │ ├── _account.tsx
│ │ │ ├── clients.$id.tsx
│ │ │ ├── devices.$.tsx
│ │ │ ├── emails.$id.in-use.tsx
│ │ │ ├── emails.$id.verify.tsx
│ │ │ ├── password.change.index.tsx
│ │ │ ├── password.change.success.tsx
│ │ │ ├── password.recovery.index.tsx
│ │ │ ├── reset-cross-signing.cancelled.tsx
│ │ │ ├── reset-cross-signing.index.tsx
│ │ │ ├── reset-cross-signing.success.tsx
│ │ │ ├── reset-cross-signing.tsx
│ │ │ └── sessions.$id.tsx
│ │ ├── styles/
│ │ │ ├── cpd-button.css
│ │ │ ├── cpd-checkbox-control.css
│ │ │ ├── cpd-form.css
│ │ │ ├── cpd-link.css
│ │ │ ├── cpd-mfa-control.css
│ │ │ └── cpd-text-control.css
│ │ ├── test-utils/
│ │ │ ├── mockLocale.ts
│ │ │ ├── render.tsx
│ │ │ └── router.tsx
│ │ ├── utils/
│ │ │ ├── dates.ts
│ │ │ ├── deviceIdFromScope.test.ts
│ │ │ ├── deviceIdFromScope.ts
│ │ │ ├── password_complexity/
│ │ │ │ ├── enwiki.json
│ │ │ │ ├── index.ts
│ │ │ │ ├── namesf.json
│ │ │ │ ├── namesm.json
│ │ │ │ ├── namess.json
│ │ │ │ ├── passwords.json
│ │ │ │ └── ustvfilm.json
│ │ │ └── simplifyUrl.ts
│ │ └── vite-env.d.ts
│ ├── stories/
│ │ └── routes/
│ │ ├── app.tsx
│ │ ├── index.stories.tsx
│ │ └── reset-cross-signing.stories.tsx
│ ├── tailwind.config.cjs
│ ├── tests/
│ │ ├── mocks/
│ │ │ └── handlers.ts
│ │ └── routes/
│ │ ├── __snapshots__/
│ │ │ └── reset-cross-signing.test.tsx.snap
│ │ ├── account/
│ │ │ ├── __snapshots__/
│ │ │ │ ├── index.test.tsx.snap
│ │ │ │ └── sessions.test.tsx.snap
│ │ │ ├── index.test.tsx
│ │ │ └── sessions.test.tsx
│ │ ├── render.tsx
│ │ ├── reset-cross-signing.test.tsx
│ │ └── types.d.ts
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ ├── vite.config.ts
│ ├── vitest.global-setup.ts
│ └── vitest.setup.ts
├── localazy.json
├── misc/
│ ├── build-docs.sh
│ ├── device-code-grant.sh
│ ├── sqlx_update.sh
│ └── update.sh
├── policies/
│ ├── .gitignore
│ ├── .regal/
│ │ └── config.yaml
│ ├── Makefile
│ ├── authorization_grant/
│ │ ├── authorization_grant.rego
│ │ └── authorization_grant_test.rego
│ ├── client_registration/
│ │ ├── client_registration.rego
│ │ └── client_registration_test.rego
│ ├── common/
│ │ ├── common.rego
│ │ └── common_test.rego
│ ├── compat_login/
│ │ ├── compat_login.rego
│ │ └── compat_login_test.rego
│ ├── email/
│ │ ├── email.rego
│ │ └── email_test.rego
│ ├── register/
│ │ ├── register.rego
│ │ └── register_test.rego
│ ├── schema/
│ │ ├── authorization_grant_input.json
│ │ ├── client_registration_input.json
│ │ ├── compat_login_input.json
│ │ ├── email_input.json
│ │ └── register_input.json
│ └── util/
│ └── coveralls.rego
├── templates/
│ ├── app.html
│ ├── base.html
│ ├── components/
│ │ ├── back_to_client.html
│ │ ├── button.html
│ │ ├── captcha.html
│ │ ├── errors.html
│ │ ├── field.html
│ │ ├── footer.html
│ │ ├── icon.html
│ │ ├── idp_brand.html
│ │ ├── logout.html
│ │ └── scope.html
│ ├── device_name.txt
│ ├── emails/
│ │ ├── recovery.html
│ │ ├── recovery.subject
│ │ ├── recovery.txt
│ │ ├── verification.html
│ │ ├── verification.subject
│ │ └── verification.txt
│ ├── form_post.html
│ ├── pages/
│ │ ├── 404.html
│ │ ├── account/
│ │ │ ├── deactivated.html
│ │ │ ├── locked.html
│ │ │ └── logged_out.html
│ │ ├── compat_login_policy_violation.html
│ │ ├── consent.html
│ │ ├── device_consent.html
│ │ ├── device_link.html
│ │ ├── error.html
│ │ ├── index.html
│ │ ├── login.html
│ │ ├── policy_violation.html
│ │ ├── reauth.html
│ │ ├── recovery/
│ │ │ ├── consumed.html
│ │ │ ├── disabled.html
│ │ │ ├── expired.html
│ │ │ ├── finish.html
│ │ │ ├── progress.html
│ │ │ └── start.html
│ │ ├── register/
│ │ │ ├── index.html
│ │ │ ├── password.html
│ │ │ └── steps/
│ │ │ ├── display_name.html
│ │ │ ├── email_in_use.html
│ │ │ ├── registration_token.html
│ │ │ └── verify_email.html
│ │ ├── sso.html
│ │ └── upstream_oauth2/
│ │ ├── do_register.html
│ │ ├── link_mismatch.html
│ │ └── suggest_link.html
│ └── swagger/
│ ├── doc.html
│ └── oauth2-redirect.html
└── translations/
├── cs.json
├── da.json
├── de.json
├── en.json
├── et.json
├── fi.json
├── fr.json
├── hu.json
├── nb-NO.json
├── nl.json
├── pl.json
├── pt-BR.json
├── pt.json
├── ru.json
├── sk.json
├── sv.json
├── uk.json
├── uz.json
└── zh-Hans.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .cargo/config.toml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
[build]
rustflags = ["--cfg", "tokio_unstable"]
# On x86_64, we target the x86-64-v2 psABI, as it is a good compromise between
# modern CPU instructions and compatibility.
[target.x86_64-unknown-linux-gnu]
rustflags = ["--cfg", "tokio_unstable", "-C", "target-cpu=x86-64-v2"]
================================================
FILE: .codecov.yml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
comment: false
flag_management:
default_rules:
carryforward: true
================================================
FILE: .config/nextest.toml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
[profile.default]
retries = 1
================================================
FILE: .dockerignore
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
target/
crates/*/target
crates/*/node_modules
frontend/node_modules
frontend/dist
docs/
.devcontainer/
.github/
.gitignore
Dockerfile
.dockerignore
docker-bake.hcl
================================================
FILE: .editorconfig
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
root = true
[*]
charset=utf-8
end_of_line = lf
[*.{ts,tsx,cts,mts,js,cjs,mjs,css,json,graphql}]
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
================================================
FILE: .github/CODEOWNERS
================================================
* @element-hq/mas-maintainers
================================================
FILE: .github/actions/build-frontend/action.yml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
name: Build the frontend assets
description: Installs Node.js and builds the frontend assets from the frontend directory
runs:
using: composite
steps:
- name: Install Node
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: "24"
- name: Install dependencies
run: npm ci
working-directory: ./frontend
shell: sh
- name: Build the frontend assets
run: npm run build
working-directory: ./frontend
shell: sh
================================================
FILE: .github/actions/build-policies/action.yml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
name: Build the Open Policy Agent policies
description: Installs OPA and builds the policies
runs:
using: composite
steps:
- name: Install Open Policy Agent
uses: open-policy-agent/setup-opa@34a30e8a924d1b03ce2cf7abe97250bbb1f332b5 # v2.2.0
with:
# Keep in sync with the Dockerfile and policies/Makefile
version: 1.13.1
- name: Build the policies
run: make
working-directory: ./policies
shell: sh
================================================
FILE: .github/dependabot.yml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
version: 2
updates:
- package-ecosystem: "cargo"
directory: "/"
labels:
- "A-Dependencies"
- "Z-Deps-Backend"
schedule:
interval: "monthly"
groups:
axum:
patterns:
- "axum"
- "axum-*"
opentelemetry:
patterns:
- "opentelemetry"
- "opentelemetry_sdk"
- "opentelemetry-*"
- "tracing-opentelemetry"
sea-query:
patterns:
- "sea-query"
- "sea-query-*"
sentry:
patterns:
- "sentry"
- "sentry-*"
tracing:
patterns:
- "tracing-*"
exclude-patterns:
- "tracing-opentelemetry"
icu:
patterns:
- "icu"
- "icu_*"
cooldown:
default-days: 14
- package-ecosystem: "github-actions"
directory: "/"
labels:
- "A-Dependencies"
- "Z-Deps-CI"
schedule:
interval: "monthly"
cooldown:
default-days: 14
- package-ecosystem: "npm"
directory: "/frontend/"
labels:
- "A-Dependencies"
- "Z-Deps-Frontend"
schedule:
interval: "monthly"
groups:
storybook:
patterns:
- "storybook"
- "storybook-*"
- "@storybook/*"
fontsource:
patterns:
- "@fontsource/*"
vitest:
patterns:
- "vitest"
- "@vitest/*"
vite:
patterns:
- "vite"
- "@vitejs/*"
- "vite-*"
i18next:
patterns:
- "i18next"
- "i18next-*"
- "react-i18next"
react:
patterns:
- "react"
- "react-*"
exclude-patterns:
- "react-i18next"
jotai:
patterns:
- "jotai"
- "jotai-*"
graphql-codegen:
patterns:
- "@graphql-codegen/*"
tanstack-router:
patterns:
- "@tanstack/react-router"
- "@tanstack/react-router-*"
- "@tanstack/router-*"
tanstack-query:
patterns:
- "@tanstack/react-query"
- "@tanstack/react-query-*"
types:
patterns:
- "@types/*"
browser-logos:
patterns:
- "@browser-logos/*"
cooldown:
default-days: 14
================================================
FILE: .github/release.yml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
changelog:
categories:
- title: Bug Fixes
labels:
- T-Defect
- title: New Features
labels:
- T-Enhancement
exclude:
labels:
- A-Admin-API
- A-Documentation
- title: Changes to the admin API
labels:
- A-Admin-API
- title: Documentation
labels:
- A-Documentation
- title: Translations
labels:
- A-I18n
- title: Internal Changes
labels:
- T-Task
- title: Other Changes
labels:
- "*"
exclude:
labels:
- A-Dependencies
- title: Dependency Updates
labels:
- A-Dependencies
================================================
FILE: .github/scripts/.gitignore
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
node_modules/
package-lock.json
================================================
FILE: .github/scripts/cleanup-pr.cjs
================================================
// Copyright 2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
// @ts-check
/** @param {import('@actions/github-script').AsyncFunctionArguments} AsyncFunctionArguments */
module.exports = async ({ github, context }) => {
const metadataJson = process.env.BUILD_IMAGE_MANIFEST;
if (!metadataJson) throw new Error("BUILD_IMAGE_MANIFEST is not defined");
/** @type {Record<string, {tags: string[]}>} */
const metadata = JSON.parse(metadataJson);
await github.rest.issues.removeLabel({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
name: "Z-Build-Workflow",
});
const tagListMarkdown = metadata.regular.tags
.map((tag) => `- \`${tag}\``)
.join("\n");
// Get the workflow run
const run = await github.rest.actions.getWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.runId,
});
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `A build for this PR at commit <kbd>${context.sha}</kbd> has been created through the <kbd>Z-Build-Workflow</kbd> label by <kbd>${context.actor}</kbd>.
Docker image is available at:
${tagListMarkdown}
Pre-built binaries are available through the [workflow run artifacts](${run.data.html_url}).`,
});
};
================================================
FILE: .github/scripts/commit-and-tag.cjs
================================================
// Copyright 2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
// @ts-check
/** @param {import('@actions/github-script').AsyncFunctionArguments} AsyncFunctionArguments */
module.exports = async ({ github, context }) => {
const fs = require("node:fs/promises");
const { owner, repo } = context.repo;
const version = process.env.VERSION;
const parent = context.sha;
if (!version) throw new Error("VERSION is not defined");
const files = ["Cargo.toml", "Cargo.lock"];
/** @type {{path: string, mode: "100644", type: "blob", sha: string}[]} */
const tree = [];
for (const file of files) {
const content = await fs.readFile(file);
const blob = await github.rest.git.createBlob({
owner,
repo,
content: content.toString("base64"),
encoding: "base64",
});
console.log(`Created blob for ${file}:`, blob.data.url);
tree.push({
path: file,
mode: "100644",
type: "blob",
sha: blob.data.sha,
});
}
const treeObject = await github.rest.git.createTree({
owner,
repo,
tree,
base_tree: parent,
});
console.log("Created tree:", treeObject.data.url);
const commit = await github.rest.git.createCommit({
owner,
repo,
message: version,
parents: [parent],
tree: treeObject.data.sha,
});
console.log("Created commit:", commit.data.url);
const tag = await github.rest.git.createTag({
owner,
repo,
tag: `v${version}`,
message: version,
type: "commit",
object: commit.data.sha,
});
console.log("Created tag:", tag.data.url);
return { commit: commit.data.sha, tag: tag.data.sha };
};
================================================
FILE: .github/scripts/create-release-branch.cjs
================================================
// Copyright 2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
// @ts-check
/** @param {import('@actions/github-script').AsyncFunctionArguments} AsyncFunctionArguments */
module.exports = async ({ github, context }) => {
const { owner, repo } = context.repo;
const branch = process.env.BRANCH;
const sha = process.env.SHA;
if (!sha) throw new Error("SHA is not defined");
await github.rest.git.createRef({
owner,
repo,
ref: `refs/heads/${branch}`,
sha,
});
console.log(`Created branch ${branch} from ${sha}`);
};
================================================
FILE: .github/scripts/create-version-tag.cjs
================================================
// Copyright 2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
// @ts-check
/** @param {import('@actions/github-script').AsyncFunctionArguments} AsyncFunctionArguments */
module.exports = async ({ github, context }) => {
const { owner, repo } = context.repo;
const version = process.env.VERSION;
const tagSha = process.env.TAG_SHA;
if (!version) throw new Error("VERSION is not defined");
if (!tagSha) throw new Error("TAG_SHA is not defined");
const tag = await github.rest.git.createRef({
owner,
repo,
ref: `refs/tags/v${version}`,
sha: tagSha,
});
console.log("Created tag ref:", tag.data.url);
};
================================================
FILE: .github/scripts/merge-back.cjs
================================================
// Copyright 2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
// @ts-check
/** @param {import('@actions/github-script').AsyncFunctionArguments} AsyncFunctionArguments */
module.exports = async ({ github, context }) => {
const { owner, repo } = context.repo;
const sha = process.env.SHA;
const branch = `ref-merge/${sha}`;
if (!sha) throw new Error("SHA is not defined");
await github.rest.git.createRef({
owner,
repo,
ref: `refs/heads/${branch}`,
sha,
});
console.log(`Created branch ${branch} to ${sha}`);
// Create a PR to merge the branch back to main
const pr = await github.rest.pulls.create({
owner,
repo,
head: branch,
base: "main",
title: "Automatic merge back to main",
body: "This pull request was automatically created by the release workflow. It merges the release branch back to main.",
maintainer_can_modify: true,
});
console.log(
`Created pull request #${pr.data.number} to merge the release branch back to main`,
);
console.log(`PR URL: ${pr.data.html_url}`);
// Add the `T-Task` label to the PR
await github.rest.issues.addLabels({
owner,
repo,
issue_number: pr.data.number,
labels: ["T-Task"],
});
// Enable auto-merge on the PR
await github.graphql(
`
mutation AutoMerge($id: ID!) {
enablePullRequestAutoMerge(input: {
pullRequestId: $id,
mergeMethod: MERGE,
}) {
clientMutationId
}
}
`,
{ id: pr.data.node_id },
);
};
================================================
FILE: .github/scripts/package.json
================================================
{
"private": true,
"devDependencies": {
"@actions/github-script": "github:actions/github-script",
"typescript": "^5.7.3"
}
}
================================================
FILE: .github/scripts/update-release-branch.cjs
================================================
// Copyright 2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
// @ts-check
/** @param {import('@actions/github-script').AsyncFunctionArguments} AsyncFunctionArguments */
module.exports = async ({ github, context }) => {
const { owner, repo } = context.repo;
const branch = process.env.BRANCH;
const sha = process.env.SHA;
if (!sha) throw new Error("SHA is not defined");
await github.rest.git.updateRef({
owner,
repo,
ref: `heads/${branch}`,
sha,
});
console.log(`Updated branch ${branch} to ${sha}`);
};
================================================
FILE: .github/scripts/update-unstable-tag.cjs
================================================
// Copyright 2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
// @ts-check
/** @param {import('@actions/github-script').AsyncFunctionArguments} AsyncFunctionArguments */
module.exports = async ({ github, context }) => {
const { owner, repo } = context.repo;
const sha = context.sha;
const tag = await github.rest.git.updateRef({
owner,
repo,
force: true,
ref: "tags/unstable",
sha,
});
console.log("Updated tag ref:", tag.data.url);
};
================================================
FILE: .github/workflows/build.yaml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
name: Build
on:
push:
branches:
- main
- "release/**"
tags:
- "v*"
# Run when there is a label change on the pull request
# This runs only if the 'Z-Build-Workflow' is added to the pull request
pull_request:
types: [labeled]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
BUILDCACHE: ghcr.io/element-hq/matrix-authentication-service/buildcache
# metadata-action defaults to `manifest`, which `docker buildx imagetools
# create --annotation` refuses with "manifest annotations are not supported
# yet". We only want annotations on the manifest list anyway, so narrow it
# to `index`.
DOCKER_METADATA_ANNOTATIONS_LEVELS: index
jobs:
compute-version:
name: Compute version using git describe
if: github.event_name == 'push' || github.event.label.name == 'Z-Build-Workflow'
runs-on: ubuntu-24.04
permissions:
contents: read
outputs:
describe: ${{ steps.git.outputs.describe }}
timestamp: ${{ steps.git.outputs.timestamp }}
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
# Need a full clone so that `git describe` reports the right version
fetch-depth: 0
persist-credentials: false
- name: Compute version and timestamp out of git history
id: git
run: |
echo "describe=$(git describe --tags --match 'v*.*.*' --always)" >> $GITHUB_OUTPUT
echo "timestamp=$(git log -1 --format=%ct)" >> $GITHUB_OUTPUT
build-assets:
name: Build assets
if: github.event_name == 'push' || github.event.label.name == 'Z-Build-Workflow'
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: ./.github/actions/build-frontend
- uses: ./.github/actions/build-policies
- name: Prepare assets artifact
run: |
mkdir -p assets-dist/share
cp policies/policy.wasm assets-dist/share/policy.wasm
cp frontend/dist/manifest.json assets-dist/share/manifest.json
cp -r frontend/dist/ assets-dist/share/assets
cp -r templates/ assets-dist/share/templates
cp -r translations/ assets-dist/share/translations
cp LICENSE assets-dist/LICENSE
chmod -R u=rwX,go=rX assets-dist/
- name: Upload assets
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: assets
path: assets-dist
build-binaries:
name: Build binaries
if: github.event_name == 'push' || github.event.label.name == 'Z-Build-Workflow'
runs-on: ubuntu-24.04
needs:
- compute-version
strategy:
matrix:
include:
- target: x86_64-unknown-linux-gnu
- target: aarch64-unknown-linux-gnu
env:
VERGEN_GIT_DESCRIBE: ${{ needs.compute-version.outputs.describe }}
SOURCE_DATE_EPOCH: ${{ needs.compute-version.outputs.timestamp }}
permissions:
contents: read
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Rust toolchain
# Install the minimal toolchain, which includes rustc, rustdoc, and cargo.
run: |
rustup toolchain install stable --profile minimal
rustup target add ${{ matrix.target }}
- name: Setup sccache
uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9
- name: Install zig
uses: goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406 # v2
with:
version: 0.13.0
- name: Install cargo-zigbuild
uses: taiki-e/install-action@7ea35f098a7369cd23488403f58be9c491a6c55f # v2
with:
tool: cargo-zigbuild
- name: Build the binary
run: |
cargo zigbuild \
--release \
--target ${{ matrix.target }}.2.17 \
--no-default-features \
--features dist \
-p mas-cli
- name: Upload binary artifact
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: binary-${{ matrix.target }}
path: target/${{ matrix.target }}/release/mas-cli
assemble-archives:
name: Assemble release archives
if: github.event_name == 'push' || github.event.label.name == 'Z-Build-Workflow'
runs-on: ubuntu-24.04
needs:
- build-assets
- build-binaries
permissions:
contents: read
steps:
- name: Download assets
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: assets
path: assets-dist
- name: Download binary x86_64
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: binary-x86_64-unknown-linux-gnu
path: binary-x86_64
- name: Download binary aarch64
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: binary-aarch64-unknown-linux-gnu
path: binary-aarch64
- name: Create final archives
run: |
for arch in x86_64 aarch64; do
mkdir -p dist/${arch}/share
cp -r assets-dist/share/* dist/${arch}/share/
cp assets-dist/LICENSE dist/${arch}/LICENSE
cp binary-$arch/mas-cli dist/${arch}/mas-cli
chmod -R u=rwX,go=rX dist/${arch}/
chmod u=rwx,go=rx dist/${arch}/mas-cli
tar -czvf mas-cli-${arch}-linux.tar.gz --owner=0 --group=0 -C dist/${arch}/ .
done
- name: Upload aarch64 archive
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: mas-cli-aarch64-linux
path: mas-cli-aarch64-linux.tar.gz
- name: Upload x86_64 archive
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: mas-cli-x86_64-linux
path: mas-cli-x86_64-linux.tar.gz
compute-image-meta:
name: Compute Docker image metadata
if: github.event_name == 'push' || github.event.label.name == 'Z-Build-Workflow'
runs-on: ubuntu-24.04
permissions:
contents: read
outputs:
regular-tags: ${{ steps.meta.outputs.tags }}
regular-annotations: ${{ steps.meta.outputs.annotations }}
debug-tags: ${{ steps.meta-debug.outputs.tags }}
debug-annotations: ${{ steps.meta-debug.outputs.annotations }}
steps:
- name: Docker meta
id: meta
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
with:
# The oci-push registry login requires Tailscale + Vault, which only
# works for `push` events (PR-labelled runs lack the right OIDC token).
images: |
ghcr.io/element-hq/matrix-authentication-service
${{ github.event_name == 'push' && 'oci-push.vpn.infra.element.io/matrix-authentication-service' || '' }}
bake-target: docker-metadata-action
flavor: |
latest=auto
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
# GitHub's license detection (via Licensee) can only report a single
# SPDX identifier and still emits the legacy `AGPL-3.0` form, which
# `metadata-action` would otherwise propagate as-is. Override it with
# the project's actual dual-license SPDX expression so the image
# advertises both halves of the dual licensing.
labels: |
org.opencontainers.image.licenses=AGPL-3.0-only OR LicenseRef-Element-Commercial
annotations: |
org.opencontainers.image.licenses=AGPL-3.0-only OR LicenseRef-Element-Commercial
- name: Docker meta (debug variant)
id: meta-debug
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
with:
images: |
ghcr.io/element-hq/matrix-authentication-service
${{ github.event_name == 'push' && 'oci-push.vpn.infra.element.io/matrix-authentication-service' || '' }}
bake-target: docker-metadata-action-debug
flavor: |
latest=auto
suffix=-debug,onlatest=true
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
labels: |
org.opencontainers.image.licenses=AGPL-3.0-only OR LicenseRef-Element-Commercial
annotations: |
org.opencontainers.image.licenses=AGPL-3.0-only OR LicenseRef-Element-Commercial
# Stage the labels bake files under predictable names (the metadata-action
# writes them under a random temp dir with the same base name) and ship
# them to `build-image` as an artifact. We deliberately only pass through
# `bake-file-labels` (not `bake-file-annotations`): the per-arch images
# then carry the same config labels as today, while annotations are only
# applied at the index level in `finalize-image` — matching `:latest`'s
# shape and sidestepping `metadata-action`'s empty-value annotations
# which would otherwise trip `docker buildx imagetools create`.
- name: Stage bake files
env:
REGULAR_FILE: ${{ steps.meta.outputs.bake-file-labels }}
DEBUG_FILE: ${{ steps.meta-debug.outputs.bake-file-labels }}
run: |
mkdir -p /tmp/bake
cp "$REGULAR_FILE" /tmp/bake/regular.json
cp "$DEBUG_FILE" /tmp/bake/debug.json
- name: Upload bake files
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: bake-files
path: /tmp/bake/
retention-days: 1
build-image:
name: Build Docker image (${{ matrix.arch }})
if: github.event_name == 'push' || github.event.label.name == 'Z-Build-Workflow'
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
id-token: write
needs:
- compute-version
- compute-image-meta
strategy:
fail-fast: false
matrix:
arch: [amd64, arm64]
env:
VERGEN_GIT_DESCRIBE: ${{ needs.compute-version.outputs.describe }}
SOURCE_DATE_EPOCH: ${{ needs.compute-version.outputs.timestamp }}
# Comma-separated list of registries to push each per-arch image to.
# The oci-push registry is only included on `push` events because the
# Tailscale + Vault login below requires the right OIDC token.
IMAGES: ghcr.io/element-hq/matrix-authentication-service${{ github.event_name == 'push' && ',oci-push.vpn.infra.element.io/matrix-authentication-service' || '' }}
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
with:
buildkitd-config-inline: |
[registry."docker.io"]
mirrors = ["mirror.gcr.io"]
- name: Login to GitHub Container Registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
# The Element OCI Registry is only reachable via Tailscale, and the Vault
# JWT exchange relies on a GitHub OIDC token issued from a `push` event.
# PR-labelled builds (`Z-Build-Workflow`) skip this and push only to ghcr.
- name: Tailscale
if: github.event_name == 'push'
uses: tailscale/github-action@53acf823325fe9ca47f4cdaa951f90b4b0de5bb9 # v4.1.1
with:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
audience: ${{ secrets.TS_AUDIENCE }}
tags: tag:github-actions
- name: Compute vault jwt role name
id: vault-jwt-role
if: github.event_name == 'push'
run: |
echo "role_name=github_service_management_$( echo "${{ github.repository }}" | sed -r 's|[/-]|_|g')" | tee -a "$GITHUB_OUTPUT"
- name: Get team registry token
id: import-secrets
if: github.event_name == 'push'
uses: hashicorp/vault-action@4c06c5ccf5c0761b6029f56cfb1dcf5565918a3b # v3.4.0
with:
url: https://vault.infra.ci.i.element.dev
role: ${{ steps.vault-jwt-role.outputs.role_name }}
path: service-management/github-actions
jwtGithubAudience: https://vault.infra.ci.i.element.dev
method: jwt
secrets: |
services/backend-repositories/secret/data/oci.element.io username | OCI_USERNAME ;
services/backend-repositories/secret/data/oci.element.io password | OCI_PASSWORD ;
- name: Login to Element OCI Registry
if: github.event_name == 'push'
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: oci-push.vpn.infra.element.io
username: ${{ steps.import-secrets.outputs.OCI_USERNAME }}
password: ${{ steps.import-secrets.outputs.OCI_PASSWORD }}
- name: Download bake files
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: bake-files
path: /tmp/bake
- name: Build and push by digest
id: bake
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
env:
# By default, docker bake will add provenance information to the
# metadata output. This makes the output larger and may exceed the
# shell ARG_MAX limit. Disabling through this environment variable
# disables provenance in the metadata while still attaching provenance
# attestations to the image we push.
# https://github.com/docker/bake-action/issues/239#issuecomment-3828170326
BUILDX_METADATA_PROVENANCE: disabled
with:
files: |
./docker-bake.hcl
cwd:///tmp/bake/regular.json
cwd:///tmp/bake/debug.json
set: |
*.platform=linux/${{ matrix.arch }}
*.output=type=image,"name=${{ env.IMAGES }}",push-by-digest=true,name-canonical=true,push=true
*.cache-from=type=registry,ref=${{ env.BUILDCACHE }}:buildcache-${{ matrix.arch }}
*.cache-to=type=registry,ref=${{ env.BUILDCACHE }}:buildcache-${{ matrix.arch }},mode=max
# We use github-script rather than shelling out to jq because the bake
# metadata can exceed the shell ARG_MAX limit when inherited as an env
# var by an exec'd jq.
- name: Export digests
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
STEPS_BAKE_OUTPUTS_METADATA: ${{ steps.bake.outputs.metadata }}
ARCH: ${{ matrix.arch }}
with:
script: |
const fs = require('node:fs');
const path = require('node:path');
const bakeOutput = JSON.parse(process.env.STEPS_BAKE_OUTPUTS_METADATA);
const arch = process.env.ARCH;
fs.mkdirSync('/tmp/digests', { recursive: true });
for (const target of ['regular', 'debug']) {
const digest = bakeOutput[target]?.['containerimage.digest'];
if (!digest) {
throw new Error(`Missing containerimage.digest for target ${target}`);
}
fs.writeFileSync(path.join('/tmp/digests', `${target}-${arch}`), digest);
}
- name: Upload digests
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: digests-${{ matrix.arch }}
path: /tmp/digests/*
retention-days: 1
finalize-image:
name: Create multi-arch manifests
if: github.event_name == 'push' || github.event.label.name == 'Z-Build-Workflow'
runs-on: ubuntu-24.04
needs:
- build-image
- compute-image-meta
outputs:
metadata: ${{ steps.output.outputs.metadata }}
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Download digests
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: digests-*
path: /tmp/digests
# Collect digests from both amd64 and arm64 builds
merge-multiple: true
- name: Setup Cosign
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Login to GitHub Container Registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
# See `build-image` for why the Element OCI Registry login is gated on
# `push` events.
- name: Tailscale
if: github.event_name == 'push'
uses: tailscale/github-action@53acf823325fe9ca47f4cdaa951f90b4b0de5bb9 # v4.1.1
with:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
audience: ${{ secrets.TS_AUDIENCE }}
tags: tag:github-actions
- name: Compute vault jwt role name
id: vault-jwt-role
if: github.event_name == 'push'
run: |
echo "role_name=github_service_management_$( echo "${{ github.repository }}" | sed -r 's|[/-]|_|g')" | tee -a "$GITHUB_OUTPUT"
- name: Get team registry token
id: import-secrets
if: github.event_name == 'push'
uses: hashicorp/vault-action@4c06c5ccf5c0761b6029f56cfb1dcf5565918a3b # v3.4.0
with:
url: https://vault.infra.ci.i.element.dev
role: ${{ steps.vault-jwt-role.outputs.role_name }}
path: service-management/github-actions
jwtGithubAudience: https://vault.infra.ci.i.element.dev
method: jwt
secrets: |
services/backend-repositories/secret/data/oci.element.io username | OCI_USERNAME ;
services/backend-repositories/secret/data/oci.element.io password | OCI_PASSWORD ;
- name: Login to Element OCI Registry
if: github.event_name == 'push'
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: oci-push.vpn.infra.element.io
username: ${{ steps.import-secrets.outputs.OCI_USERNAME }}
password: ${{ steps.import-secrets.outputs.OCI_PASSWORD }}
- name: Create regular manifest
id: regular
env:
TAGS: ${{ needs.compute-image-meta.outputs.regular-tags }}
ANNOTATIONS: ${{ needs.compute-image-meta.outputs.regular-annotations }}
run: |
# Construct the `imagetools create` command line from the tag and annotation inputs.
args=()
# Add a `-t <tag>` argument for each non-empty tag.
while IFS= read -r t; do [[ -n $t ]] && args+=(-t "$t"); done <<< "$TAGS"
# Add a `--annotation <key>=<value>` argument for each non-empty annotation
while IFS= read -r a; do [[ -n $a && $a != *= ]] && args+=(--annotation "$a"); done <<< "$ANNOTATIONS"
docker buildx imagetools create "${args[@]}" \
"ghcr.io/element-hq/matrix-authentication-service@$(cat /tmp/digests/regular-amd64)" \
"ghcr.io/element-hq/matrix-authentication-service@$(cat /tmp/digests/regular-arm64)" \
--metadata-file regular-metadata.json
# `imagetools create` wrote the digest to regular-metadata.json
echo "digest=$(jq -r '.["containerimage.descriptor"].digest' regular-metadata.json)" >> "$GITHUB_OUTPUT"
- name: Create debug manifest
id: debug
env:
TAGS: ${{ needs.compute-image-meta.outputs.debug-tags }}
ANNOTATIONS: ${{ needs.compute-image-meta.outputs.debug-annotations }}
run: |
# See comments in regular manifest creation for argument construction.
args=()
while IFS= read -r t; do [[ -n $t ]] && args+=(-t "$t"); done <<< "$TAGS"
while IFS= read -r a; do [[ -n $a && $a != *= ]] && args+=(--annotation "$a"); done <<< "$ANNOTATIONS"
docker buildx imagetools create "${args[@]}" \
"ghcr.io/element-hq/matrix-authentication-service@$(cat /tmp/digests/debug-amd64)" \
"ghcr.io/element-hq/matrix-authentication-service@$(cat /tmp/digests/debug-arm64)" \
--metadata-file debug-metadata.json
echo "digest=$(jq -r '.["containerimage.descriptor"].digest' debug-metadata.json)" >> "$GITHUB_OUTPUT"
- name: Sign the images with GitHub Actions provided token
# Only sign on tags and on commits on main branch
if: |
github.event_name != 'pull_request'
&& (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
env:
REGULAR_DIGEST: ${{ steps.regular.outputs.digest }}
DEBUG_DIGEST: ${{ steps.debug.outputs.digest }}
run: |-
cosign sign --yes \
"ghcr.io/element-hq/matrix-authentication-service@$REGULAR_DIGEST" \
"ghcr.io/element-hq/matrix-authentication-service@$DEBUG_DIGEST"
cosign sign --yes \
"oci-push.vpn.infra.element.io/matrix-authentication-service@$REGULAR_DIGEST" \
"oci-push.vpn.infra.element.io/matrix-authentication-service@$DEBUG_DIGEST"
- name: Output metadata
id: output
env:
REGULAR_DIGEST: ${{ steps.regular.outputs.digest }}
DEBUG_DIGEST: ${{ steps.debug.outputs.digest }}
REGULAR_TAGS: ${{ needs.compute-image-meta.outputs.regular-tags }}
DEBUG_TAGS: ${{ needs.compute-image-meta.outputs.debug-tags }}
run: |
# Convert the newline-separated tag lists into JSON arrays.
regular_tags=$(jq -Rnc '[inputs | select(length > 0)]' <<< "$REGULAR_TAGS")
debug_tags=$(jq -Rnc '[inputs | select(length > 0)]' <<< "$DEBUG_TAGS")
{
echo 'metadata<<EOF'
jq -nc \
--arg regular_digest "$REGULAR_DIGEST" \
--arg debug_digest "$DEBUG_DIGEST" \
--argjson regular_tags "$regular_tags" \
--argjson debug_tags "$debug_tags" \
'{regular: {digest: $regular_digest, tags: $regular_tags}, debug: {digest: $debug_digest, tags: $debug_tags}}'
echo 'EOF'
} >> "$GITHUB_OUTPUT"
release:
name: Release
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-24.04
needs:
- assemble-archives
- finalize-image
steps:
- name: Download the artifacts from the previous job
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: mas-cli-*
path: artifacts
merge-multiple: true
- name: Prepare a release
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
with:
generate_release_notes: true
body: |
### Docker image
Regular image:
- Digest:
```
ghcr.io/element-hq/matrix-authentication-service@${{ fromJSON(needs.finalize-image.outputs.metadata).regular.digest }}
oci.element.io/matrix-authentication-service@${{ fromJSON(needs.finalize-image.outputs.metadata).regular.digest }}
```
- Tags:
```
${{ join(fromJSON(needs.finalize-image.outputs.metadata).regular.tags, '
') }}
```
Debug variant:
- Digest:
```
ghcr.io/element-hq/matrix-authentication-service@${{ fromJSON(needs.finalize-image.outputs.metadata).debug.digest }}
oci.element.io/matrix-authentication-service@${{ fromJSON(needs.finalize-image.outputs.metadata).debug.digest }}
```
- Tags:
```
${{ join(fromJSON(needs.finalize-image.outputs.metadata).debug.tags, '
') }}
```
files: |
artifacts/mas-cli-aarch64-linux.tar.gz
artifacts/mas-cli-x86_64-linux.tar.gz
draft: true
unstable:
name: Update the unstable release
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-24.04
needs:
- assemble-archives
- finalize-image
permissions:
contents: write
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
sparse-checkout: |
.github/scripts
persist-credentials: false
- name: Download the artifacts from the previous job
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: mas-cli-*
path: artifacts
merge-multiple: true
- name: Update unstable git tag
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const script = require('./.github/scripts/update-unstable-tag.cjs');
await script({ core, github, context });
- name: Update unstable release
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
with:
name: "Unstable build"
tag_name: unstable
body: |
This is an automatically updated unstable release containing the latest builds from the main branch.
**⚠️ Warning: These are development builds and may be unstable.**
Last updated: ${{ github.event.head_commit.timestamp }}
Commit: ${{ github.sha }}
### Docker image
Regular image:
- Digest:
```
ghcr.io/element-hq/matrix-authentication-service@${{ fromJSON(needs.finalize-image.outputs.metadata).regular.digest }}
oci.element.io/matrix-authentication-service@${{ fromJSON(needs.finalize-image.outputs.metadata).regular.digest }}
```
- Tags:
```
${{ join(fromJSON(needs.finalize-image.outputs.metadata).regular.tags, '
') }}
```
Debug variant:
- Digest:
```
ghcr.io/element-hq/matrix-authentication-service@${{ fromJSON(needs.finalize-image.outputs.metadata).debug.digest }}
oci.element.io/matrix-authentication-service@${{ fromJSON(needs.finalize-image.outputs.metadata).debug.digest }}
```
- Tags:
```
${{ join(fromJSON(needs.finalize-image.outputs.metadata).debug.tags, '
') }}
```
files: |
artifacts/mas-cli-aarch64-linux.tar.gz
artifacts/mas-cli-x86_64-linux.tar.gz
prerelease: true
make_latest: false
pr-cleanup:
name: "Remove workflow build PR label and comment on it"
runs-on: ubuntu-24.04
if: github.event_name == 'pull_request' && github.event.label.name == 'Z-Build-Workflow'
needs:
- finalize-image
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
sparse-checkout: |
.github/scripts
persist-credentials: false
- name: Remove label and comment
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
BUILD_IMAGE_MANIFEST: ${{ needs.finalize-image.outputs.metadata }}
with:
script: |
const script = require('./.github/scripts/cleanup-pr.cjs');
await script({ core, github, context });
================================================
FILE: .github/workflows/ci.yaml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
name: CI
on:
push:
branches:
- main
- "release/**"
tags:
- "v*"
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
jobs:
opa-lint:
name: Lint and test OPA policies
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: ./.github/actions/build-policies
- name: Setup Regal
uses: StyraInc/setup-regal@33a142b1189004e0f14bf42b15972c67eecce776 # v1
with:
# Keep in sync with policies/Makefile
version: 0.38.1
- name: Lint policies
working-directory: ./policies
run: make lint
- name: Run OPA tests
working-directory: ./policies
run: make test
frontend-lint:
name: Check frontend style
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: 24
- name: Install Node dependencies
working-directory: ./frontend
run: npm ci
- name: Lint
working-directory: ./frontend
run: npm run lint
frontend-test:
name: Run the frontend test suite
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: 24
- name: Install Node dependencies
working-directory: ./frontend
run: npm ci
- name: Test
working-directory: ./frontend
run: npm test
frontend-knip:
name: Check the frontend for unused dependencies
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: 24
- name: Install Node dependencies
working-directory: ./frontend
run: npm ci
- name: Check for unused dependencies
working-directory: ./frontend
run: npm run knip
rustfmt:
name: Check Rust style
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Rust toolchain
# Install the minimal toolchain, which includes rustc, rustdoc, and cargo.
# Then install rustfmt for `cargo fmt`.
#
# --override sets this as the default rust toolchain version in this directory.
run: |
rustup toolchain install nightly --profile minimal --component rustfmt --override
- name: Check style
run: cargo fmt --all -- --check
cargo-deny:
name: Run `cargo deny` checks
runs-on: ubuntu-24.04
env:
# We need to remove the sccache wrapper because we don't install it in this job
RUSTC_WRAPPER: ""
permissions:
contents: read
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Run `cargo-deny`
uses: EmbarkStudios/cargo-deny-action@175dc7fd4fb85ec8f46948fb98f44db001149081 # v2.0.16
with:
rust-version: stable
check-schema:
name: Check schema
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Rust toolchain
run: |
rustup toolchain install stable
- name: Setup sccache
uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9
- uses: ./.github/actions/build-frontend
- name: Update the schemas
run: sh ./misc/update.sh
- name: Check that the workspace is clean
run: |
if ! [[ -z $(git status -s) ]]; then
echo "::error title=Workspace is not clean::Please run 'sh ./misc/update.sh' and commit the changes"
(
echo '## Diff after running `sh ./misc/update.sh`:'
echo
echo '```diff'
git diff
echo '```'
) >> $GITHUB_STEP_SUMMARY
exit 1
fi
clippy:
name: Run Clippy
needs: [rustfmt, opa-lint]
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Rust toolchain
# Install the minimal toolchain with clippy, pinned to a version kept in
# sync with Dockerfile.
#
# --override sets this as the default rust toolchain version in this directory.
run: rustup toolchain install 1.93.0 --profile minimal --component clippy --override
- uses: ./.github/actions/build-policies
- name: Setup sccache
uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9
- name: Run clippy
run: |
cargo clippy --workspace --tests --bins --lib -- -D warnings
compile-test-artifacts:
name: Compile test artifacts
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Rust toolchain
# Install the minimal toolchain, which includes rustc, rustdoc, and cargo.
run: rustup toolchain install stable --profile minimal
- name: Install nextest
uses: taiki-e/install-action@7ea35f098a7369cd23488403f58be9c491a6c55f # v2
with:
tool: cargo-nextest
- name: Setup sccache
uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9
- name: Build and archive tests
run: cargo nextest archive --workspace --archive-file nextest-archive.tar.zst
env:
SQLX_OFFLINE: "1"
- name: Upload archive to workflow
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: nextest-archive
path: nextest-archive.tar.zst
test:
name: Run test suite with Rust stable
needs: [rustfmt, opa-lint, compile-test-artifacts]
runs-on: ubuntu-24.04
permissions:
contents: read
strategy:
matrix:
partition: [1, 2, 3]
services:
postgres:
image: docker.io/library/postgres:15.3
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- "5432:5432"
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Rust toolchain
# Install the minimal toolchain, which includes rustc, rustdoc, and cargo.
run: rustup toolchain install stable --profile minimal
- name: Install nextest
uses: taiki-e/install-action@7ea35f098a7369cd23488403f58be9c491a6c55f # v2
with:
tool: cargo-nextest
- uses: ./.github/actions/build-frontend
- uses: ./.github/actions/build-policies
- name: Download archive
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: nextest-archive
- name: Test
env:
DATABASE_URL: postgresql://postgres:postgres@localhost/postgres
run: |
~/.cargo/bin/cargo-nextest nextest run \
--archive-file nextest-archive.tar.zst \
--partition count:${{ matrix.partition }}/3
tests-done:
name: Tests done
if: ${{ always() }}
needs:
- opa-lint
- frontend-lint
- frontend-test
- frontend-knip
- rustfmt
- cargo-deny
- clippy
- check-schema
- test
runs-on: ubuntu-24.04
steps:
- uses: matrix-org/done-action@3409aa904e8a2aaf2220f09bc954d3d0b0a2ee67 # v3
with:
needs: ${{ toJSON(needs) }}
================================================
FILE: .github/workflows/coverage.yaml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
name: Coverage
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
env:
CARGO_TERM_COLOR: always
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
jobs:
opa:
name: Run OPA test suite with coverage
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: ./.github/actions/build-policies
- name: Run OPA tests with coverage
working-directory: ./policies
run: make coverage
- name: Upload to codecov.io
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: policies/coverage.json
flags: policies
frontend:
name: Run frontend test suite with coverage
runs-on: ubuntu-24.04
permissions:
id-token: write
contents: read
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: ./.github/actions/build-frontend
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: Test
working-directory: ./frontend
run: npm run coverage
- name: Upload to codecov.io
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: frontend/coverage/
flags: frontend
rust:
name: Run Rust test suite with coverage
runs-on: ubuntu-24.04
permissions:
contents: read
env:
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
services:
postgres:
image: docker.io/library/postgres:15.3
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- "5432:5432"
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Rust toolchain
# Install the minimal toolchain, which includes rustc, rustdoc, and cargo.
#
# llvm-tools-preview installs some llvm tools, which are needed for `grcov` below.
# See https://github.com/rust-lang/rust/issues/85658 for stability tracking issue.
run: rustup toolchain install stable --profile minimal --component llvm-tools-preview
- name: Setup sccache
uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9
- name: Install grcov
uses: taiki-e/install-action@7ea35f098a7369cd23488403f58be9c491a6c55f # v2
with:
tool: grcov
- uses: ./.github/actions/build-frontend
- uses: ./.github/actions/build-policies
- name: Run test suite with profiling enabled
run: |
cargo test --no-fail-fast --workspace
env:
RUSTFLAGS: "-Cinstrument-coverage --cfg tokio_unstable"
LLVM_PROFILE_FILE: "cargo-test-%p-%m.profraw"
DATABASE_URL: postgresql://postgres:postgres@localhost/postgres
SQLX_OFFLINE: "1"
- name: Build grcov report
run: |
mkdir -p target/coverage
grcov . --binary-path ./target/debug/deps/ -s . -t lcov --branch --ignore-not-existing --ignore '../*' --ignore "/*" -o target/coverage/tests.lcov
- name: Upload to codecov.io
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: target/coverage/*.lcov
flags: unit
================================================
FILE: .github/workflows/docs.yaml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
name: Build and deploy the documentation
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
jobs:
build:
name: Build the documentation
runs-on: ubuntu-24.04
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Rust toolchain
# Install the minimal toolchain, which includes rustc, rustdoc, and cargo
run: rustup toolchain install stable --profile minimal
- name: Setup sccache
uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9
- name: Install mdbook
uses: taiki-e/install-action@7ea35f098a7369cd23488403f58be9c491a6c55f # v2
with:
tool: mdbook
- name: Install Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: 24
- name: Build the documentation
run: sh misc/build-docs.sh
- name: Fix permissions
run: |
chmod -c -R +rX "target/book/" | while read line; do
echo "::warning title=Invalid file permissions automatically fixed::$line"
done
- name: Upload GitHub Pages artifacts
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
with:
path: target/book/
deploy:
name: Deploy the documentation on GitHub Pages
runs-on: ubuntu-24.04
needs: build
if: github.ref == 'refs/heads/main'
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
================================================
FILE: .github/workflows/merge-back.yaml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
name: Merge back a reference to main
on:
workflow_call:
inputs:
sha:
required: true
type: string
secrets:
BOT_GITHUB_TOKEN:
required: true
jobs:
merge-back:
name: Merge back the reference to main
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
sparse-checkout: |
.github/scripts
persist-credentials: false
- name: Push branch and open a PR
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
SHA: ${{ inputs.sha }}
with:
github-token: ${{ secrets.BOT_GITHUB_TOKEN }}
script: |
const script = require('./.github/scripts/merge-back.cjs');
await script({ core, github, context });
================================================
FILE: .github/workflows/release-branch.yaml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
name: Create a new release branch
on:
workflow_dispatch:
inputs:
kind:
description: Kind of release (major = v1.2.3 -> v2.0.0-rc.0, minor = v1.2.3 -> v1.3.0-rc.0)
required: true
type: choice
default: minor
options:
- major
- minor
jobs:
compute-version:
name: Compute the next ${{ inputs.kind }} RC version
runs-on: ubuntu-24.04
permissions:
contents: read
outputs:
full: ${{ steps.next.outputs.full }}
short: ${{ steps.next.outputs.short }}
steps:
- name: Fail the workflow if this is not the main branch
if: ${{ github.ref_name != 'main' }}
run: exit 1
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Rust toolchain
# Only the minimal profile is required here to run `cargo metadata`
run: rustup toolchain install stable --profile minimal
- name: Compute the new minor RC
id: next
env:
BUMP: pre${{ inputs.kind }}
run: |
CURRENT_VERSION="$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "mas-cli") | .version')"
NEXT_VERSION="$(npx --yes semver@7.5.4 -i "$BUMP" --preid rc "${CURRENT_VERSION}")"
# compute the short minor version, e.g. 0.1.0-rc.1 -> 0.1
SHORT_VERSION="$(echo "${NEXT_VERSION}" | cut -d. -f1-2)"
echo "full=${NEXT_VERSION}" >> "$GITHUB_OUTPUT"
echo "short=${SHORT_VERSION}" >> "$GITHUB_OUTPUT"
localazy:
name: Create a new branch in Localazy
runs-on: ubuntu-24.04
needs: [compute-version]
permissions:
contents: read
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: 24
- name: Install Localazy CLI
run: npm install -g @localazy/cli
- name: Create a new branch in Localazy
run: localazy branch -w "$LOCALAZY_WRITE_KEY" create main "$BRANCH"
env:
LOCALAZY_WRITE_KEY: ${{ secrets.LOCALAZY_WRITE_KEY }}
# Localazy doesn't like slashes in branch names, so we just use the short version
# For example, a 0.13.0 release will create a localazy branch named "v0.13" and a git branch named "release/v0.13"
BRANCH: v${{ needs.compute-version.outputs.short }}
tag:
uses: ./.github/workflows/tag.yaml
needs: [compute-version]
with:
version: ${{ needs.compute-version.outputs.full }}
secrets:
BOT_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
merge-back:
uses: ./.github/workflows/merge-back.yaml
needs: [tag]
with:
sha: ${{ needs.tag.outputs.sha }}
secrets:
BOT_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
branch:
name: Create a new release branch
runs-on: ubuntu-24.04
permissions:
contents: write
pull-requests: write
needs: [tag, compute-version, localazy]
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
sparse-checkout: |
.github/scripts
persist-credentials: false
- name: Create a new release branch
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
BRANCH: release/v${{ needs.compute-version.outputs.short }}
SHA: ${{ needs.tag.outputs.sha }}
with:
github-token: ${{ secrets.BOT_GITHUB_TOKEN }}
script: |
const script = require('./.github/scripts/create-release-branch.cjs');
await script({ core, github, context });
================================================
FILE: .github/workflows/release-bump.yaml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
name: Bump the version on a release branch
on:
workflow_dispatch:
inputs:
rc:
description: "Is it a release candidate?"
type: boolean
default: false
merge-back:
description: "Should we merge back the release branch to main?"
type: boolean
default: true
jobs:
compute-version:
name: Compute the next version
runs-on: ubuntu-24.04
permissions:
contents: read
outputs:
version: ${{ steps.next.outputs.version }}
steps:
- name: Fail the workflow if not on a release branch
if: ${{ !startsWith(github.ref_name, 'release/v') }}
run: exit 1
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Rust toolchain
# Only the minimal profile is required here to run `cargo`
run: rustup toolchain install stable --profile minimal
- name: Extract the current version
id: current
run: echo "version=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "mas-cli") | .version')" >> "$GITHUB_OUTPUT"
- name: Compute the new minor RC
id: next
env:
BUMP: ${{ inputs.rc && 'prerelease' || 'patch' }}
VERSION: ${{ steps.current.outputs.version }}
run: echo "version=$(npx --yes semver@7.5.4 -i "$BUMP" --preid rc "$VERSION")" >> "$GITHUB_OUTPUT"
tag:
uses: ./.github/workflows/tag.yaml
needs: [compute-version]
with:
version: ${{ needs.compute-version.outputs.version }}
secrets:
BOT_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
merge-back:
uses: ./.github/workflows/merge-back.yaml
needs: [tag]
if: inputs.merge-back
with:
sha: ${{ needs.tag.outputs.sha }}
secrets:
BOT_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
update-branch:
name: Update the release branch
runs-on: ubuntu-24.04
permissions:
pull-requests: write
needs: [tag, compute-version]
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
sparse-checkout: |
.github/scripts
persist-credentials: false
- name: Update the release branch
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
BRANCH: "${{ github.ref_name }}"
SHA: ${{ needs.tag.outputs.sha }}
with:
github-token: ${{ secrets.BOT_GITHUB_TOKEN }}
script: |
const script = require('./.github/scripts/update-release-branch.cjs');
await script({ core, github, context });
================================================
FILE: .github/workflows/tag.yaml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
name: Tag a new version
on:
workflow_call:
inputs:
version:
required: true
type: string
outputs:
sha:
description: "The SHA of the commit made which bumps the version"
value: ${{ jobs.tag.outputs.sha }}
secrets:
BOT_GITHUB_TOKEN:
required: true
jobs:
tag:
name: Tag a new version
runs-on: ubuntu-24.04
permissions:
contents: write
outputs:
sha: ${{ fromJSON(steps.commit.outputs.result).commit }}
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Rust toolchain
# Only the minimal profile is required here to run `cargo metadata`
run: rustup toolchain install stable --profile minimal
- name: Set the crates version
env:
VERSION: ${{ inputs.version }}
run: |
sed -i "s/^package.version = .*/package.version = \"$VERSION\"/" Cargo.toml
sed -i "/path = \".\/crates\//s/version = \".*\"/version = \"=$VERSION\"/" Cargo.toml
- name: Run `cargo metadata` to make sure the lockfile is up to date
run: cargo metadata --format-version 1
- name: Commit and tag using the GitHub API
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
id: commit
env:
VERSION: ${{ inputs.version }}
with:
# Commit & tag with the actions token, so that they get signed
# This returns the commit sha and the tag object sha
script: |
const script = require('./.github/scripts/commit-and-tag.cjs');
return await script({ core, github, context });
- name: Update the refs
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
VERSION: ${{ inputs.version }}
TAG_SHA: ${{ fromJSON(steps.commit.outputs.result).tag }}
COMMIT_SHA: ${{ fromJSON(steps.commit.outputs.result).commit }}
with:
# Update the refs with the bot token, so that workflows are triggered
github-token: ${{ secrets.BOT_GITHUB_TOKEN }}
script: |
const script = require('./.github/scripts/create-version-tag.cjs');
await script({ core, github, context });
================================================
FILE: .github/workflows/translations-download.yaml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
name: Download translation files from Localazy
on:
workflow_dispatch:
jobs:
download:
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- name: Fail the workflow if not on the main branch or a release branch
if: ${{ !(startsWith(github.ref_name, 'release/v') || github.ref_name == 'main') }}
run: exit 1
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: 24
- name: Install Localazy CLI
run: npm install -g @localazy/cli
- name: Compute the Localazy branch name
id: branch
# This will strip the "release/" prefix if present, keeping 'main' as-is
run: echo "name=${GITHUB_REF_NAME#release/}" >> "$GITHUB_OUTPUT"
- name: Download translations from Localazy
run: localazy download -w "$LOCALAZY_WRITE_KEY" -b "$BRANCH"
env:
LOCALAZY_WRITE_KEY: ${{ secrets.LOCALAZY_WRITE_KEY }}
BRANCH: ${{ steps.branch.outputs.name }}
- name: Create Pull Request
id: cpr
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
with:
sign-commits: true
token: ${{ secrets.BOT_GITHUB_TOKEN }}
branch-token: ${{ secrets.GITHUB_TOKEN }}
branch: actions/localazy-download/${{ steps.branch.outputs.name }}
delete-branch: true
title: Translations updates for ${{ steps.branch.outputs.name }}
labels: |
T-Task
A-I18n
commit-message: Translations updates
- name: Enable automerge
run: gh pr merge --merge --auto "$PR_NUMBER"
if: steps.cpr.outputs.pull-request-operation == 'created'
env:
GH_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
PR_NUMBER: ${{ steps.cpr.outputs.pull-request-number }}
================================================
FILE: .github/workflows/translations-upload.yaml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
name: Upload translation files to Localazy
on:
push:
branches:
- main
- release/v**
jobs:
upload:
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: 24
- name: Install Localazy CLI
run: npm install -g @localazy/cli
- name: Compute the Localazy branch name
id: branch
run: |
# This will strip the "release/" prefix if present, keeping 'main' as-is
echo "name=${GITHUB_REF_NAME#release/}" >> "$GITHUB_OUTPUT"
- name: Upload translations to Localazy
run: localazy upload -w "$LOCALAZY_WRITE_KEY" -b "$BRANCH"
env:
LOCALAZY_WRITE_KEY: ${{ secrets.LOCALAZY_WRITE_KEY }}
BRANCH: ${{ steps.branch.outputs.name }}
================================================
FILE: .gitignore
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
# Rust
target
# Editors
.idea
.nova
# OS garbage
.DS_Store
================================================
FILE: .rustfmt.toml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
max_width = 100
comment_width = 80
wrap_comments = true
imports_granularity = "Crate"
use_small_heuristics = "Default"
group_imports = "StdExternalCrate"
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to MAS
Thank you for taking the time to contribute to Matrix!
Please see the [contributors' guide](https://element-hq.github.io/matrix-authentication-service/development/contributing.html) in our rendered documentation.
================================================
FILE: Cargo.toml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
[workspace]
default-members = ["crates/cli"]
members = ["crates/*"]
resolver = "2"
# Updated in the CI with a `sed` command
package.version = "1.17.0"
package.license = "AGPL-3.0-only OR LicenseRef-Element-Commercial"
package.authors = ["Element Backend Team"]
package.edition = "2024"
package.homepage = "https://element-hq.github.io/matrix-authentication-service/"
package.repository = "https://github.com/element-hq/matrix-authentication-service/"
package.publish = false
[workspace.lints.rust]
unsafe_code = "deny"
[workspace.lints.clippy]
# We use groups as good defaults, but with a lower priority so that we can override them
all = { level = "deny", priority = -1 }
pedantic = { level = "warn", priority = -1 }
# Allowed because it's nice to have temporary semantic names, see
# https://github.com/rust-lang/rust-clippy/issues/12512#issuecomment-3316736180
let_and_return = "allow"
str_to_string = "deny"
too_many_lines = "allow"
# Sometimes variables are long and annoying to inline in the format string.
# And this lint also complains about aliases, https://github.com/rust-lang/rust-clippy/issues/10247
uninlined_format_args = "allow"
[workspace.lints.rustdoc]
broken_intra_doc_links = "deny"
[workspace.dependencies]
# Workspace crates
mas-axum-utils = { path = "./crates/axum-utils/", version = "=1.17.0" }
mas-cli = { path = "./crates/cli/", version = "=1.17.0" }
mas-config = { path = "./crates/config/", version = "=1.17.0" }
mas-context = { path = "./crates/context/", version = "=1.17.0" }
mas-data-model = { path = "./crates/data-model/", version = "=1.17.0" }
mas-email = { path = "./crates/email/", version = "=1.17.0" }
mas-graphql = { path = "./crates/graphql/", version = "=1.17.0" }
mas-handlers = { path = "./crates/handlers/", version = "=1.17.0" }
mas-http = { path = "./crates/http/", version = "=1.17.0" }
mas-i18n = { path = "./crates/i18n/", version = "=1.17.0" }
mas-i18n-scan = { path = "./crates/i18n-scan/", version = "=1.17.0" }
mas-iana = { path = "./crates/iana/", version = "=1.17.0" }
mas-iana-codegen = { path = "./crates/iana-codegen/", version = "=1.17.0" }
mas-jose = { path = "./crates/jose/", version = "=1.17.0" }
mas-keystore = { path = "./crates/keystore/", version = "=1.17.0" }
mas-listener = { path = "./crates/listener/", version = "=1.17.0" }
mas-matrix = { path = "./crates/matrix/", version = "=1.17.0" }
mas-matrix-synapse = { path = "./crates/matrix-synapse/", version = "=1.17.0" }
mas-oidc-client = { path = "./crates/oidc-client/", version = "=1.17.0" }
mas-policy = { path = "./crates/policy/", version = "=1.17.0" }
mas-router = { path = "./crates/router/", version = "=1.17.0" }
mas-spa = { path = "./crates/spa/", version = "=1.17.0" }
mas-storage = { path = "./crates/storage/", version = "=1.17.0" }
mas-storage-pg = { path = "./crates/storage-pg/", version = "=1.17.0" }
mas-tasks = { path = "./crates/tasks/", version = "=1.17.0" }
mas-templates = { path = "./crates/templates/", version = "=1.17.0" }
mas-tower = { path = "./crates/tower/", version = "=1.17.0" }
oauth2-types = { path = "./crates/oauth2-types/", version = "=1.17.0" }
syn2mas = { path = "./crates/syn2mas", version = "=1.17.0" }
# OpenAPI schema generation and validation
[workspace.dependencies.aide]
version = "0.15.1"
features = ["axum", "axum-extra", "axum-extra-query", "axum-json", "macros"]
# An `Arc` that can be atomically updated
[workspace.dependencies.arc-swap]
version = "1.8.1"
# GraphQL server
[workspace.dependencies.async-graphql]
version = "7.0.17"
default-features = false
features = ["chrono", "url", "tracing", "playground"]
[workspace.dependencies.async-stream]
version = "0.3.6"
# Utility to write and implement async traits
[workspace.dependencies.async-trait]
version = "0.1.89"
# High-level error handling
[workspace.dependencies.anyhow]
version = "1.0.102"
# Assert that a value matches a pattern
[workspace.dependencies.assert_matches]
version = "1.5.0"
# HTTP router
[workspace.dependencies.axum]
version = "0.8.6"
# Extra utilities for Axum
[workspace.dependencies.axum-extra]
version = "0.10.3"
features = ["cookie-private", "cookie-key-expansion", "typed-header", "query"]
# Axum macros
[workspace.dependencies.axum-macros]
version = "0.5.0"
# AEAD (Authenticated Encryption with Associated Data)
[workspace.dependencies.aead]
version = "0.5.2"
features = ["std"]
# Argon2 password hashing
[workspace.dependencies.argon2]
version = "0.5.3"
features = ["password-hash", "std"]
# Constant-time base64
[workspace.dependencies.base64ct]
version = "1.8.0"
features = ["std"]
# Bcrypt password hashing
[workspace.dependencies.bcrypt]
version = "0.18.0"
default-features = true
# Packed bitfields
[workspace.dependencies.bitflags]
version = "2.10.0"
# Bytes
[workspace.dependencies.bytes]
version = "1.10.1"
# UTF-8 paths
[workspace.dependencies.camino]
version = "1.2.1"
features = ["serde1"]
# ChaCha20Poly1305 AEAD
[workspace.dependencies.chacha20poly1305]
version = "0.10.1"
features = ["std"]
# Memory optimisation for short strings
[workspace.dependencies.compact_str]
version = "0.9.0"
# Terminal formatting
[workspace.dependencies.console]
version = "0.15.11"
# Cookie store
[workspace.dependencies.cookie_store]
version = "0.22.0"
default-features = false
features = ["serde_json"]
# Time utilities
[workspace.dependencies.chrono]
version = "0.4.42"
default-features = false
features = ["serde", "clock"]
# CLI argument parsing
[workspace.dependencies.clap]
version = "4.5.50"
features = ["derive"]
# Object Identifiers (OIDs) as constants
[workspace.dependencies.const-oid]
version = "0.9.6"
features = ["std"]
# Utility for converting between different cases
[workspace.dependencies.convert_case]
version = "0.9.0"
# CRC calculation
[workspace.dependencies.crc]
version = "3.3.0"
# Cron expressions
[workspace.dependencies.cron]
version = "0.15.0"
# CSV parsing and writing
[workspace.dependencies.csv]
version = "1.4.0"
# DER encoding
[workspace.dependencies.der]
version = "0.7.10"
features = ["std"]
# Interactive CLI dialogs
[workspace.dependencies.dialoguer]
version = "0.11.0"
default-features = false
features = ["fuzzy-select", "password"]
# Cryptographic digest algorithms
[workspace.dependencies.digest]
version = "0.10.7"
# Load environment variables from .env files
[workspace.dependencies.dotenvy]
version = "0.15.7"
# ECDSA algorithms
[workspace.dependencies.ecdsa]
version = "0.16.9"
features = ["signing", "verifying"]
# Elliptic curve cryptography
[workspace.dependencies.elliptic-curve]
version = "0.13.8"
features = ["std", "pem", "sec1"]
# Configuration loading
[workspace.dependencies.figment]
version = "0.10.19"
features = ["env", "yaml", "test"]
# URL form encoding
[workspace.dependencies.form_urlencoded]
version = "1.2.2"
# Utilities for dealing with futures
[workspace.dependencies.futures-util]
version = "0.3.31"
# Fixed-size arrays with trait implementations
[workspace.dependencies.generic-array]
version = "0.14.7"
# Rate-limiting
[workspace.dependencies.governor]
version = "0.10.1"
default-features = false
features = ["std", "dashmap", "quanta"]
# HMAC calculation
[workspace.dependencies.hmac]
version = "0.12.1"
# HTTP headers
[workspace.dependencies.headers]
version = "0.4.1"
# Hex encoding and decoding
[workspace.dependencies.hex]
version = "0.4.3"
# HTTP request/response
[workspace.dependencies.http]
version = "1.3.1"
# HTTP body trait
[workspace.dependencies.http-body]
version = "1.0.1"
# http-body utilities
[workspace.dependencies.http-body-util]
version = "0.1.3"
# HTTP client and server
[workspace.dependencies.hyper]
version = "1.7.0"
features = ["client", "server", "http1", "http2"]
# Additional Hyper utilties
[workspace.dependencies.hyper-util]
version = "0.1.18"
features = [
"client",
"server",
"server-auto",
"service",
"http1",
"http2",
"tokio",
]
# Hyper Rustls support
[workspace.dependencies.hyper-rustls]
version = "0.27.7"
features = ["http1", "http2"]
default-features = false
# ICU libraries for internationalization
[workspace.dependencies.icu_calendar]
version = "1.5.2"
features = ["compiled_data", "std"]
[workspace.dependencies.icu_datetime]
version = "1.5.1"
features = ["compiled_data", "std"]
[workspace.dependencies.icu_experimental]
version = "0.1.0"
features = ["compiled_data", "std"]
[workspace.dependencies.icu_locid]
version = "1.5.0"
features = ["std"]
[workspace.dependencies.icu_locid_transform]
version = "1.5.0"
features = ["compiled_data", "std"]
[workspace.dependencies.icu_normalizer]
version = "1.5.0"
[workspace.dependencies.icu_plurals]
version = "1.5.0"
features = ["compiled_data", "std"]
[workspace.dependencies.icu_provider]
version = "1.5.0"
features = ["std", "sync"]
[workspace.dependencies.icu_provider_adapters]
version = "1.5.0"
features = ["std"]
# HashMap which preserves insertion order
[workspace.dependencies.indexmap]
version = "2.11.4"
features = ["serde"]
# Indented string literals
[workspace.dependencies.indoc]
version = "2.0.6"
# Snapshot testing
[workspace.dependencies.insta]
version = "1.46.3"
features = ["yaml", "json"]
# IP network address types
[workspace.dependencies.ipnetwork]
version = "0.20.0"
features = ["serde"]
# Iterator utilities
[workspace.dependencies.itertools]
version = "0.14.0"
# K256 elliptic curve
[workspace.dependencies.k256]
version = "0.13.4"
features = ["std"]
# RFC 5646 language tags
[workspace.dependencies.language-tags]
version = "0.3.2"
features = ["serde"]
# Email sending
[workspace.dependencies.lettre]
version = "0.11.22"
default-features = false
features = [
"tokio1-rustls",
"rustls-platform-verifier",
"aws-lc-rs",
"hostname",
"builder",
"tracing",
"pool",
"smtp-transport",
"sendmail-transport",
]
# Listening on passed FDs
[workspace.dependencies.listenfd]
version = "1.0.2"
# MIME type support
[workspace.dependencies.mime]
version = "0.3.17"
# Templates
[workspace.dependencies.minijinja]
version = "2.15.1"
features = ["urlencode", "loader", "json", "speedups", "unstable_machinery"]
# Additional filters for minijinja
[workspace.dependencies.minijinja-contrib]
version = "2.12.0"
features = ["pycompat"]
# Utilities to deal with non-zero values
[workspace.dependencies.nonzero_ext]
version = "0.3.0"
# Open Policy Agent support through WASM
[workspace.dependencies.opa-wasm]
version = "0.2.0"
# OpenTelemetry
[workspace.dependencies.opentelemetry]
version = "0.31.0"
features = ["trace", "metrics"]
[workspace.dependencies.opentelemetry-http]
version = "0.31.0"
features = ["reqwest"]
[workspace.dependencies.opentelemetry-instrumentation-process]
version = "0.1.2"
[workspace.dependencies.opentelemetry-instrumentation-tokio]
version = "0.1.2"
[workspace.dependencies.opentelemetry-jaeger-propagator]
version = "0.31.0"
[workspace.dependencies.opentelemetry-otlp]
version = "0.31.1"
default-features = false
features = ["trace", "metrics", "http-proto", "tls-provider-agnostic"]
[workspace.dependencies.opentelemetry-prometheus-text-exporter]
version = "0.2.1"
[workspace.dependencies.opentelemetry-resource-detectors]
version = "0.10.0"
[workspace.dependencies.opentelemetry-semantic-conventions]
version = "0.31.0"
features = ["semconv_experimental"]
[workspace.dependencies.opentelemetry-stdout]
version = "0.31.0"
features = ["trace", "metrics"]
[workspace.dependencies.opentelemetry_sdk]
version = "0.31.0"
features = [
"experimental_trace_batch_span_processor_with_async_runtime",
"experimental_metrics_periodicreader_with_async_runtime",
"rt-tokio",
]
[workspace.dependencies.tracing-opentelemetry]
version = "0.32.1"
default-features = false
# P256 elliptic curve
[workspace.dependencies.p256]
version = "0.13.2"
features = ["std"]
# P384 elliptic curve
[workspace.dependencies.p384]
version = "0.13.1"
features = ["std"]
# Text padding utilities
[workspace.dependencies.pad]
version = "0.1.6"
# PBKDF2 password hashing
[workspace.dependencies.pbkdf2]
version = "0.12.2"
features = ["password-hash", "std", "simple", "parallel"]
# PEM encoding/decoding
[workspace.dependencies.pem-rfc7468]
version = "0.7.0"
features = ["std"]
# Parser generator
[workspace.dependencies.pest]
version = "2.8.3"
# Pest derive macros
[workspace.dependencies.pest_derive]
version = "2.8.3"
# Pin projection
[workspace.dependencies.pin-project-lite]
version = "0.2.16"
# PKCS#1 encoding
[workspace.dependencies.pkcs1]
version = "0.7.5"
features = ["std"]
# PKCS#8 encoding
[workspace.dependencies.pkcs8]
version = "0.10.2"
features = ["std", "pkcs5", "encryption"]
# Public Suffix List
[workspace.dependencies.psl]
version = "2.1.162"
# High-precision clock
[workspace.dependencies.quanta]
version = "0.12.6"
# Random values
[workspace.dependencies.rand]
version = "0.8.5"
[workspace.dependencies.rand_chacha]
version = "0.3.1"
[workspace.dependencies.rand_core]
version = "0.6.4"
# Regular expressions
[workspace.dependencies.regex]
version = "1.12.2"
# High-level HTTP client
[workspace.dependencies.reqwest]
version = "0.12.24"
default-features = false
features = [
"http2",
"rustls-tls-manual-roots-no-provider",
"charset",
"json",
"socks",
"system-proxy",
]
# RSA cryptography
[workspace.dependencies.rsa]
version = "0.9.10"
features = ["std", "pem"]
# Fast hash algorithm for HashMap
[workspace.dependencies.rustc-hash]
version = "2.1.1"
# Matrix-related types
[workspace.dependencies.ruma-common]
version = "0.16.0"
# TLS stack
[workspace.dependencies.rustls]
version = "0.23.35"
# PKI types for rustls
[workspace.dependencies.rustls-pki-types]
version = "1.13.0"
# Use platform-specific verifier for TLS
[workspace.dependencies.rustls-platform-verifier]
version = "0.7.0"
# systemd service status notification
[workspace.dependencies.sd-notify]
version = "0.4.5"
# JSON Schema generation
[workspace.dependencies.schemars]
version = "0.9.0"
features = ["url2", "chrono04", "preserve_order"]
# SEC1 encoding format
[workspace.dependencies.sec1]
version = "0.7.3"
features = ["std"]
# Query builder
[workspace.dependencies.sea-query]
version = "0.32.7"
features = ["derive", "attr", "with-uuid", "with-chrono", "postgres-array"]
# Query builder
[workspace.dependencies.sea-query-binder]
version = "0.7.0"
features = [
"sqlx",
"sqlx-postgres",
"with-uuid",
"with-chrono",
"postgres-array",
]
# Sentry error tracking
[workspace.dependencies.sentry]
version = "0.46.2"
default-features = false
features = ["backtrace", "contexts", "panic", "tower", "reqwest"]
# Sentry tower layer
[workspace.dependencies.sentry-tower]
version = "0.46.0"
features = ["http", "axum-matched-path"]
# Sentry tracing integration
[workspace.dependencies.sentry-tracing]
version = "0.46.0"
# Serialization and deserialization
[workspace.dependencies.serde]
version = "1.0.228"
features = ["derive"] # Most of the time, if we need serde, we need derive
# JSON serialization and deserialization
[workspace.dependencies.serde_json]
version = "1.0.145"
features = ["preserve_order"]
# URL encoded form serialization
[workspace.dependencies.serde_urlencoded]
version = "0.7.1"
# Custom serialization helpers
[workspace.dependencies.serde_with]
version = "3.14.0"
features = ["hex", "chrono"]
# YAML serialization
[workspace.dependencies.serde_yaml]
version = "0.9.34"
# SHA-2 cryptographic hash algorithm
[workspace.dependencies.sha2]
version = "0.10.9"
features = ["oid"]
# Digital signature traits
[workspace.dependencies.signature]
version = "2.2.0"
# Low-level socket manipulation
[workspace.dependencies.socket2]
version = "0.6.2"
# Subject Public Key Info
[workspace.dependencies.spki]
version = "0.7.3"
features = ["std"]
# SQL database support
[workspace.dependencies.sqlx]
version = "0.8.6"
features = [
"runtime-tokio",
"tls-rustls-aws-lc-rs",
"postgres",
"migrate",
"chrono",
"json",
"uuid",
"ipnetwork",
]
# Custom error types
[workspace.dependencies.thiserror]
version = "2.0.17"
[workspace.dependencies.thiserror-ext]
version = "0.3.0"
# Async runtime
[workspace.dependencies.tokio]
version = "1.48.0"
features = ["full"]
[workspace.dependencies.tokio-stream]
version = "0.1.17"
# Tokio rustls integration
[workspace.dependencies.tokio-rustls]
version = "0.26.4"
# Tokio test utilities
[workspace.dependencies.tokio-test]
version = "0.4.4"
# Useful async utilities
[workspace.dependencies.tokio-util]
version = "0.7.16"
features = ["rt"]
# Tower services
[workspace.dependencies.tower]
version = "0.5.2"
features = ["util"]
# Tower service trait
[workspace.dependencies.tower-service]
version = "0.3.3"
# Tower layer trait
[workspace.dependencies.tower-layer]
version = "0.3.3"
# Tower HTTP layers
[workspace.dependencies.tower-http]
version = "0.6.6"
features = ["cors", "fs", "add-extension", "set-header"]
# Logging and tracing
[workspace.dependencies.tracing]
version = "0.1.41"
[workspace.dependencies.tracing-subscriber]
version = "0.3.22"
features = ["env-filter"]
[workspace.dependencies.tracing-appender]
version = "0.2.4"
# URL manipulation
[workspace.dependencies.url]
version = "2.5.7"
features = ["serde"]
# URL encoding
[workspace.dependencies.urlencoding]
version = "2.1.3"
# ULID support
[workspace.dependencies.ulid]
version = "=1.1.4" # Pinned to the latest version which used rand 0.8
features = ["serde", "uuid"]
# UUID support
[workspace.dependencies.uuid]
version = "1.18.1"
# HTML escaping
[workspace.dependencies.v_htmlescape]
version = "0.15.8"
# Version information generation
[workspace.dependencies.vergen-gitcl]
version = "1.0.8"
features = ["rustc"]
# Directory traversal
[workspace.dependencies.walkdir]
version = "2.5.0"
# HTTP mock server
[workspace.dependencies.wiremock]
version = "0.6.5"
# User-agent parser
[workspace.dependencies.woothee]
version = "0.13.0"
# String writing interface
[workspace.dependencies.writeable]
version = "0.5.5"
# Zero memory after use
[workspace.dependencies.zeroize]
version = "1.8.2"
# Password strength estimation
[workspace.dependencies.zxcvbn]
version = "3.1.0"
[profile.release]
codegen-units = 1 # Reduce the number of codegen units to increase optimizations
lto = true # Enable fat LTO
# A few profile opt-level tweaks to make the test suite run faster
[profile.dev.package]
argon2.opt-level = 3
bcrypt.opt-level = 3
block-buffer.opt-level = 3
cranelift-codegen.opt-level = 3
digest.opt-level = 3
hmac.opt-level = 3
generic-array.opt-level = 3
num-bigint-dig.opt-level = 3
pbkdf2.opt-level = 3
rayon.opt-level = 3
regalloc2.opt-level = 3
sha2.opt-level = 3
sqlx-macros.opt-level = 3
================================================
FILE: Dockerfile
================================================
# syntax = docker/dockerfile:1.21.0
# Copyright 2025, 2026 Element Creations Ltd.
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
# Builds a minimal image with the binary only. It is multi-arch capable,
# cross-building to aarch64 or x86_64. When cross-compiling, Docker sets two
# implicit BUILDARG: BUILDPLATFORM being the host platform and TARGETPLATFORM
# being the platform being built. Each architecture is built separately.
# The Debian version and version name must be in sync
ARG DEBIAN_VERSION=13
ARG DEBIAN_VERSION_NAME=trixie
# Keep in sync with .github/workflows/ci.yaml
ARG RUSTC_VERSION=1.93.0
ARG NODEJS_VERSION=24.13.0
# Keep in sync with .github/actions/build-policies/action.yml and policies/Makefile
ARG OPA_VERSION=1.13.1
ARG CARGO_AUDITABLE_VERSION=0.7.2
##########################################
## Build stage that builds the frontend ##
##########################################
FROM --platform=${BUILDPLATFORM} docker.io/library/node:${NODEJS_VERSION}-${DEBIAN_VERSION_NAME} AS frontend
WORKDIR /app/frontend
COPY ./frontend/.npmrc ./frontend/package.json ./frontend/package-lock.json /app/frontend/
# Network access: to fetch dependencies
RUN --network=default \
npm ci
COPY ./frontend/ /app/frontend/
COPY ./templates/ /app/templates/
RUN --network=none \
npm run build
# Move the built files
RUN --network=none \
mkdir -p /share/assets && \
cp ./dist/manifest.json /share/manifest.json && \
rm -f ./dist/index.html* ./dist/manifest.json* && \
cp ./dist/* /share/assets/
##############################################
## Build stage that builds the OPA policies ##
##############################################
FROM --platform=${BUILDPLATFORM} docker.io/library/buildpack-deps:${DEBIAN_VERSION_NAME} AS policy
ARG BUILDOS
ARG BUILDARCH
ARG OPA_VERSION
# Download Open Policy Agent
ADD --chmod=755 https://github.com/open-policy-agent/opa/releases/download/v${OPA_VERSION}/opa_${BUILDOS}_${BUILDARCH}_static /usr/local/bin/opa
WORKDIR /app/policies
COPY ./policies /app/policies
RUN --network=none \
make -B && \
chmod a+r ./policy.wasm
########################################
## Build stage that builds the binary ##
########################################
FROM --platform=${BUILDPLATFORM} docker.io/library/rust:${RUSTC_VERSION}-${DEBIAN_VERSION_NAME} AS builder
ARG CARGO_AUDITABLE_VERSION
ARG RUSTC_VERSION
# Install pinned versions of cargo-auditable
# Network access: to fetch dependencies
RUN --network=default \
cargo install --locked \
cargo-auditable@=${CARGO_AUDITABLE_VERSION}
# Install all cross-compilation targets
# Network access: to download the targets
RUN --network=default \
rustup target add \
--toolchain "${RUSTC_VERSION}" \
x86_64-unknown-linux-gnu \
aarch64-unknown-linux-gnu
RUN --network=none \
dpkg --add-architecture arm64 && \
dpkg --add-architecture amd64
ARG BUILDPLATFORM
# Install cross-compilation toolchains for all supported targets
# Network access: to install apt packages
RUN --network=default \
apt-get update && apt-get install -y \
$(if [ "${BUILDPLATFORM}" != "linux/arm64" ]; then echo "g++-aarch64-linux-gnu"; fi) \
$(if [ "${BUILDPLATFORM}" != "linux/amd64" ]; then echo "g++-x86-64-linux-gnu"; fi) \
libc6-dev-amd64-cross \
libc6-dev-arm64-cross \
g++
# Setup the cross-compilation environment
ENV \
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \
CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc \
CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ \
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-gnu-gcc \
CC_x86_64_unknown_linux_gnu=x86_64-linux-gnu-gcc \
CXX_x86_64_unknown_linux_gnu=x86_64-linux-gnu-g++
# Set the working directory
WORKDIR /app
# Copy the code
COPY ./ /app
ENV SQLX_OFFLINE=true
ARG VERGEN_GIT_DESCRIBE
ENV VERGEN_GIT_DESCRIBE=${VERGEN_GIT_DESCRIBE}
ARG TARGETARCH
# Network access: cargo auditable needs it
RUN --network=default \
--mount=type=cache,target=/root/.cargo/registry \
--mount=type=cache,target=/app/target \
RUST_TARGET=$(case "${TARGETARCH}" in \
amd64) echo "x86_64-unknown-linux-gnu" ;; \
arm64) echo "aarch64-unknown-linux-gnu" ;; \
*) echo "unsupported architecture: ${TARGETARCH}" >&2; exit 1 ;; \
esac) && \
cargo auditable build \
--locked \
--release \
--bin mas-cli \
--no-default-features \
--features docker \
--target "${RUST_TARGET}" \
&& mv "target/${RUST_TARGET}/release/mas-cli" /usr/local/bin/mas-cli
#######################################
## Prepare /usr/local/share/mas-cli/ ##
#######################################
FROM --platform=${BUILDPLATFORM} scratch AS share
COPY --from=frontend /share /share
COPY --from=policy /app/policies/policy.wasm /share/policy.wasm
COPY ./templates/ /share/templates
COPY ./translations/ /share/translations
##################################
## Runtime stage, debug variant ##
##################################
FROM gcr.io/distroless/cc-debian${DEBIAN_VERSION}:debug-nonroot AS debug
COPY --from=builder /usr/local/bin/mas-cli /usr/local/bin/mas-cli
COPY --from=share /share /usr/local/share/mas-cli
WORKDIR /
ENTRYPOINT ["/usr/local/bin/mas-cli"]
###################
## Runtime stage ##
###################
FROM gcr.io/distroless/cc-debian${DEBIAN_VERSION}:nonroot
COPY --from=builder /usr/local/bin/mas-cli /usr/local/bin/mas-cli
COPY --from=share /share /usr/local/share/mas-cli
WORKDIR /
ENTRYPOINT ["/usr/local/bin/mas-cli"]
================================================
FILE: LICENSE
================================================
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
================================================
FILE: LICENSE-COMMERCIAL
================================================
Licensees holding a valid commercial license with Element may use this
software in accordance with the terms contained in a written agreement
between you and Element.
To purchase a commercial license please contact our sales team at
licensing@element.io
================================================
FILE: README.md
================================================
# Matrix Authentication Service
MAS (Matrix Authentication Service) is a user management and authentication service for [Matrix](https://matrix.org/) homeservers, written and maintained by [Element](https://element.io/). You can directly run and manage the source code in this repository, available under an AGPL license (or alternatively under a commercial license from Element). Support is not provided by Element unless you have a subscription.
It has been created to support the migration of Matrix to a next-generation of auth APIs per [MSC3861](https://github.com/matrix-org/matrix-doc/pull/3861).
See the [Documentation](https://element-hq.github.io/matrix-authentication-service/index.html) for information on installation and use.
You can learn more about Matrix and next-generation auth at [areweoidcyet.com](https://areweoidcyet.com/).
## 🚀 Getting started
This component is developed and maintained by [Element](https://element.io). It gets shipped as part of the **Element Server Suite (ESS)** which provides the official means of deployment.
ESS is a Matrix distribution from Element with focus on quality and ease of use. It ships a full Matrix stack tailored to the respective use case.
There are three editions of ESS:
- [ESS Community](https://github.com/element-hq/ess-helm) - the free Matrix
distribution from Element tailored to small-/mid-scale, non-commercial
community use cases
- [ESS Pro](https://element.io/server-suite) - the commercial Matrix
distribution from Element for professional use
- [ESS TI-M](https://element.io/server-suite/ti-messenger) - a special version
of ESS Pro focused on the requirements of TI-Messenger Pro and ePA as
specified by the German National Digital Health Agency Gematik
## 💬 Community room
Developers and users of Matrix Authentication Service can chat in the [#matrix-auth:matrix.org](https://matrix.to/#/#matrix-auth:matrix.org) room on Matrix.
## 🛠️ Standalone installation and configuration
The best way to get a modern Element Matrix stack is through the [Element Server Suite](https://element.io/en/server-suite), which includes MAS.
The MAS documentation describes [how to install and configure MAS](https://element-hq.github.io/matrix-authentication-service/setup/).
We recommend using the [Docker image](https://element-hq.github.io/matrix-authentication-service/setup/installation.html#using-the-docker-image) or the [pre-built binaries](https://element-hq.github.io/matrix-authentication-service/setup/installation.html#pre-built-binaries).
## 📖 Translations
Matrix Authentication Service is available in multiple languages.
Anyone can contribute to translations through [Localazy](https://localazy.com/element-matrix-authentication-service/).
## 🏗️ Contributing
See the [contribution guidelines](https://element-hq.github.io/matrix-authentication-service/development/contributing.html) for information on how to contribute to this project.
## ⚖️ Copyright & License
Copyright 2021-2024 The Matrix.org Foundation C.I.C.
Copyright 2024, 2025 New Vector Ltd.
Copyright 2025, 2026 Element Creations Ltd.
This software is dual-licensed by Element Creations Ltd (Element). It can be used either:
(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR
(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to).
Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses.
================================================
FILE: biome.json
================================================
{
"$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",
"assist": { "actions": { "source": { "organizeImports": "on" } } },
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"includes": [
"**",
"!**/.devcontainer/**",
"!**/docs/**",
"!**/translations/**",
"!**/policies/**",
"!**/crates/**",
"!**/frontend/package.json",
"!**/frontend/src/gql/**",
"!**/frontend/src/routeTree.gen.ts",
"!**/frontend/.storybook/locales.ts",
"!**/frontend/.storybook/public/mockServiceWorker.js",
"!**/frontend/locales/**/*.json",
"!**/coverage/**",
"!**/dist/**"
]
},
"formatter": {
"enabled": true,
"useEditorconfig": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"complexity": {
"noImportantStyles": "off"
},
"suspicious": {
"noUnknownAtRules": "off"
},
"correctness": {
"noUnusedImports": "warn",
"noUnusedVariables": "warn"
},
"style": {
"noParameterAssign": "error",
"useAsConstAssertion": "error",
"useDefaultParameterLast": "error",
"useEnumInitializers": "error",
"useSelfClosingElements": "error",
"useSingleVarDeclarator": "error",
"noUnusedTemplateLiteral": "error",
"useNumberNamespace": "error",
"noInferrableTypes": "error",
"noUselessElse": "error",
"noDescendingSpecificity": "off"
}
}
}
}
================================================
FILE: book.toml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
# Documentation for possible options in this file is at
# https://rust-lang.github.io/mdBook/format/config.html
[book]
title = "Matrix Authentication Service"
authors = ["Element Backend Team"]
language = "en"
src = "docs"
[build]
build-dir = "target/book"
[output.html]
# The URL visitors will be directed to when they try to edit a page
edit-url-template = "https://github.com/element-hq/matrix-authentication-service/edit/main/{path}"
# The source code URL of the repository
git-repository-url = "https://github.com/element-hq/matrix-authentication-service"
# The path that the docs are hosted on
site-url = "/matrix-authentication-service/"
================================================
FILE: clippy.toml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
doc-valid-idents = ["OpenID", "OAuth", "UserInfo", "..", "PostgreSQL", "SQLite"]
disallowed-methods = [
{ path = "rand::thread_rng", reason = "do not create rngs on the fly, pass them as parameters" },
{ path = "chrono::Utc::now", reason = "source the current time from the clock instead" },
{ path = "ulid::Ulid::from_datetime", reason = "use Ulid::from_datetime_with_source instead" },
{ path = "ulid::Ulid::new", reason = "use Ulid::from_datetime_with_source instead" },
{ path = "reqwest::Client::new", reason = "use mas_http::reqwest_client instead" },
{ path = "reqwest::RequestBuilder::send", reason = "use send_traced instead" },
]
disallowed-types = [
{ path = "std::path::PathBuf", reason = "use camino::Utf8PathBuf instead" },
{ path = "std::path::Path", reason = "use camino::Utf8Path instead" },
{ path = "axum::extract::Query", reason = "use axum_extra::extract::Query instead. The built-in version doesn't deserialise lists."},
{ path = "axum::extract::rejection::QueryRejection", reason = "use axum_extra::extract::QueryRejection instead"}
]
================================================
FILE: crates/axum-utils/Cargo.toml
================================================
# Copyright 2025 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# Please see LICENSE files in the repository root for full details.
[package]
name = "mas-axum-utils"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
publish.workspace = true
[lints]
workspace = true
[dependencies]
anyhow.workspace = true
axum.workspace = true
axum-extra.workspace = true
base64ct.workspace = true
chrono.workspace = true
headers.workspace = true
http.workspace = true
icu_locid.workspace = true
mime.workspace = true
rand.workspace = true
reqwest.workspace = true
sentry.workspace = true
serde.workspace = true
serde_with.workspace = true
serde_json.workspace = true
thiserror.workspace = true
tokio.workspace = true
tracing.workspace = true
url.workspace = true
ulid.workspace = true
oauth2-types.workspace = true
mas-data-model.workspace = true
mas-http.workspace = true
mas-iana.workspace = true
mas-jose.workspace = true
mas-keystore.workspace = true
mas-storage.workspace = true
mas-templates.workspace = true
================================================
FILE: crates/axum-utils/src/client_authorization.rs
================================================
// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
use std::collections::HashMap;
use axum::{
BoxError, Json,
extract::{
Form, FromRequest,
rejection::{FailedToDeserializeForm, FormRejection},
},
response::IntoResponse,
};
use headers::authorization::{Basic, Bearer, Credentials as _};
use http::{Request, StatusCode};
use mas_data_model::{Client, JwksOrJwksUri};
use mas_http::RequestBuilderExt;
use mas_iana::oauth::OAuthClientAuthenticationMethod;
use mas_jose::{jwk::PublicJsonWebKeySet, jwt::Jwt};
use mas_keystore::Encrypter;
use mas_storage::{RepositoryAccess, oauth2::OAuth2ClientRepository};
use oauth2_types::errors::{ClientError, ClientErrorCode};
use serde::{Deserialize, de::DeserializeOwned};
use serde_json::Value;
use thiserror::Error;
use crate::record_error;
static JWT_BEARER_CLIENT_ASSERTION: &str = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
#[derive(Deserialize)]
struct AuthorizedForm<F = ()> {
client_id: Option<String>,
client_secret: Option<String>,
client_assertion_type: Option<String>,
client_assertion: Option<String>,
#[serde(flatten)]
inner: F,
}
#[derive(Debug, PartialEq, Eq)]
pub enum Credentials {
None {
client_id: String,
},
ClientSecretBasic {
client_id: String,
client_secret: String,
},
ClientSecretPost {
client_id: String,
client_secret: String,
},
ClientAssertionJwtBearer {
client_id: String,
jwt: Box<Jwt<'static, HashMap<String, serde_json::Value>>>,
},
BearerToken {
token: String,
},
}
impl Credentials {
/// Get the `client_id` of the credentials
#[must_use]
pub fn client_id(&self) -> Option<&str> {
match self {
Credentials::None { client_id }
| Credentials::ClientSecretBasic { client_id, .. }
| Credentials::ClientSecretPost { client_id, .. }
| Credentials::ClientAssertionJwtBearer { client_id, .. } => Some(client_id),
Credentials::BearerToken { .. } => None,
}
}
/// Get the bearer token from the credentials.
#[must_use]
pub fn bearer_token(&self) -> Option<&str> {
match self {
Credentials::BearerToken { token } => Some(token),
_ => None,
}
}
/// Fetch the client from the database
///
/// # Errors
///
/// Returns an error if the client could not be found or if the underlying
/// repository errored.
pub async fn fetch<E>(
&self,
repo: &mut impl RepositoryAccess<Error = E>,
) -> Result<Option<Client>, E> {
let client_id = match self {
Credentials::None { client_id }
| Credentials::ClientSecretBasic { client_id, .. }
| Credentials::ClientSecretPost { client_id, .. }
| Credentials::ClientAssertionJwtBearer { client_id, .. } => client_id,
Credentials::BearerToken { .. } => return Ok(None),
};
repo.oauth2_client().find_by_client_id(client_id).await
}
/// Verify credentials presented by the client for authentication
///
/// # Errors
///
/// Returns an error if the credentials are invalid.
#[tracing::instrument(skip_all)]
pub async fn verify(
&self,
http_client: &reqwest::Client,
encrypter: &Encrypter,
method: &OAuthClientAuthenticationMethod,
client: &Client,
) -> Result<(), CredentialsVerificationError> {
match (self, method) {
(Credentials::None { .. }, OAuthClientAuthenticationMethod::None) => {}
(
Credentials::ClientSecretPost { client_secret, .. },
OAuthClientAuthenticationMethod::ClientSecretPost,
)
| (
Credentials::ClientSecretBasic { client_secret, .. },
OAuthClientAuthenticationMethod::ClientSecretBasic,
) => {
// Decrypt the client_secret
let encrypted_client_secret = client
.encrypted_client_secret
.as_ref()
.ok_or(CredentialsVerificationError::InvalidClientConfig)?;
let decrypted_client_secret = encrypter
.decrypt_string(encrypted_client_secret)
.map_err(|_e| CredentialsVerificationError::DecryptionError)?;
// Check if the client_secret matches
if client_secret.as_bytes() != decrypted_client_secret {
return Err(CredentialsVerificationError::ClientSecretMismatch);
}
}
(
Credentials::ClientAssertionJwtBearer { jwt, .. },
OAuthClientAuthenticationMethod::PrivateKeyJwt,
) => {
// Get the client JWKS
let jwks = client
.jwks
.as_ref()
.ok_or(CredentialsVerificationError::InvalidClientConfig)?;
let jwks = fetch_jwks(http_client, jwks)
.await
.map_err(CredentialsVerificationError::JwksFetchFailed)?;
jwt.verify_with_jwks(&jwks)
.map_err(|_| CredentialsVerificationError::InvalidAssertionSignature)?;
}
(
Credentials::ClientAssertionJwtBearer { jwt, .. },
OAuthClientAuthenticationMethod::ClientSecretJwt,
) => {
// Decrypt the client_secret
let encrypted_client_secret = client
.encrypted_client_secret
.as_ref()
.ok_or(CredentialsVerificationError::InvalidClientConfig)?;
let decrypted_client_secret = encrypter
.decrypt_string(encrypted_client_secret)
.map_err(|_e| CredentialsVerificationError::DecryptionError)?;
jwt.verify_with_shared_secret(decrypted_client_secret)
.map_err(|_| CredentialsVerificationError::InvalidAssertionSignature)?;
}
(_, _) => {
return Err(CredentialsVerificationError::AuthenticationMethodMismatch);
}
}
Ok(())
}
}
async fn fetch_jwks(
http_client: &reqwest::Client,
jwks: &JwksOrJwksUri,
) -> Result<PublicJsonWebKeySet, BoxError> {
let uri = match jwks {
JwksOrJwksUri::Jwks(j) => return Ok(j.clone()),
JwksOrJwksUri::JwksUri(u) => u,
};
let response = http_client
.get(uri.as_str())
.send_traced()
.await?
.error_for_status()?
.json()
.await?;
Ok(response)
}
#[derive(Debug, Error)]
pub enum CredentialsVerificationError {
#[error("failed to decrypt client credentials")]
DecryptionError,
#[error("invalid client configuration")]
InvalidClientConfig,
#[error("client secret did not match")]
ClientSecretMismatch,
#[error("authentication method mismatch")]
AuthenticationMethodMismatch,
#[error("invalid assertion signature")]
InvalidAssertionSignature,
#[error("failed to fetch jwks")]
JwksFetchFailed(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
}
impl CredentialsVerificationError {
/// Returns true if the error is an internal error, not caused by the client
#[must_use]
pub fn is_internal(&self) -> bool {
matches!(
self,
Self::DecryptionError | Self::InvalidClientConfig | Self::JwksFetchFailed(_)
)
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ClientAuthorization<F = ()> {
pub credentials: Credentials,
pub form: Option<F>,
}
impl<F> ClientAuthorization<F> {
/// Get the `client_id` from the credentials.
#[must_use]
pub fn client_id(&self) -> Option<&str> {
self.credentials.client_id()
}
}
#[derive(Debug, Error)]
pub enum ClientAuthorizationError {
#[error("Invalid Authorization header")]
InvalidHeader,
#[error("Could not deserialize request body")]
BadForm(#[source] FailedToDeserializeForm),
#[error("client_id in form ({form:?}) does not match credential ({credential:?})")]
ClientIdMismatch { credential: String, form: String },
#[error("Unsupported client_assertion_type: {client_assertion_type}")]
UnsupportedClientAssertion { client_assertion_type: String },
#[error("No credentials were presented")]
MissingCredentials,
#[error("Invalid request")]
InvalidRequest,
#[error("Invalid client_assertion")]
InvalidAssertion,
#[error(transparent)]
Internal(Box<dyn std::error::Error>),
}
impl IntoResponse for ClientAuthorizationError {
fn into_response(self) -> axum::response::Response {
let sentry_event_id = record_error!(self, Self::Internal(_));
match &self {
ClientAuthorizationError::InvalidHeader => (
StatusCode::BAD_REQUEST,
sentry_event_id,
Json(ClientError::new(
ClientErrorCode::InvalidRequest,
"Invalid Authorization header",
)),
),
ClientAuthorizationError::BadForm(err) => (
StatusCode::BAD_REQUEST,
sentry_event_id,
Json(
ClientError::from(ClientErrorCode::InvalidRequest)
.with_description(format!("{err}")),
),
),
ClientAuthorizationError::ClientIdMismatch { .. } => (
StatusCode::BAD_REQUEST,
sentry_event_id,
Json(
ClientError::from(ClientErrorCode::InvalidGrant)
.with_description(format!("{self}")),
),
),
ClientAuthorizationError::UnsupportedClientAssertion { .. } => (
StatusCode::BAD_REQUEST,
sentry_event_id,
Json(
ClientError::from(ClientErrorCode::InvalidRequest)
.with_description(format!("{self}")),
),
),
ClientAuthorizationError::MissingCredentials => (
StatusCode::BAD_REQUEST,
sentry_event_id,
Json(ClientError::new(
ClientErrorCode::InvalidRequest,
"No credentials were presented",
)),
),
ClientAuthorizationError::InvalidRequest => (
StatusCode::BAD_REQUEST,
sentry_event_id,
Json(ClientError::from(ClientErrorCode::InvalidRequest)),
),
ClientAuthorizationError::InvalidAssertion => (
StatusCode::BAD_REQUEST,
sentry_event_id,
Json(ClientError::new(
ClientErrorCode::InvalidRequest,
"Invalid client_assertion",
)),
),
ClientAuthorizationError::Internal(e) => (
StatusCode::INTERNAL_SERVER_ERROR,
sentry_event_id,
Json(
ClientError::from(ClientErrorCode::ServerError)
.with_description(format!("{e}")),
),
),
}
.into_response()
}
}
impl<S, F> FromRequest<S> for ClientAuthorization<F>
where
F: DeserializeOwned,
S: Send + Sync,
{
type Rejection = ClientAuthorizationError;
async fn from_request(
req: Request<axum::body::Body>,
state: &S,
) -> Result<Self, Self::Rejection> {
enum Authorization {
Basic(String, String),
Bearer(String),
}
// Sadly, the typed-header 'Authorization' doesn't let us check for both
// Basic and Bearer at the same time, so we need to parse them manually
let authorization = if let Some(header) = req.headers().get(http::header::AUTHORIZATION) {
let bytes = header.as_bytes();
if bytes.len() >= 6 && bytes[..6].eq_ignore_ascii_case(b"Basic ") {
let Some(decoded) = Basic::decode(header) else {
return Err(ClientAuthorizationError::InvalidHeader);
};
Some(Authorization::Basic(
decoded.username().to_owned(),
decoded.password().to_owned(),
))
} else if bytes.len() >= 7 && bytes[..7].eq_ignore_ascii_case(b"Bearer ") {
let Some(decoded) = Bearer::decode(header) else {
return Err(ClientAuthorizationError::InvalidHeader);
};
Some(Authorization::Bearer(decoded.token().to_owned()))
} else {
return Err(ClientAuthorizationError::InvalidHeader);
}
} else {
None
};
// Take the form value
let (
client_id_from_form,
client_secret_from_form,
client_assertion_type,
client_assertion,
form,
) = match Form::<AuthorizedForm<F>>::from_request(req, state).await {
Ok(Form(form)) => (
form.client_id,
form.client_secret,
form.client_assertion_type,
form.client_assertion,
Some(form.inner),
),
// If it is not a form, continue
Err(FormRejection::InvalidFormContentType(_err)) => (None, None, None, None, None),
// If the form could not be read, return a Bad Request error
Err(FormRejection::FailedToDeserializeForm(err)) => {
return Err(ClientAuthorizationError::BadForm(err));
}
// Other errors (body read twice, byte stream broke) return an internal error
Err(e) => return Err(ClientAuthorizationError::Internal(Box::new(e))),
};
// And now, figure out the actual auth method
let credentials = match (
authorization,
client_id_from_form,
client_secret_from_form,
client_assertion_type,
client_assertion,
) {
(
Some(Authorization::Basic(client_id, client_secret)),
client_id_from_form,
None,
None,
None,
) => {
if let Some(client_id_from_form) = client_id_from_form {
// If the client_id was in the body, verify it matches with the header
if client_id != client_id_from_form {
return Err(ClientAuthorizationError::ClientIdMismatch {
credential: client_id,
form: client_id_from_form,
});
}
}
Credentials::ClientSecretBasic {
client_id,
client_secret,
}
}
(None, Some(client_id), Some(client_secret), None, None) => {
// Got both client_id and client_secret from the form
Credentials::ClientSecretPost {
client_id,
client_secret,
}
}
(None, Some(client_id), None, None, None) => {
// Only got a client_id in the form
Credentials::None { client_id }
}
(
None,
client_id_from_form,
None,
Some(client_assertion_type),
Some(client_assertion),
) if client_assertion_type == JWT_BEARER_CLIENT_ASSERTION => {
// Got a JWT bearer client_assertion
let jwt: Jwt<'static, HashMap<String, Value>> = Jwt::try_from(client_assertion)
.map_err(|_| ClientAuthorizationError::InvalidAssertion)?;
let client_id = if let Some(Value::String(client_id)) = jwt.payload().get("sub") {
client_id.clone()
} else {
return Err(ClientAuthorizationError::InvalidAssertion);
};
if let Some(client_id_from_form) = client_id_from_form {
// If the client_id was in the body, verify it matches the one in the JWT
if client_id != client_id_from_form {
return Err(ClientAuthorizationError::ClientIdMismatch {
credential: client_id,
form: client_id_from_form,
});
}
}
Credentials::ClientAssertionJwtBearer {
client_id,
jwt: Box::new(jwt),
}
}
(None, None, None, Some(client_assertion_type), Some(_client_assertion)) => {
// Got another unsupported client_assertion
return Err(ClientAuthorizationError::UnsupportedClientAssertion {
client_assertion_type,
});
}
(Some(Authorization::Bearer(token)), None, None, None, None) => {
// Got a bearer token
Credentials::BearerToken { token }
}
(None, None, None, None, None) => {
// Special case when there are no credentials anywhere
return Err(ClientAuthorizationError::MissingCredentials);
}
_ => {
// Every other combination is an invalid request
return Err(ClientAuthorizationError::InvalidRequest);
}
};
Ok(ClientAuthorization { credentials, form })
}
}
#[cfg(test)]
mod tests {
use axum::body::Body;
use http::{Method, Request};
use super::*;
#[tokio::test]
async fn none_test() {
let req = Request::builder()
.method(Method::POST)
.header(
http::header::CONTENT_TYPE,
mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(),
)
.body(Body::new("client_id=client-id&foo=bar".to_owned()))
.unwrap();
assert_eq!(
ClientAuthorization::<serde_json::Value>::from_request(req, &())
.await
.unwrap(),
ClientAuthorization {
credentials: Credentials::None {
client_id: "client-id".to_owned(),
},
form: Some(serde_json::json!({"foo": "bar"})),
}
);
}
#[tokio::test]
async fn client_secret_basic_test() {
let req = Request::builder()
.method(Method::POST)
.header(
http::header::CONTENT_TYPE,
mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(),
)
.header(
http::header::AUTHORIZATION,
"Basic Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ=",
)
.body(Body::new("foo=bar".to_owned()))
.unwrap();
assert_eq!(
ClientAuthorization::<serde_json::Value>::from_request(req, &())
.await
.unwrap(),
ClientAuthorization {
credentials: Credentials::ClientSecretBasic {
client_id: "client-id".to_owned(),
client_secret: "client-secret".to_owned(),
},
form: Some(serde_json::json!({"foo": "bar"})),
}
);
// client_id in both header and body
let req = Request::builder()
.method(Method::POST)
.header(
http::header::CONTENT_TYPE,
mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(),
)
.header(
http::header::AUTHORIZATION,
"Basic Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ=",
)
.body(Body::new("client_id=client-id&foo=bar".to_owned()))
.unwrap();
assert_eq!(
ClientAuthorization::<serde_json::Value>::from_request(req, &())
.await
.unwrap(),
ClientAuthorization {
credentials: Credentials::ClientSecretBasic {
client_id: "client-id".to_owned(),
client_secret: "client-secret".to_owned(),
},
form: Some(serde_json::json!({"foo": "bar"})),
}
);
// client_id in both header and body mismatch
let req = Request::builder()
.method(Method::POST)
.header(
http::header::CONTENT_TYPE,
mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(),
)
.header(
http::header::AUTHORIZATION,
"Basic Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ=",
)
.body(Body::new("client_id=mismatch-id&foo=bar".to_owned()))
.unwrap();
assert!(matches!(
ClientAuthorization::<serde_json::Value>::from_request(req, &()).await,
Err(ClientAuthorizationError::ClientIdMismatch { .. }),
));
// Invalid header
let req = Request::builder()
.method(Method::POST)
.header(
http::header::CONTENT_TYPE,
mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(),
)
.header(http::header::AUTHORIZATION, "Basic invalid")
.body(Body::new("foo=bar".to_owned()))
.unwrap();
assert!(matches!(
ClientAuthorization::<serde_json::Value>::from_request(req, &()).await,
Err(ClientAuthorizationError::InvalidHeader),
));
}
#[tokio::test]
async fn client_secret_post_test() {
let req = Request::builder()
.method(Method::POST)
.header(
http::header::CONTENT_TYPE,
mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(),
)
.body(Body::new(
"client_id=client-id&client_secret=client-secret&foo=bar".to_owned(),
))
.unwrap();
assert_eq!(
ClientAuthorization::<serde_json::Value>::from_request(req, &())
.await
.unwrap(),
ClientAuthorization {
credentials: Credentials::ClientSecretPost {
client_id: "client-id".to_owned(),
client_secret: "client-secret".to_owned(),
},
form: Some(serde_json::json!({"foo": "bar"})),
}
);
}
#[tokio::test]
async fn client_assertion_test() {
// Signed with client_secret = "client-secret"
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJjbGllbnQtaWQiLCJzdWIiOiJjbGllbnQtaWQiLCJhdWQiOiJodHRwczovL2V4YW1wbGUuY29tL29hdXRoMi9pbnRyb3NwZWN0IiwianRpIjoiYWFiYmNjIiwiZXhwIjoxNTE2MjM5MzIyLCJpYXQiOjE1MTYyMzkwMjJ9.XTaACG_Rww0GPecSZvkbem-AczNy9LLNBueCLCiQajU";
let body = Body::new(format!(
"client_assertion_type={JWT_BEARER_CLIENT_ASSERTION}&client_assertion={jwt}&foo=bar",
));
let req = Request::builder()
.method(Method::POST)
.header(
http::header::CONTENT_TYPE,
mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(),
)
.body(body)
.unwrap();
let authz = ClientAuthorization::<serde_json::Value>::from_request(req, &())
.await
.unwrap();
assert_eq!(authz.form, Some(serde_json::json!({"foo": "bar"})));
let Credentials::ClientAssertionJwtBearer { client_id, jwt } = authz.credentials else {
panic!("expected a JWT client_assertion");
};
assert_eq!(client_id, "client-id");
jwt.verify_with_shared_secret(b"client-secret".to_vec())
.unwrap();
}
#[tokio::test]
async fn bearer_token_test() {
let req = Request::builder()
.method(Method::POST)
.header(
http::header::CONTENT_TYPE,
mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(),
)
.header(http::header::AUTHORIZATION, "Bearer token")
.body(Body::new("foo=bar".to_owned()))
.unwrap();
assert_eq!(
ClientAuthorization::<serde_json::Value>::from_request(req, &())
.await
.unwrap(),
ClientAuthorization {
credentials: Credentials::BearerToken {
token: "token".to_owned(),
},
form: Some(serde_json::json!({"foo": "bar"})),
}
);
}
}
================================================
FILE: crates/axum-utils/src/cookies.rs
================================================
// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
//! Private (encrypted) cookie jar, based on axum-extra's cookie jar
use std::convert::Infallible;
use axum::{
extract::{FromRef, FromRequestParts},
response::{IntoResponseParts, ResponseParts},
};
use axum_extra::extract::cookie::{Cookie, Key, PrivateCookieJar, SameSite};
use http::request::Parts;
use serde::{Serialize, de::DeserializeOwned};
use thiserror::Error;
use url::Url;
#[derive(Debug, Error)]
#[error("could not decode cookie")]
pub enum CookieDecodeError {
Deserialize(#[from] serde_json::Error),
}
/// Manages cookie options and encryption key
///
/// This is meant to be accessible through axum's state via the [`FromRef`]
/// trait
#[derive(Clone)]
pub struct CookieManager {
options: CookieOption,
key: Key,
}
impl CookieManager {
#[must_use]
pub const fn new(base_url: Url, key: Key) -> Self {
let options = CookieOption::new(base_url);
Self { options, key }
}
#[must_use]
pub fn derive_from(base_url: Url, key: &[u8]) -> Self {
let key = Key::derive_from(key);
Self::new(base_url, key)
}
#[must_use]
pub fn cookie_jar(&self) -> CookieJar {
let inner = PrivateCookieJar::new(self.key.clone());
let options = self.options.clone();
CookieJar { inner, options }
}
#[must_use]
pub fn cookie_jar_from_headers(&self, headers: &http::HeaderMap) -> CookieJar {
let inner = PrivateCookieJar::from_headers(headers, self.key.clone());
let options = self.options.clone();
CookieJar { inner, options }
}
}
impl<S> FromRequestParts<S> for CookieJar
where
CookieManager: FromRef<S>,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
let cookie_manager = CookieManager::from_ref(state);
Ok(cookie_manager.cookie_jar_from_headers(&parts.headers))
}
}
#[derive(Debug, Clone)]
struct CookieOption {
base_url: Url,
}
impl CookieOption {
const fn new(base_url: Url) -> Self {
Self { base_url }
}
fn secure(&self) -> bool {
self.base_url.scheme() == "https"
}
fn path(&self) -> &str {
self.base_url.path()
}
fn apply<'a>(&self, mut cookie: Cookie<'a>) -> Cookie<'a> {
cookie.set_http_only(true);
cookie.set_secure(self.secure());
cookie.set_path(self.path().to_owned());
cookie.set_same_site(SameSite::Lax);
cookie
}
}
/// A cookie jar which encrypts cookies & sets secure options
pub struct CookieJar {
inner: PrivateCookieJar<Key>,
options: CookieOption,
}
impl CookieJar {
/// Save the given payload in a cookie
///
/// If `permanent` is true, the cookie will be valid for 10 years
///
/// # Panics
///
/// Panics if the payload cannot be serialized
#[must_use]
pub fn save<T: Serialize>(mut self, key: &str, payload: &T, permanent: bool) -> Self {
let serialized =
serde_json::to_string(payload).expect("failed to serialize cookie payload");
let cookie = Cookie::new(key.to_owned(), serialized);
let mut cookie = self.options.apply(cookie);
if permanent {
// XXX: this should use a clock
cookie.make_permanent();
}
self.inner = self.inner.add(cookie);
self
}
/// Remove a cookie from the jar
#[must_use]
pub fn remove(mut self, key: &str) -> Self {
self.inner = self.inner.remove(key.to_owned());
self
}
/// Load and deserialize a cookie from the jar
///
/// Returns `None` if the cookie is not present
///
/// # Errors
///
/// Returns an error if the cookie cannot be deserialized
pub fn load<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>, CookieDecodeError> {
let Some(cookie) = self.inner.get(key) else {
return Ok(None);
};
let decoded = serde_json::from_str(cookie.value())?;
Ok(Some(decoded))
}
}
impl IntoResponseParts for CookieJar {
type Error = Infallible;
fn into_response_parts(self, res: ResponseParts) -> Result<ResponseParts, Self::Error> {
self.inner.into_response_parts(res)
}
}
================================================
FILE: crates/axum-utils/src/csrf.rs
================================================
// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
use base64ct::{Base64UrlUnpadded, Encoding};
use chrono::{DateTime, Duration, Utc};
use mas_data_model::Clock;
use rand::{Rng, RngCore, distributions::Standard, prelude::Distribution as _};
use serde::{Deserialize, Serialize};
use serde_with::{TimestampSeconds, serde_as};
use thiserror::Error;
use crate::cookies::{CookieDecodeError, CookieJar};
/// Failed to validate CSRF token
#[derive(Debug, Error)]
pub enum CsrfError {
/// The token in the form did not match the token in the cookie
#[error("CSRF token mismatch")]
Mismatch,
/// The token in the form did not match the token in the cookie
#[error("Missing CSRF cookie")]
Missing,
/// Failed to decode the token
#[error("could not decode CSRF cookie")]
DecodeCookie(#[from] CookieDecodeError),
/// The token expired
#[error("CSRF token expired")]
Expired,
/// Failed to decode the token
#[error("could not decode CSRF token")]
Decode(#[from] base64ct::Error),
}
/// A CSRF token
#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
pub struct CsrfToken {
#[serde_as(as = "TimestampSeconds<i64>")]
expiration: DateTime<Utc>,
token: [u8; 32],
}
impl CsrfToken {
/// Create a new token from a defined value valid for a specified duration
fn new(token: [u8; 32], now: DateTime<Utc>, ttl: Duration) -> Self {
let expiration = now + ttl;
Self { expiration, token }
}
/// Generate a new random token valid for a specified duration
fn generate(now: DateTime<Utc>, mut rng: impl Rng, ttl: Duration) -> Self {
let token = Standard.sample(&mut rng);
Self::new(token, now, ttl)
}
/// Generate a new token with the same value but an up to date expiration
fn refresh(self, now: DateTime<Utc>, ttl: Duration) -> Self {
Self::new(self.token, now, ttl)
}
/// Get the value to include in HTML forms
#[must_use]
pub fn form_value(&self) -> String {
Base64UrlUnpadded::encode_string(&self.token[..])
}
/// Verifies that the value got from an HTML form matches this token
///
/// # Errors
///
/// Returns an error if the value in the form does not match this token
pub fn verify_form_value(&self, form_value: &str) -> Result<(), CsrfError> {
let form_value = Base64UrlUnpadded::decode_vec(form_value)?;
if self.token[..] == form_value {
Ok(())
} else {
Err(CsrfError::Mismatch)
}
}
fn verify_expiration(self, now: DateTime<Utc>) -> Result<Self, CsrfError> {
if now < self.expiration {
Ok(self)
} else {
Err(CsrfError::Expired)
}
}
}
// A CSRF-protected form
#[derive(Deserialize)]
pub struct ProtectedForm<T> {
csrf: String,
#[serde(flatten)]
inner: T,
}
pub trait CsrfExt {
/// Get the current CSRF token out of the cookie jar, generating a new one
/// if necessary
fn csrf_token<C, R>(self, clock: &C, rng: R) -> (CsrfToken, Self)
where
R: RngCore,
C: Clock;
/// Verify that the given CSRF-protected form is valid, returning the inner
/// value
///
/// # Errors
///
/// Returns an error if the CSRF cookie is missing or if the value in the
/// form is invalid
fn verify_form<C, T>(&self, clock: &C, form: ProtectedForm<T>) -> Result<T, CsrfError>
where
C: Clock;
}
impl CsrfExt for CookieJar {
fn csrf_token<C, R>(self, clock: &C, rng: R) -> (CsrfToken, Self)
where
R: RngCore,
C: Clock,
{
let now = clock.now();
let maybe_token = match self.load::<CsrfToken>("csrf") {
Ok(Some(token)) => {
let token = token.verify_expiration(now);
// If the token is expired, just ignore it
token.ok()
}
Ok(None) => None,
Err(e) => {
tracing::warn!("Failed to decode CSRF cookie: {}", e);
None
}
};
let token = maybe_token.map_or_else(
|| CsrfToken::generate(now, rng, Duration::try_hours(1).unwrap()),
|token| token.refresh(now, Duration::try_hours(1).unwrap()),
);
let jar = self.save("csrf", &token, false);
(token, jar)
}
fn verify_form<C, T>(&self, clock: &C, form: ProtectedForm<T>) -> Result<T, CsrfError>
where
C: Clock,
{
let token: CsrfToken = self.load("csrf")?.ok_or(CsrfError::Missing)?;
let token = token.verify_expiration(clock.now())?;
token.verify_form_value(&form.csrf)?;
Ok(form.inner)
}
}
================================================
FILE: crates/axum-utils/src/error_wrapper.rs
================================================
// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
use axum::response::{IntoResponse, Response};
use crate::InternalError;
/// A simple wrapper around an error that implements [`IntoResponse`].
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct ErrorWrapper<T>(#[from] pub T);
impl<T> IntoResponse for ErrorWrapper<T>
where
T: std::error::Error + 'static,
{
fn into_response(self) -> Response {
InternalError::from(self.0).into_response()
}
}
================================================
FILE: crates/axum-utils/src/fancy_error.rs
================================================
// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
use axum::{
Extension,
http::StatusCode,
response::{IntoResponse, Response},
};
use axum_extra::typed_header::TypedHeader;
use headers::ContentType;
use mas_templates::ErrorContext;
use crate::sentry::SentryEventID;
fn build_context(mut err: &dyn std::error::Error) -> ErrorContext {
let description = err.to_string();
let mut details = Vec::new();
while let Some(source) = err.source() {
err = source;
details.push(err.to_string());
}
ErrorContext::new()
.with_description(description)
.with_details(details.join("\n"))
}
pub struct GenericError {
error: Box<dyn std::error::Error + 'static>,
code: StatusCode,
}
impl IntoResponse for GenericError {
fn into_response(self) -> Response {
tracing::warn!(message = &*self.error);
let context = build_context(&*self.error);
let context_text = format!("{context}");
(
self.code,
TypedHeader(ContentType::text()),
Extension(context),
context_text,
)
.into_response()
}
}
impl GenericError {
pub fn new(code: StatusCode, err: impl std::error::Error + 'static) -> Self {
Self {
error: Box::new(err),
code,
}
}
}
pub struct InternalError {
error: Box<dyn std::error::Error + 'static>,
}
impl IntoResponse for InternalError {
fn into_response(self) -> Response {
tracing::error!(message = &*self.error);
let event_id = SentryEventID::for_last_event();
let context = build_context(&*self.error);
let context_text = format!("{context}");
(
StatusCode::INTERNAL_SERVER_ERROR,
TypedHeader(ContentType::text()),
event_id,
Extension(context),
context_text,
)
.into_response()
}
}
impl<E: std::error::Error + 'static> From<E> for InternalError {
fn from(err: E) -> Self {
Self {
error: Box::new(err),
}
}
}
impl InternalError {
/// Create a new error from a boxed error
#[must_use]
pub fn new(error: Box<dyn std::error::Error + 'static>) -> Self {
Self { error }
}
/// Create a new error from an [`anyhow::Error`]
#[must_use]
pub fn from_anyhow(err: anyhow::Error) -> Self {
Self {
error: err.into_boxed_dyn_error(),
}
}
}
================================================
FILE: crates/axum-utils/src/jwt.rs
================================================
// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
use axum::response::{IntoResponse, Response};
use axum_extra::typed_header::TypedHeader;
use headers::ContentType;
use mas_jose::jwt::Jwt;
use mime::Mime;
pub struct JwtResponse<T>(pub Jwt<'static, T>);
impl<T> IntoResponse for JwtResponse<T> {
fn into_response(self) -> Response {
let application_jwt: Mime = "application/jwt".parse().unwrap();
let content_type = ContentType::from(application_jwt);
(TypedHeader(content_type), self.0.into_string()).into_response()
}
}
================================================
FILE: crates/axum-utils/src/language_detection.rs
================================================
// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
use std::cmp::Reverse;
use headers::{Error, Header};
use http::{HeaderName, HeaderValue, header::ACCEPT_LANGUAGE};
use icu_locid::Locale;
#[derive(PartialEq, Eq, Debug)]
struct AcceptLanguagePart {
// None means *
locale: Option<Locale>,
// Quality is between 0 and 1 with 3 decimal places
// Which we map from 0 to 1000, e.g. 0.5 becomes 500
quality: u16,
}
impl PartialOrd for AcceptLanguagePart {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for AcceptLanguagePart {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
// When comparing two AcceptLanguage structs, we only consider the
// quality, in reverse.
Reverse(self.quality).cmp(&Reverse(other.quality))
}
}
/// A header that represents the `Accept-Language` header.
#[derive(PartialEq, Eq, Debug)]
pub struct AcceptLanguage {
parts: Vec<AcceptLanguagePart>,
}
impl AcceptLanguage {
pub fn iter(&self) -> impl Iterator<Item = &Locale> {
// This should stop when we hit the first None, aka the first *
self.parts.iter().map_while(|item| item.locale.as_ref())
}
}
/// Utility to trim ASCII whitespace from the start and end of a byte slice
const fn trim_bytes(mut bytes: &[u8]) -> &[u8] {
// Trim leading and trailing whitespace
while let [first, rest @ ..] = bytes {
if first.is_ascii_whitespace() {
bytes = rest;
} else {
break;
}
}
while let [rest @ .., last] = bytes {
if last.is_ascii_whitespace() {
bytes = rest;
} else {
break;
}
}
bytes
}
impl Header for AcceptLanguage {
fn name() -> &'static HeaderName {
&ACCEPT_LANGUAGE
}
fn decode<'i, I>(values: &mut I) -> Result<Self, Error>
where
Self: Sized,
I: Iterator<Item = &'i HeaderValue>,
{
let mut parts = Vec::new();
for value in values {
for part in value.as_bytes().split(|b| *b == b',') {
let mut it = part.split(|b| *b == b';');
let locale = it.next().ok_or(Error::invalid())?;
let locale = trim_bytes(locale);
let locale = match locale {
b"*" => None,
locale => {
let locale =
Locale::try_from_bytes(locale).map_err(|_e| Error::invalid())?;
Some(locale)
}
};
let quality = if let Some(quality) = it.next() {
let quality = trim_bytes(quality);
let quality = quality.strip_prefix(b"q=").ok_or(Error::invalid())?;
let quality = std::str::from_utf8(quality).map_err(|_e| Error::invalid())?;
let quality = quality.parse::<f64>().map_err(|_e| Error::invalid())?;
// Bound the quality between 0 and 1
let quality = quality.clamp(0_f64, 1_f64);
// Make sure the iterator is empty
if it.next().is_some() {
return Err(Error::invalid());
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
{
f64::round(quality * 1000_f64) as u16
}
} else {
1000
};
parts.push(AcceptLanguagePart { locale, quality });
}
}
parts.sort();
Ok(AcceptLanguage { parts })
}
fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {
let mut value = String::new();
let mut first = true;
for part in &self.parts {
if first {
first = false;
} else {
value.push_str(", ");
}
if let Some(locale) = &part.locale {
value.push_str(&locale.to_string());
} else {
value.push('*');
}
if part.quality != 1000 {
value.push_str(";q=");
value.push_str(&(f64::from(part.quality) / 1000_f64).to_string());
}
}
// We know this is safe because we only use ASCII characters
values.extend(Some(HeaderValue::from_str(&value).unwrap()));
}
}
#[cfg(test)]
mod tests {
use headers::HeaderMapExt;
use http::{HeaderMap, HeaderValue, header::ACCEPT_LANGUAGE};
use icu_locid::locale;
use super::*;
#[test]
fn test_decode() {
let headers = HeaderMap::from_iter([(
ACCEPT_LANGUAGE,
HeaderValue::from_str("fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5").unwrap(),
)]);
let accept_language: Option<AcceptLanguage> = headers.typed_get();
assert!(accept_language.is_some());
let accept_language = accept_language.unwrap();
assert_eq!(
accept_language,
AcceptLanguage {
parts: vec![
AcceptLanguagePart {
locale: Some(locale!("fr-CH")),
quality: 1000,
},
AcceptLanguagePart {
locale: Some(locale!("fr")),
quality: 900,
},
AcceptLanguagePart {
locale: Some(locale!("en")),
quality: 800,
},
AcceptLanguagePart {
locale: Some(locale!("de")),
quality: 700,
},
AcceptLanguagePart {
locale: None,
quality: 500,
},
]
}
);
}
#[test]
/// Test that we can decode a header with multiple values unordered, and
/// that the output is ordered by quality
fn test_decode_order() {
let headers = HeaderMap::from_iter([(
ACCEPT_LANGUAGE,
HeaderValue::from_str("*;q=0.5, fr-CH, en;q=0.8, fr;q=0.9, de;q=0.9").unwrap(),
)]);
let accept_language: Option<AcceptLanguage> = headers.typed_get();
assert!(accept_language.is_some());
let accept_language = accept_language.unwrap();
assert_eq!(
accept_language,
AcceptLanguage {
parts: vec![
AcceptLanguagePart {
locale: Some(locale!("fr-CH")),
quality: 1000,
},
AcceptLanguagePart {
locale: Some(locale!("fr")),
quality: 900,
},
AcceptLanguagePart {
locale: Some(locale!("de")),
quality: 900,
},
AcceptLanguagePart {
locale: Some(locale!("en")),
quality: 800,
},
AcceptLanguagePart {
locale: None,
quality: 500,
},
]
}
);
}
#[test]
fn test_encode() {
let accept_language = AcceptLanguage {
parts: vec![
AcceptLanguagePart {
locale: Some(locale!("fr-CH")),
quality: 1000,
},
AcceptLanguagePart {
locale: Some(locale!("fr")),
quality: 900,
},
AcceptLanguagePart {
locale: Some(locale!("de")),
quality: 900,
},
AcceptLanguagePart {
locale: Some(locale!("en")),
quality: 800,
},
AcceptLanguagePart {
locale: None,
quality: 500,
},
],
};
let mut headers = HeaderMap::new();
headers.typed_insert(accept_language);
let header = headers.get(ACCEPT_LANGUAGE).unwrap();
assert_eq!(
header.to_str().unwrap(),
"fr-CH, fr;q=0.9, de;q=0.9, en;q=0.8, *;q=0.5"
);
}
}
================================================
FILE: crates/axum-utils/src/lib.rs
================================================
// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
#![deny(clippy::future_not_send)]
#![allow(clippy::module_name_repetitions)]
pub mod client_authorization;
pub mod cookies;
pub mod csrf;
pub mod error_wrapper;
pub mod fancy_error;
pub mod jwt;
pub mod language_detection;
pub mod sentry;
pub mod session;
pub mod user_authorization;
pub use axum;
pub use self::{
error_wrapper::ErrorWrapper,
fancy_error::{GenericError, InternalError},
session::{SessionInfo, SessionInfoExt},
};
================================================
FILE: crates/axum-utils/src/sentry.rs
================================================
// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
use std::convert::Infallible;
use axum::response::{IntoResponseParts, ResponseParts};
use sentry::types::Uuid;
/// A wrapper to include a Sentry event ID in the response headers.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SentryEventID(Uuid);
impl SentryEventID {
/// Create a new Sentry event ID header for the last event on the hub.
pub fn for_last_event() -> Option<Self> {
sentry::last_event_id().map(Self)
}
}
impl From<Uuid> for SentryEventID {
fn from(uuid: Uuid) -> Self {
Self(uuid)
}
}
impl IntoResponseParts for SentryEventID {
type Error = Infallible;
fn into_response_parts(self, mut res: ResponseParts) -> Result<ResponseParts, Self::Error> {
res.headers_mut()
.insert("X-Sentry-Event-ID", self.0.to_string().parse().unwrap());
Ok(res)
}
}
/// Record an error. It will emit a tracing event with the error level if
/// matches the pattern, warning otherwise. It also returns the Sentry event ID
/// if the error was recorded.
#[macro_export]
macro_rules! record_error {
($error:expr, !) => {{
tracing::warn!(message = &$error as &dyn std::error::Error);
Option::<$crate::sentry::SentryEventID>::None
}};
($error:expr) => {{
tracing::error!(message = &$error as &dyn std::error::Error);
// With the `sentry-tracing` integration, Sentry should have
// captured an error, so let's extract the last event ID from the
// current hub
$crate::sentry::SentryEventID::for_last_event()
}};
($error:expr, $pattern:pat) => {
if let $pattern = $error {
record_error!($error)
} else {
record_error!($error, !)
}
};
}
================================================
FILE: crates/axum-utils/src/session.rs
================================================
// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
use mas_data_model::BrowserSession;
use mas_storage::RepositoryAccess;
use serde::{Deserialize, Serialize};
use ulid::Ulid;
use crate::cookies::CookieJar;
/// An encrypted cookie to save the session ID
#[derive(Serialize, Deserialize, Debug, Default, C
gitextract_fztawn0h/
├── .cargo/
│ └── config.toml
├── .codecov.yml
├── .config/
│ └── nextest.toml
├── .dockerignore
├── .editorconfig
├── .github/
│ ├── CODEOWNERS
│ ├── actions/
│ │ ├── build-frontend/
│ │ │ └── action.yml
│ │ └── build-policies/
│ │ └── action.yml
│ ├── dependabot.yml
│ ├── release.yml
│ ├── scripts/
│ │ ├── .gitignore
│ │ ├── cleanup-pr.cjs
│ │ ├── commit-and-tag.cjs
│ │ ├── create-release-branch.cjs
│ │ ├── create-version-tag.cjs
│ │ ├── merge-back.cjs
│ │ ├── package.json
│ │ ├── update-release-branch.cjs
│ │ └── update-unstable-tag.cjs
│ └── workflows/
│ ├── build.yaml
│ ├── ci.yaml
│ ├── coverage.yaml
│ ├── docs.yaml
│ ├── merge-back.yaml
│ ├── release-branch.yaml
│ ├── release-bump.yaml
│ ├── tag.yaml
│ ├── translations-download.yaml
│ └── translations-upload.yaml
├── .gitignore
├── .rustfmt.toml
├── CONTRIBUTING.md
├── Cargo.toml
├── Dockerfile
├── LICENSE
├── LICENSE-COMMERCIAL
├── README.md
├── biome.json
├── book.toml
├── clippy.toml
├── crates/
│ ├── axum-utils/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── client_authorization.rs
│ │ ├── cookies.rs
│ │ ├── csrf.rs
│ │ ├── error_wrapper.rs
│ │ ├── fancy_error.rs
│ │ ├── jwt.rs
│ │ ├── language_detection.rs
│ │ ├── lib.rs
│ │ ├── sentry.rs
│ │ ├── session.rs
│ │ └── user_authorization.rs
│ ├── cli/
│ │ ├── Cargo.toml
│ │ ├── build.rs
│ │ └── src/
│ │ ├── app_state.rs
│ │ ├── commands/
│ │ │ ├── config.rs
│ │ │ ├── database.rs
│ │ │ ├── debug.rs
│ │ │ ├── doctor.rs
│ │ │ ├── manage.rs
│ │ │ ├── mod.rs
│ │ │ ├── server.rs
│ │ │ ├── syn2mas.rs
│ │ │ ├── templates.rs
│ │ │ └── worker.rs
│ │ ├── lifecycle.rs
│ │ ├── main.rs
│ │ ├── server.rs
│ │ ├── sync.rs
│ │ ├── telemetry.rs
│ │ └── util.rs
│ ├── config/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── bin/
│ │ │ └── schema.rs
│ │ ├── lib.rs
│ │ ├── schema.rs
│ │ ├── sections/
│ │ │ ├── account.rs
│ │ │ ├── branding.rs
│ │ │ ├── captcha.rs
│ │ │ ├── clients.rs
│ │ │ ├── database.rs
│ │ │ ├── email.rs
│ │ │ ├── experimental.rs
│ │ │ ├── http.rs
│ │ │ ├── matrix.rs
│ │ │ ├── mod.rs
│ │ │ ├── oauth.rs
│ │ │ ├── passwords.rs
│ │ │ ├── policy.rs
│ │ │ ├── rate_limiting.rs
│ │ │ ├── secrets.rs
│ │ │ ├── telemetry.rs
│ │ │ ├── templates.rs
│ │ │ └── upstream_oauth2.rs
│ │ └── util.rs
│ ├── context/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── fmt.rs
│ │ ├── future.rs
│ │ ├── layer.rs
│ │ ├── lib.rs
│ │ └── service.rs
│ ├── data-model/
│ │ ├── Cargo.toml
│ │ ├── examples/
│ │ │ └── ua-parser.rs
│ │ └── src/
│ │ ├── clock.rs
│ │ ├── compat/
│ │ │ ├── device.rs
│ │ │ ├── mod.rs
│ │ │ ├── session.rs
│ │ │ └── sso_login.rs
│ │ ├── lib.rs
│ │ ├── oauth2/
│ │ │ ├── authorization_grant.rs
│ │ │ ├── client.rs
│ │ │ ├── device_code_grant.rs
│ │ │ ├── mod.rs
│ │ │ └── session.rs
│ │ ├── personal/
│ │ │ ├── mod.rs
│ │ │ └── session.rs
│ │ ├── policy_data.rs
│ │ ├── site_config.rs
│ │ ├── tokens.rs
│ │ ├── upstream_oauth2/
│ │ │ ├── link.rs
│ │ │ ├── mod.rs
│ │ │ ├── provider.rs
│ │ │ └── session.rs
│ │ ├── user_agent.rs
│ │ ├── users.rs
│ │ ├── utils.rs
│ │ └── version.rs
│ ├── email/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── mailer.rs
│ │ └── transport.rs
│ ├── handlers/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── activity_tracker/
│ │ │ ├── bound.rs
│ │ │ ├── mod.rs
│ │ │ └── worker.rs
│ │ ├── admin/
│ │ │ ├── call_context.rs
│ │ │ ├── mod.rs
│ │ │ ├── model.rs
│ │ │ ├── params.rs
│ │ │ ├── response.rs
│ │ │ ├── schema.rs
│ │ │ └── v1/
│ │ │ ├── compat_sessions/
│ │ │ │ ├── finish.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ └── mod.rs
│ │ │ ├── mod.rs
│ │ │ ├── oauth2_sessions/
│ │ │ │ ├── finish.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ └── mod.rs
│ │ │ ├── personal_sessions/
│ │ │ │ ├── add.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── regenerate.rs
│ │ │ │ └── revoke.rs
│ │ │ ├── policy_data/
│ │ │ │ ├── get.rs
│ │ │ │ ├── get_latest.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── set.rs
│ │ │ ├── site_config.rs
│ │ │ ├── upstream_oauth_links/
│ │ │ │ ├── add.rs
│ │ │ │ ├── delete.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ └── mod.rs
│ │ │ ├── upstream_oauth_providers/
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ └── mod.rs
│ │ │ ├── user_emails/
│ │ │ │ ├── add.rs
│ │ │ │ ├── delete.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ └── mod.rs
│ │ │ ├── user_registration_tokens/
│ │ │ │ ├── add.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── revoke.rs
│ │ │ │ ├── unrevoke.rs
│ │ │ │ └── update.rs
│ │ │ ├── user_sessions/
│ │ │ │ ├── finish.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ └── mod.rs
│ │ │ ├── users/
│ │ │ │ ├── add.rs
│ │ │ │ ├── by_username.rs
│ │ │ │ ├── deactivate.rs
│ │ │ │ ├── get.rs
│ │ │ │ ├── list.rs
│ │ │ │ ├── lock.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── reactivate.rs
│ │ │ │ ├── set_admin.rs
│ │ │ │ ├── set_password.rs
│ │ │ │ └── unlock.rs
│ │ │ └── version.rs
│ │ ├── bin/
│ │ │ ├── api-schema.rs
│ │ │ └── graphql-schema.rs
│ │ ├── captcha.rs
│ │ ├── cleanup_tests.rs
│ │ ├── compat/
│ │ │ ├── login.rs
│ │ │ ├── login_sso_complete.rs
│ │ │ ├── login_sso_redirect.rs
│ │ │ ├── logout.rs
│ │ │ ├── logout_all.rs
│ │ │ ├── mod.rs
│ │ │ ├── refresh.rs
│ │ │ └── tests.rs
│ │ ├── graphql/
│ │ │ ├── mod.rs
│ │ │ ├── model/
│ │ │ │ ├── browser_sessions.rs
│ │ │ │ ├── compat_sessions.rs
│ │ │ │ ├── cursor.rs
│ │ │ │ ├── matrix.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── node.rs
│ │ │ │ ├── oauth.rs
│ │ │ │ ├── site_config.rs
│ │ │ │ ├── upstream_oauth.rs
│ │ │ │ ├── users.rs
│ │ │ │ └── viewer/
│ │ │ │ ├── anonymous.rs
│ │ │ │ └── mod.rs
│ │ │ ├── mutations/
│ │ │ │ ├── browser_session.rs
│ │ │ │ ├── compat_session.rs
│ │ │ │ ├── matrix.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── oauth2_session.rs
│ │ │ │ ├── user.rs
│ │ │ │ └── user_email.rs
│ │ │ ├── query/
│ │ │ │ ├── mod.rs
│ │ │ │ ├── session.rs
│ │ │ │ ├── upstream_oauth.rs
│ │ │ │ ├── user.rs
│ │ │ │ └── viewer.rs
│ │ │ ├── state.rs
│ │ │ └── tests.rs
│ │ ├── health.rs
│ │ ├── lib.rs
│ │ ├── oauth2/
│ │ │ ├── authorization/
│ │ │ │ ├── callback.rs
│ │ │ │ ├── consent.rs
│ │ │ │ └── mod.rs
│ │ │ ├── device/
│ │ │ │ ├── authorize.rs
│ │ │ │ ├── consent.rs
│ │ │ │ ├── link.rs
│ │ │ │ └── mod.rs
│ │ │ ├── discovery.rs
│ │ │ ├── introspection.rs
│ │ │ ├── keys.rs
│ │ │ ├── mod.rs
│ │ │ ├── registration.rs
│ │ │ ├── revoke.rs
│ │ │ ├── token.rs
│ │ │ ├── userinfo.rs
│ │ │ └── webfinger.rs
│ │ ├── passwords.rs
│ │ ├── preferred_language.rs
│ │ ├── rate_limit.rs
│ │ ├── session.rs
│ │ ├── snapshots/
│ │ │ ├── mas_handlers__passwords__tests__hash_verify_and_upgrade-2.snap
│ │ │ ├── mas_handlers__passwords__tests__hash_verify_and_upgrade-3.snap
│ │ │ ├── mas_handlers__passwords__tests__hash_verify_and_upgrade.snap
│ │ │ ├── mas_handlers__passwords__tests__hashing_argon2id-2.snap
│ │ │ ├── mas_handlers__passwords__tests__hashing_argon2id.snap
│ │ │ ├── mas_handlers__passwords__tests__hashing_bcrypt-2.snap
│ │ │ ├── mas_handlers__passwords__tests__hashing_bcrypt.snap
│ │ │ ├── mas_handlers__passwords__tests__hashing_pbkdf2-2.snap
│ │ │ └── mas_handlers__passwords__tests__hashing_pbkdf2.snap
│ │ ├── test_utils.rs
│ │ ├── upstream_oauth2/
│ │ │ ├── authorize.rs
│ │ │ ├── backchannel_logout.rs
│ │ │ ├── cache.rs
│ │ │ ├── callback.rs
│ │ │ ├── cookie.rs
│ │ │ ├── link.rs
│ │ │ ├── mod.rs
│ │ │ └── template.rs
│ │ └── views/
│ │ ├── app.rs
│ │ ├── index.rs
│ │ ├── login.rs
│ │ ├── logout.rs
│ │ ├── mod.rs
│ │ ├── recovery/
│ │ │ ├── mod.rs
│ │ │ ├── progress.rs
│ │ │ └── start.rs
│ │ ├── register/
│ │ │ ├── cookie.rs
│ │ │ ├── mod.rs
│ │ │ ├── password.rs
│ │ │ └── steps/
│ │ │ ├── display_name.rs
│ │ │ ├── finish.rs
│ │ │ ├── mod.rs
│ │ │ ├── registration_token.rs
│ │ │ └── verify_email.rs
│ │ └── shared.rs
│ ├── http/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── ext.rs
│ │ ├── lib.rs
│ │ └── reqwest.rs
│ ├── i18n/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── lib.rs
│ │ │ ├── sprintf/
│ │ │ │ ├── argument.rs
│ │ │ │ ├── formatter.rs
│ │ │ │ ├── grammar.pest
│ │ │ │ ├── message.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── parser.rs
│ │ │ ├── translations.rs
│ │ │ └── translator.rs
│ │ └── test_data/
│ │ ├── en-US.json
│ │ ├── en.json
│ │ └── fr.json
│ ├── i18n-scan/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── key.rs
│ │ ├── main.rs
│ │ └── minijinja.rs
│ ├── iana/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── jose.rs
│ │ ├── lib.rs
│ │ └── oauth.rs
│ ├── iana-codegen/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── generation.rs
│ │ ├── jose.rs
│ │ ├── main.rs
│ │ ├── oauth.rs
│ │ └── traits.rs
│ ├── jose/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── base64.rs
│ │ │ ├── claims.rs
│ │ │ ├── constraints.rs
│ │ │ ├── jwa/
│ │ │ │ ├── asymmetric.rs
│ │ │ │ ├── hmac.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── signature.rs
│ │ │ │ └── symmetric.rs
│ │ │ ├── jwk/
│ │ │ │ ├── mod.rs
│ │ │ │ ├── private_parameters.rs
│ │ │ │ └── public_parameters.rs
│ │ │ ├── jwt/
│ │ │ │ ├── header.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── raw.rs
│ │ │ │ └── signed.rs
│ │ │ └── lib.rs
│ │ └── tests/
│ │ ├── generate.py
│ │ ├── jws.rs
│ │ ├── jwts/
│ │ │ ├── eddsa-ed25519.jwt
│ │ │ ├── eddsa-ed448.jwt
│ │ │ ├── es256.jwt
│ │ │ ├── es256k.jwt
│ │ │ ├── es384.jwt
│ │ │ ├── es512.jwt
│ │ │ ├── hs256.jwt
│ │ │ ├── hs384.jwt
│ │ │ ├── hs512.jwt
│ │ │ ├── ps256.jwt
│ │ │ ├── ps384.jwt
│ │ │ ├── ps512.jwt
│ │ │ ├── rs256.jwt
│ │ │ ├── rs384.jwt
│ │ │ └── rs512.jwt
│ │ ├── keys/
│ │ │ ├── ed25519.priv.pem
│ │ │ ├── ed25519.pub.pem
│ │ │ ├── ed448.priv.pem
│ │ │ ├── ed448.pub.pem
│ │ │ ├── jwks.priv.json
│ │ │ ├── jwks.pub.json
│ │ │ ├── k256.priv.pem
│ │ │ ├── k256.pub.pem
│ │ │ ├── p256.priv.pem
│ │ │ ├── p256.pub.pem
│ │ │ ├── p384.priv.pem
│ │ │ ├── p384.pub.pem
│ │ │ ├── p521.priv.pem
│ │ │ ├── p521.pub.pem
│ │ │ ├── rsa.priv.pem
│ │ │ └── rsa.pub.pem
│ │ └── snapshots/
│ │ ├── jws__es256__sign_jwt.snap
│ │ ├── jws__es256k__sign_jwt.snap
│ │ ├── jws__es384__sign_jwt.snap
│ │ ├── jws__ps256__sign_jwt.snap
│ │ ├── jws__ps384__sign_jwt.snap
│ │ ├── jws__ps512__sign_jwt.snap
│ │ ├── jws__rs256__sign_jwt.snap
│ │ ├── jws__rs384__sign_jwt.snap
│ │ └── jws__rs512__sign_jwt.snap
│ ├── keystore/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── encrypter.rs
│ │ │ └── lib.rs
│ │ └── tests/
│ │ ├── generate.sh
│ │ ├── keys/
│ │ │ ├── ec-k256.pkcs8.der
│ │ │ ├── ec-k256.pkcs8.encrypted.der
│ │ │ ├── ec-k256.pkcs8.encrypted.pem
│ │ │ ├── ec-k256.pkcs8.pem
│ │ │ ├── ec-k256.sec1.der
│ │ │ ├── ec-k256.sec1.pem
│ │ │ ├── ec-p256.pkcs8.der
│ │ │ ├── ec-p256.pkcs8.encrypted.der
│ │ │ ├── ec-p256.pkcs8.encrypted.pem
│ │ │ ├── ec-p256.pkcs8.pem
│ │ │ ├── ec-p256.sec1.der
│ │ │ ├── ec-p256.sec1.pem
│ │ │ ├── ec-p384.pkcs8.der
│ │ │ ├── ec-p384.pkcs8.encrypted.der
│ │ │ ├── ec-p384.pkcs8.encrypted.pem
│ │ │ ├── ec-p384.pkcs8.pem
│ │ │ ├── ec-p384.sec1.der
│ │ │ ├── ec-p384.sec1.pem
│ │ │ ├── ec256.pkcs8.encrypted.pem
│ │ │ ├── rsa.pkcs1.der
│ │ │ ├── rsa.pkcs1.pem
│ │ │ ├── rsa.pkcs8.der
│ │ │ ├── rsa.pkcs8.encrypted.der
│ │ │ ├── rsa.pkcs8.encrypted.pem
│ │ │ └── rsa.pkcs8.pem
│ │ ├── keystore.rs
│ │ └── snapshots/
│ │ ├── keystore__generate_sign_and_verify-2.snap
│ │ ├── keystore__generate_sign_and_verify-3.snap
│ │ ├── keystore__generate_sign_and_verify-4.snap
│ │ ├── keystore__generate_sign_and_verify-5.snap
│ │ ├── keystore__generate_sign_and_verify.snap
│ │ ├── keystore__jwt_ES256.snap
│ │ ├── keystore__jwt_ES256K.snap
│ │ ├── keystore__jwt_ES384.snap
│ │ ├── keystore__jwt_PS256.snap
│ │ ├── keystore__jwt_PS384.snap
│ │ ├── keystore__jwt_PS512.snap
│ │ ├── keystore__jwt_RS256.snap
│ │ ├── keystore__jwt_RS384.snap
│ │ └── keystore__jwt_RS512.snap
│ ├── listener/
│ │ ├── Cargo.toml
│ │ ├── examples/
│ │ │ └── demo/
│ │ │ ├── certs/
│ │ │ │ ├── ca-key.pem
│ │ │ │ ├── ca.csr
│ │ │ │ ├── ca.json
│ │ │ │ ├── ca.pem
│ │ │ │ ├── client-key.pem
│ │ │ │ ├── client.csr
│ │ │ │ ├── client.json
│ │ │ │ ├── client.pem
│ │ │ │ ├── config.json
│ │ │ │ ├── gen.sh
│ │ │ │ ├── server-key.pem
│ │ │ │ ├── server.csr
│ │ │ │ ├── server.json
│ │ │ │ └── server.pem
│ │ │ └── main.rs
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── maybe_tls.rs
│ │ ├── proxy_protocol/
│ │ │ ├── acceptor.rs
│ │ │ ├── maybe.rs
│ │ │ ├── mod.rs
│ │ │ └── v1.rs
│ │ ├── rewind.rs
│ │ ├── server.rs
│ │ └── unix_or_tcp.rs
│ ├── matrix/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── mock.rs
│ │ └── readonly.rs
│ ├── matrix-synapse/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── error.rs
│ │ ├── legacy.rs
│ │ ├── lib.rs
│ │ └── modern.rs
│ ├── oauth2-types/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── errors.rs
│ │ ├── lib.rs
│ │ ├── oidc.rs
│ │ ├── pkce.rs
│ │ ├── registration/
│ │ │ ├── client_metadata_serde.rs
│ │ │ └── mod.rs
│ │ ├── requests.rs
│ │ ├── response_type.rs
│ │ ├── scope.rs
│ │ ├── test_utils.rs
│ │ └── webfinger.rs
│ ├── oidc-client/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── error.rs
│ │ │ ├── lib.rs
│ │ │ ├── requests/
│ │ │ │ ├── authorization_code.rs
│ │ │ │ ├── client_credentials.rs
│ │ │ │ ├── discovery.rs
│ │ │ │ ├── jose.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── refresh_token.rs
│ │ │ │ ├── token.rs
│ │ │ │ └── userinfo.rs
│ │ │ └── types/
│ │ │ ├── client_credentials.rs
│ │ │ └── mod.rs
│ │ └── tests/
│ │ └── it/
│ │ ├── main.rs
│ │ ├── requests/
│ │ │ ├── authorization_code.rs
│ │ │ ├── client_credentials.rs
│ │ │ ├── discovery.rs
│ │ │ ├── jose.rs
│ │ │ ├── mod.rs
│ │ │ ├── refresh_token.rs
│ │ │ └── userinfo.rs
│ │ └── types/
│ │ ├── client_credentials.rs
│ │ └── mod.rs
│ ├── policy/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── bin/
│ │ │ └── schema.rs
│ │ ├── lib.rs
│ │ └── model.rs
│ ├── router/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── endpoints.rs
│ │ ├── lib.rs
│ │ ├── traits.rs
│ │ └── url_builder.rs
│ ├── spa/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ └── vite.rs
│ ├── storage/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── app_session.rs
│ │ ├── compat/
│ │ │ ├── access_token.rs
│ │ │ ├── mod.rs
│ │ │ ├── refresh_token.rs
│ │ │ ├── session.rs
│ │ │ └── sso_login.rs
│ │ ├── lib.rs
│ │ ├── oauth2/
│ │ │ ├── access_token.rs
│ │ │ ├── authorization_grant.rs
│ │ │ ├── client.rs
│ │ │ ├── device_code_grant.rs
│ │ │ ├── mod.rs
│ │ │ ├── refresh_token.rs
│ │ │ └── session.rs
│ │ ├── pagination.rs
│ │ ├── personal/
│ │ │ ├── access_token.rs
│ │ │ ├── mod.rs
│ │ │ └── session.rs
│ │ ├── policy_data.rs
│ │ ├── queue/
│ │ │ ├── job.rs
│ │ │ ├── mod.rs
│ │ │ ├── schedule.rs
│ │ │ ├── tasks.rs
│ │ │ └── worker.rs
│ │ ├── repository.rs
│ │ ├── upstream_oauth2/
│ │ │ ├── link.rs
│ │ │ ├── mod.rs
│ │ │ ├── provider.rs
│ │ │ └── session.rs
│ │ ├── user/
│ │ │ ├── email.rs
│ │ │ ├── mod.rs
│ │ │ ├── password.rs
│ │ │ ├── recovery.rs
│ │ │ ├── registration.rs
│ │ │ ├── registration_token.rs
│ │ │ ├── session.rs
│ │ │ └── terms.rs
│ │ └── utils.rs
│ ├── storage-pg/
│ │ ├── .sqlx/
│ │ │ ├── query-015f7ad7c8d5403ce4dfb71d598fd9af472689d5aef7c1c4b1c594ca57c02237.json
│ │ │ ├── query-037fae6964130343453ef607791c4c3deaa01b5aaa091d3a3487caf3e2634daf.json
│ │ │ ├── query-03eee34f05df9c79f8ca5bfb1af339b3fcea95ba59395106318366a6ef432d85.json
│ │ │ ├── query-047990a99794b565c2cad396946299db5b617f52f6c24bcca0a24c0c185c4478.json
│ │ │ ├── query-048eec775f4af3ffd805e830e8286c6a5745e523b76e1083d6bfced0035c2f76.json
│ │ │ ├── query-05b4dd39521eaf4e8e3c21654df67c00c8781f54054a84b3f3005b65cbc2a14a.json
│ │ │ ├── query-06d67595eeef23d5f2773632e0956577d98074e244a35c0d3be24bc18d9d0daa.json
│ │ │ ├── query-07cd2da428f0984513b4ce58e526c35c9c236ea8beb6696e5740fa45655e59f3.json
│ │ │ ├── query-093d42238578771b4183b48c1680ba438b6b18306dfe1454fa4124c0207b3deb.json
│ │ │ ├── query-0e1bce56e15751d82a622d532b279bfc50e22cb12ddf7495c7b0fedca61f9421.json
│ │ │ ├── query-0e45995714e60b71e0f0158500a63aa46225245a04d1c7bc24b5275c44a6d58d.json
│ │ │ ├── query-0f2ea548e00b080502edc04ee97ea304d43c336ce80723789ff3e66c0dd4d86c.json
│ │ │ ├── query-109f0c859e123966462f1001aef550e4e12d1778474aba72762d9aa093d21ee2.json
│ │ │ ├── query-12c4577701416a9dc23708c46700f3f086e4e62c6de9d6864a6a11a2470ebe62.json
│ │ │ ├── query-1764715e59f879f6b917ca30f8e3c1de5910c7a46e7fe52d1fb3bfd5561ac320.json
│ │ │ ├── query-188a4aeef5a8b4bf3230c7176ded64d52804848df378dc74f8f54ec4404e094e.json
│ │ │ ├── query-1919d402fd6f148d14417f633be3353004f458c85f7b4f361802f86651900fbc.json
│ │ │ ├── query-1b547552eed4128f2227c681ff2d45586cdb0c20b98393f89036fbf0f1d2dee2.json
│ │ │ ├── query-1dbc50cdab36da307c569891ab7b1ab4aaf128fed6be67ca0f139d697614c63b.json
│ │ │ ├── query-1eb829460407fca22b717b88a1a0a9b7b920d807a4b6c235e1bee524cd73b266.json
│ │ │ ├── query-21b9e39ffd89de288305765c339a991d2471667cf2981770447cde6fd025fbb7.json
│ │ │ ├── query-22896e8f2a002f307089c3e0f9ee561e6521c45ce07d3a42411984c9a6b75fdc.json
│ │ │ ├── query-23d5fcd8bf611dc7279bef0d66ce05461c3c1f43f966fee3a80ae42540783f08.json
│ │ │ ├── query-245cab1cf7d9cf4e94cdec91ecb4dc8e678278121efbe1f66bcdc24144d684d0.json
│ │ │ ├── query-2564bf6366eb59268c41fb25bb40d0e4e9e1fd1f9ea53b7a359c9025d7304223.json
│ │ │ ├── query-29148548d592046f7d711676911e3847e376e443ccd841f76b17a81f53fafc3a.json
│ │ │ ├── query-2a0d8d70d21afa9a2c9c1c432853361bb85911c48f7db6c3873b0f5abf35940b.json
│ │ │ ├── query-2a61003da3655158e6a261d91fdff670f1b4ba3c56605c53e2b905d7ec38c8be.json
│ │ │ ├── query-2d249684e0e4db0e3bc189f821521657559d9b77fd931f972ce4d9f03a57f97a.json
│ │ │ ├── query-2ee26886c56f04cd53d4c0968f5cf0963f92b6d15e6af0e69378a6447dee677c.json
│ │ │ ├── query-2f66991d7b9ba58f011d9aef0eb6a38f3b244c2f46444c0ab345de7feff54aba.json
│ │ │ ├── query-2f7aba76cd7df75d6a9a6d91d5ddebaedf37437f3bd4f796f5581fab997587d7.json
│ │ │ ├── query-2f8d402b7217aef47a5c45d4f7cfddbaeedcbbc6963ee573409bfc98e57de6ed.json
│ │ │ ├── query-31e8bf68ff70a436fd0b6787ac8e2777f9327708b450d048638a162343478cc6.json
│ │ │ ├── query-3312f901f70c3b69e0d315206c31ffe11da64835ae297c9277271b8971d5de81.json
│ │ │ ├── query-38d0608b7d8ba30927f939491c1d43cfd962c729298ad07ee1ade2f2880c0eb3.json
│ │ │ ├── query-38eb6b635d30ca78ff78b926b414cbd866cfc2918ca4b1741b5687f21cfe273b.json
│ │ │ ├── query-399e261027fe6c9167511636157ab747a469404533f59ff6fbd56e9eb5ad38e1.json
│ │ │ ├── query-3c7960a2eb2edd71bc71177fc0fb2e83858c9944893b8f3a0f0131e8a9b7a494.json
│ │ │ ├── query-3c7fc3e386ce51187f6344ad65e1d78a7f026e8311bdc7d5ccc2f39d962e898f.json
│ │ │ ├── query-3d66f3121b11ce923b9c60609b510a8ca899640e78cc8f5b03168622928ffe94.json
│ │ │ ├── query-3e6e3aad53b22fc53eb3ee881b29bb249b18ced57d6a4809dffc23972b3e9423.json
│ │ │ ├── query-3ed73cfce8ef6a1108f454e18b1668f64b76975dba07e67d04ed7a52e2e8107f.json
│ │ │ ├── query-3f9d76f442c82a1631da931950b83b80c9620e1825ab07ab6c52f3f1a32d2527.json
│ │ │ ├── query-432e199b0d47fe299d840c91159726c0a4f89f65b4dc3e33ddad58aabf6b148b.json
│ │ │ ├── query-446a8d7bd8532a751810401adfab924dc20785c91770ed43d62df2e590e8da71.json
│ │ │ ├── query-45d7e962d91fcdcf8284d81d04bc0737c0d20799b497089a566e2ff704d56b67.json
│ │ │ ├── query-494ca16f0f00f977a3031924a15318aa7346917e5c8a37bb0f5b2b3067588009.json
│ │ │ ├── query-4968c60adef69c7215a7efe2021baffb050b2f475ae106155c2e2f210a81191a.json
│ │ │ ├── query-4c2064fed8fa464ea3d2a1258fb0544dbf1493cad31a21c0cd7ddb57ed12de16.json
│ │ │ ├── query-4c37988dacca5a83c8b64209042d5f1a8ec44ec8ccccad2d7fce9ac855209883.json
│ │ │ ├── query-4d0386ad2fe47f1aded46917abe6141752ba90d36467693a68318573171d57b0.json
│ │ │ ├── query-4dad1838536c10ba723adc0fb6da0f24afb3d6a1925a80a1b6d35b9a8258a0ce.json
│ │ │ ├── query-4e64540bbffe5f4b9c4a6589012cf69eb67adaa4d40fc1910dfcd2640e32ab37.json
│ │ │ ├── query-5006c3e60c98c91a0b0fbb3205373e81d9b75e90929af80961f8b5910873a43e.json
│ │ │ ├── query-5133f9c5ba06201433be4ec784034d222975d084d0a9ebe7f1b6b865ab2e09ef.json
│ │ │ ├── query-535225206622b9190ccf42f7d66268818dc84c37b168ab45e582e0a727796a06.json
│ │ │ ├── query-53ad718642644b47a2d49f768d81bd993088526923769a9147281686c2d47591.json
│ │ │ ├── query-5402b8ddb674d05319830477eb3e72ecb536092b46c92a7dda01598962842323.json
│ │ │ ├── query-55bc51efddf7a1cf06610fdb20d46beca29964733338ea4fec2a29393f031c4f.json
│ │ │ ├── query-572ead41d62cfbe40e6f0c8edf6928e8eebd99036255b62d688ac02b5bd74b40.json
│ │ │ ├── query-5a6b91660e4c12b4a1fe2cad08e727a305cbe4029cd4cebd5ecc274e3e32f533.json
│ │ │ ├── query-5b21644dd3c094b0f2f8babb2c730554dc067d0a6cad963dd7e0c66a80b342bf.json
│ │ │ ├── query-5b697dd7834d33ec55972d3ba43d25fe794bc0b69c5938275711faa7a80b811f.json
│ │ │ ├── query-5d0d4699aa82b3976c6c1fcb0d77559da26def223b8954cf32959cce777577d7.json
│ │ │ ├── query-5da7a197e0008f100ad4daa78f4aa6515f0fc9eb54075e8d6d15520d25b75172.json
│ │ │ ├── query-5eea2f4c3e82ae606b09b8a81332594c97ba0afe972f0fee145b6094789fb6c7.json
│ │ │ ├── query-5f2199865fae3a969bb37429dd70dc74505b22c681322bd99b62c2a540c6cd35.json
│ │ │ ├── query-5fe1bb569d13a7d3ff22887b3fc5b76ff901c183b314f8ccb5018d70c516abf6.json
│ │ │ ├── query-607262ccf28b672df51e4e5d371e5cc5119a7d6e7fe784112703c0406f28300f.json
│ │ │ ├── query-608366f45ecaf392ab69cddb12252b5efcc103c3383fa68b552295e2289d1f55.json
│ │ │ ├── query-623097fc45ffa5d6e09fedfbdbe5e42662e9854430bcd9e53598debf99c9ca37.json
│ │ │ ├── query-64b6e274e2bed6814f5ae41ddf57093589f7d1b2b8458521b635546b8012041e.json
│ │ │ ├── query-6589987e88fa9dbbd2bd48acd910e08bab57721007c64ef2597cb09a62100792.json
│ │ │ ├── query-66693f31eff5673e88ca516ee727a709b06455e08b9fd75cc08f142070f330b3.json
│ │ │ ├── query-67cd4880d84b38f20c3960789934d55cbfb01492985ac2af5a1ad4af9b3ccc77.json
│ │ │ ├── query-6b8d28b76d7ab33178b46dbb28c11e41d86f22b3fa899a952cad00129e59bee6.json
│ │ │ ├── query-6bd38759f569fcf972924d12f565b531b9873f4139eadcbf1450e726b9a27379.json
│ │ │ ├── query-6d71188dffc492ddc8f7f21476516d3b08fd5d736ecf36845e6fd4bfc515b2cf.json
│ │ │ ├── query-6db23fc9c39c2c7d9224d4e1233205f636568c990ccb05cf9208750ad1330b9b.json
│ │ │ ├── query-6e21e7d816f806da9bb5176931bdb550dee05c44c9d93f53df95fe3b4a840347.json
│ │ │ ├── query-6ecad60e565367a6cfa539b4c32dabe674ea853e0d47eb5c713705cb0130c758.json
│ │ │ ├── query-6f97b5f9ad0d4d15387150bea3839fb7f81015f7ceef61ecaadba64521895cff.json
│ │ │ ├── query-707d78340069627aba9f18bbe5ac1388d6723f82549d88d704d9c939b9d35c49.json
│ │ │ ├── query-7189b6136fd08ac9ae7c51bff06fb2254d1bf9e8a97cd7d32ba789c740e0fbdb.json
│ │ │ ├── query-755f62d0a3a40acc90037371339a8459736fdd4bbffd932f7930d847f2c3ef5d.json
│ │ │ ├── query-75a62d170e4c959a14c5698f1da983113e7d1bc565d01e85c158856abb17ddc6.json
│ │ │ ├── query-77dfa9fae1a9c77b70476d7da19d3313a02886994cfff0690451229fb5ae2f77.json
│ │ │ ├── query-785e6bceed803cb1caccc373cde0c999d601f3a9730e6bbb40cfc43c04195c61.json
│ │ │ ├── query-7a0641df5058927c5cd67d4cdaa59fe609112afbabcbfcc0e7f96c1e531b6567.json
│ │ │ ├── query-7b06e6f21c69056b526538f06f06268efd13d7af3cecb452168d514a379fec30.json
│ │ │ ├── query-7ce387b1b0aaf10e72adde667b19521b66eaafa51f73bf2f95e38b8f3b64a229.json
│ │ │ ├── query-7e367e416d18fcf9b227bf053421410b4b7b4af441f0a138c5421d1111cb9f79.json
│ │ │ ├── query-7e414c29745cf5c85fa4e7cb5d661b07f43ab168956470d120166ed7eab631d9.json
│ │ │ ├── query-7f4c4634ada4dc2745530dcca8eee92abf78dfbdf1a25e58a2bc9c14be8035f0.json
│ │ │ ├── query-7f8335cc94347bc3a15afe7051658659347a1bf71dd62335df046708f19c967e.json
│ │ │ ├── query-8275a440640ea28fd8f82e7df672e45a6eba981a0d621665ed8f8b60354b3389.json
│ │ │ ├── query-83d1b0720dfde3209d77f1142aa19359913b8a934ca8a642b7bb43c9a7a58a6d.json
│ │ │ ├── query-860e01cd660b450439d63c5ee31ade59f478b0b096b4bc90c89fb9c26b467dd2.json
│ │ │ ├── query-875294dc5cf87bcf302fb9e87933745cc1c57bbe3c3c69110592a07400116c7f.json
│ │ │ ├── query-89041298e272d15c21e2b7127bd16c5a4f48e2be87dc26e9d0e3a932c9c49dfb.json
│ │ │ ├── query-89edaec8661e435c3b71bb9b995cd711eb78a4d39608e897432d6124cd135938.json
│ │ │ ├── query-8acbdc892d44efb53529da1c2df65bea6b799a43cf4c9264a37d392847e6eff0.json
│ │ │ ├── query-8afada5220fefb0d01ed6f87d3d0ee8fca86b5cdce9320e190e3d3b8fd9f63bc.json
│ │ │ ├── query-8d240d72d651f59d53bed7380710038e9d00492b1e282237c0ec0e03bc36a9c0.json
│ │ │ ├── query-8ef27901b96b73826a431ad6c5fabecc18c36d8cdba8db3b47953855fa5c9035.json
│ │ │ ├── query-8ef977487429f84c557dc62272c47e411b96b2376288a90c242034295e1a147e.json
│ │ │ ├── query-8f4f071f844281fb14ecd99db3261540441b14c8206038fdc4a4336bbae3f382.json
│ │ │ ├── query-8f5ce493e8b8473ba03d5263915a8b231f9e7c211ab83487536008e48316c269.json
│ │ │ ├── query-90875bdd2f75cdf0dc3f48dc2516f5c701411387c939f6b8a3478b41b3de4f20.json
│ │ │ ├── query-90fe32cb9c88a262a682c0db700fef7d69d6ce0be1f930d9f16c50b921a8b819.json
│ │ │ ├── query-91a3ee5ad64a947b7807a590f6b014c6856229918b972b98946f98b75686ab6c.json
│ │ │ ├── query-926cb81dc7931890a02c5a372aef79832e5d0748dad18ab44c6671f3196d6f60.json
│ │ │ ├── query-92c8eb526fcc5de6874eb0fab1d71fb1ed3dafe2bd1a49aa72e4f4862931c6c2.json
│ │ │ ├── query-933d2bed9c00eb9b37bfe757266ead15df5e0a4209ff47dcf4a5f19d35154e89.json
│ │ │ ├── query-966ca0f7eebd2896c007b2fd6e9327d03b29fe413d57cce21c67b6d539f59e7d.json
│ │ │ ├── query-98a5491eb5f10997ac1f3718c835903ac99d9bb8ca4d79c908b25a6d1209b9b1.json
│ │ │ ├── query-99394fbd9c07d6d24429934b3f7344dfab024b42e47ddc7bd9e551897ba6e9b8.json
│ │ │ ├── query-9b7363000017fa3dee46441bc0679cb16f9f8df08fa258cc907007fb9bcd0bc7.json
│ │ │ ├── query-9c9c65d4ca6847761d8f999253590082672b3782875cf3f5ba0b2f9d26e3a507.json
│ │ │ ├── query-9e8152d445f9996b221ad3690ba982ad01035296bf4539ca5620a043924a7292.json
│ │ │ ├── query-9eaf35f045aaca8473efc4a1f529afe24f01d9ec34609f373db5c535ccb58516.json
│ │ │ ├── query-9f7bdc034c618e47e49c467d0d7f5b8c297d055abe248cc876dbc12c5a7dc920.json
│ │ │ ├── query-9fe87eeaf4b7d0ba09b59ddad3476eb57ccb6e4053ab8f4450dd4a9d1f6ba108.json
│ │ │ ├── query-a0be6c56e470382b9470df414497e260ba8911123744980e24a52bc9b95bd056.json
│ │ │ ├── query-a2f7433f06fb4f6a7ad5ac6c1db18705276bce41e9b19d5d7e910ad4b767fb5e.json
│ │ │ ├── query-a50eb326c3522f971f6ee7e13dff61efbeb1ec24e2c694e1673347bae993762d.json
│ │ │ ├── query-a63a217981b97448ddcc96b2489ddd9d3bc8c99b5b8b1d373939fc3ae9715c27.json
│ │ │ ├── query-a7094d84d313602729fde155cfbe63041fca7cbab407f98452462ec45e3cfd16.json
│ │ │ ├── query-a75a6a08c9639053cfc3cffa9d4a009785f358b334f5c586c2e358f0d0b4d856.json
│ │ │ ├── query-a7f780528882a2ae66c45435215763eed0582264861436eab3f862e3eb12cab1.json
│ │ │ ├── query-ab34912b42a48a8b5c8d63e271b99b7d0b690a2471873c6654b1b6cf2079b95c.json
│ │ │ ├── query-ae6bf8958c4d9837d63f56574e91f91acc6076a8521adc3e30a83bf70e2121a0.json
│ │ │ ├── query-afa86e79e3de2a83265cb0db8549d378a2f11b2a27bbd86d60558318c87eb698.json
│ │ │ ├── query-b3568613352efae1125a88565d886157d96866f7ef9b09b03a45ba4322664bd0.json
│ │ │ ├── query-b60d34f4d250c12f75dba10491c1337d69aebad12be6fbfbdde91e34083ba4ed.json
│ │ │ ├── query-b6c4f4a23968cba2a82c2b7cfffc05a7ed582c9e5c1f65d27b0686f843ccfe42.json
│ │ │ ├── query-b700dc3f7d0f86f4904725d8357e34b7e457f857ed37c467c314142877fd5367.json
│ │ │ ├── query-b74e4d620bed4832a4e8e713a346691f260a7eca4bf494d6fb11c7cf699adaad.json
│ │ │ ├── query-b91cc2458e1a530e7cadbd1ca3e2eaf93e1c44108b6770a24c9a24ac29db37d3.json
│ │ │ ├── query-b992283a9b43cbb8f86149f3f55cb47fb628dabd8fadc50e6a5772903f851e1c.json
│ │ │ ├── query-bb0f782756c274c06c1b63af6fc3ac2a7cedfd4247b57f062d348b4b1b36bef1.json
│ │ │ ├── query-bb141d28c0c82244f31d542038c314d05ceb3a7b8f35397c0faef3b36d2d14a7.json
│ │ │ ├── query-bbf62633c561706a762089bbab2f76a9ba3e2ed3539ef16accb601fb609c2ec9.json
│ │ │ ├── query-c09e0bb0378d9dfb15de7f2f1209fab6ea87589819128e6fc9ed5da11dfc2770.json
│ │ │ ├── query-c29fa41743811a6ac3a9b952b6ea75d18e914f823902587b63c9f295407144b1.json
│ │ │ ├── query-c5e7dbb22488aca427b85b3415bd1f1a1766ff865f2e08a5daa095d2a1ccbd56.json
│ │ │ ├── query-c960f4f5571ee68816c49898125979f3c78c2caca52cb4b8dc9880e669a1f23e.json
│ │ │ ├── query-c984ae0496d0bd7520ee3d6761ce6a4f61a6a2001b597e4c63ba4588ec5cf530.json
│ │ │ ├── query-ca093cab5143bb3dded2eda9e82473215f4d3c549ea2c5a4f860a102cc46a667.json
│ │ │ ├── query-cc60ad934d347fb4546205d1fe07e9d2f127cb15b1bb650d1ea3805a4c55b196.json
│ │ │ ├── query-ce36eb8d3e4478a4e8520919ff41f1a5e6470cef581b1638f5578546dd28c4df.json
│ │ │ ├── query-cf2eeca6d8dbc2cc72160a26e81f6e963096edb610183ba13cbbbd3d95c4134b.json
│ │ │ ├── query-cf654533cfed946e9ac52dbcea1f50be3dfdac0fbfb1e8a0204c0c9c103ba5b0.json
│ │ │ ├── query-d02248136aa6b27636814dee4e0bc38395ab6c6fdf979616fa16fc490897cee3.json
│ │ │ ├── query-d0355d4e98bec6120f17d8cf81ac8c30ed19e9cebd0c8e7c7918b1c3ca0e3cba.json
│ │ │ ├── query-d26e42d9fd2b2ee3cf9702c1666d83e7cffa26b320ae1442c7f3e22376c4a4ee.json
│ │ │ ├── query-d4bc51c30f1119ea9d117fb565ec554d63c8773040679a77e99ac3fa24cec71d.json
│ │ │ ├── query-d7a0e4fa2f168976505405c7e7800847f3379f7b57c0972659a35bfb68b0f6cd.json
│ │ │ ├── query-d8f0b02952e786dd4309eac9de04a359aea3a46e5d4e07764cec56ce5d6609c0.json
│ │ │ ├── query-d95cd1b4bcfa1d7bb236d49e1956fcc9a684609956972fe4f95aac13f30b2530.json
│ │ │ ├── query-da02f93d7346992a9795f12b900f91ac0b326dd751c0d374d6ef4d19f671d22e.json
│ │ │ ├── query-dbf4be84eeff9ea51b00185faae2d453ab449017ed492bf6711dc7fceb630880.json
│ │ │ ├── query-dca9b361c4409b14498b85f192b0034201575a49e0240ac6715b55ad8d381d0e.json
│ │ │ ├── query-dd02cc4a48123c28b34da8501060096c33df9e30611ef89d01bf0502119cbbe1.json
│ │ │ ├── query-dda97742d389ffeeaab33d352d05767e2150f7da3cf384a7f44741c769f44144.json
│ │ │ ├── query-e02ea83d195cb58fa8525e66a6ac1dddae3f1dfb1ef48494f6aee3fd03abe6f6.json
│ │ │ ├── query-e1746b33c2f0d10f26332195f78e1ef2f192ca66f8000d1385626154e5ce4f7e.json
│ │ │ ├── query-e291be0434ab9c346dee777e50f8e601f12c8003fe93a5ecb110d02642d14c3c.json
│ │ │ ├── query-e35d56de7136d43d0803ec825b0612e4185cef838f105d66f18cb24865e45140.json
│ │ │ ├── query-e62d043f86e7232e6e9433631f8273e7ed0770c81071cf1f17516d3a45881ae9.json
│ │ │ ├── query-e68a7084d44462d19f30902d7e6c1bd60bb771c6f075df15ab0137a7ffc896da.json
│ │ │ ├── query-e8e48db74ac1ab5baa1e4b121643cfa33a0bf3328df6e869464fe7f31429b81e.json
│ │ │ ├── query-e99ab37ab3e03ad9c48792772b09bac77b09f67e623d5371ab4dadbe2d41fa1c.json
│ │ │ ├── query-eb095f64bec5ac885683a8c6708320760971317c4519fae7af9d44e2be50985d.json
│ │ │ ├── query-f0b4af5a9d6f1cc707a935fd5f34526a54ebbed8eef8f885f3a6723bc8490908.json
│ │ │ ├── query-f41f76c94cd68fca2285b1cc60f426603c84df4ef1c6ce5dc441a63d2dc46f6e.json
│ │ │ ├── query-f46e87bbb149b35e1d13b2b3cd2bdeab3c28a56a395f52f001a7bb013a5dfece.json
│ │ │ ├── query-f50b7fb5a2c09e7b7e89e2addb0ca42c790c101a3fc9442862b5885d5116325a.json
│ │ │ ├── query-f5c2ec9b7038d7ed36091e670f9bf34f8aa9ea8ed50929731845e32dc3176e39.json
│ │ │ ├── query-f8182fd162ffb018d4f102fa7ddbc9991135065e81af8f77b5beef9405607577.json
│ │ │ ├── query-fbf926f630df5d588df4f1c9c0dc0f594332be5829d5d7c6b66183ac25b3d166.json
│ │ │ ├── query-fc9925e19000d79c0bb020ea44e13cbb364b3505626d34550e38f6f7397b9d42.json
│ │ │ ├── query-fca331753aeccddbad96d06fc9d066dcefebe978a7af477bb6b55faa1d31e9b1.json
│ │ │ ├── query-fcd8b4b9e003d1540357c6bf1ff9c715560d011d4c01112703a9c046170c84f1.json
│ │ │ ├── query-fd32368fa6cd16a9704cdea54f7729681d450669563dd1178c492ffce51e5ff2.json
│ │ │ ├── query-fd8f3e7ff02d4d1f465aad32edcb06a842cabc787279ba7d690f69b59ad3eb50.json
│ │ │ ├── query-fe7bd146523e4bb321cb234d6bf9f3005b55c654897a8e46dc933c7fd2263c7c.json
│ │ │ └── query-ffbfef8b7e72ec4bae02b6bbe862980b5fe575ae8432a000e9c4e4307caa2d9b.json
│ │ ├── Cargo.toml
│ │ ├── build.rs
│ │ ├── migrations/
│ │ │ ├── 20220530084123_jobs_workers.sql
│ │ │ ├── 20221018142001_init.sql
│ │ │ ├── 20221121151402_upstream_oauth.sql
│ │ │ ├── 20221213145242_password_schemes.sql
│ │ │ ├── 20230408234928_add_get_jobs_fn_.sql
│ │ │ ├── 20230616093555_compat_admin_flag.sql
│ │ │ ├── 20230621140528_upstream_oauth_claims_imports.sql
│ │ │ ├── 20230626130338_oauth_clients_static.sql
│ │ │ ├── 20230728154304_user_lock.sql
│ │ │ ├── 20230823125247_drop_apalis_push_job.sql
│ │ │ ├── 20230828085439_oauth2_clients_more_fields.sql
│ │ │ ├── 20230828143553_user_session_authentication_source.sql
│ │ │ ├── 20230829092920_oauth2_sessions_user_id_scope_list.sql
│ │ │ ├── 20230829141928_user_session_user_agent.sql
│ │ │ ├── 20230904135550_oauth2_client_credentials_grant.sql
│ │ │ ├── 20230911091636_oauth2_token_expiration.sql
│ │ │ ├── 20230919155444_record_session_last_activity.sql
│ │ │ ├── 20231009142904_user_can_request_admin.sql
│ │ │ ├── 20231116104353_upstream_oauth_overrides.sql
│ │ │ ├── 20231120110559_upstream_oauth_branding.sql
│ │ │ ├── 20231207090532_oauth_device_code_grant.sql
│ │ │ ├── 20231208155602_oauth_clients_device_code_grant.sql
│ │ │ ├── 20240207100003_user_terms.sql
│ │ │ ├── 20240220141353_nonunique_compat_device_id.sql
│ │ │ ├── 20240220150201_compat_sessions_user_sessions_link.sql
│ │ │ ├── 20240221164945_sessions_user_agent.sql
│ │ │ ├── 20240301091201_upstream_oauth_additional_parameters.sql
│ │ │ ├── 20240402084854_upstream_oauth_disabled_at.sql
│ │ │ ├── 20240621080509_user_recovery.sql
│ │ │ ├── 20240718075125_sessions_active_index.sql
│ │ │ ├── 20241004075132_queue_worker.sql
│ │ │ ├── 20241004121132_queue_job.sql
│ │ │ ├── 20241007160050_oidc_login_hint.sql
│ │ │ ├── 20241115163340_upstream_oauth2_response_mode.sql
│ │ │ ├── 20241118115314_upstream_oauth2_extra_query_params.sql
│ │ │ ├── 20241120163320_queue_job_failures.sql
│ │ │ ├── 20241122130349_queue_job_scheduled.sql
│ │ │ ├── 20241122133435_queue_job_scheduled_index.sql
│ │ │ ├── 20241124145741_upstream_oauth_userinfo.sql
│ │ │ ├── 20241125110803_queue_job_recurrent.sql
│ │ │ ├── 20241129091057_upstream_oauth2_link_account_name.sql
│ │ │ ├── 20241202123523_upstream_oauth_responses_alg.sql
│ │ │ ├── 20241210115428_oauth_refresh_token_track_next.sql
│ │ │ ├── 20241210133651_oauth2_access_token_first_used.sql
│ │ │ ├── 20241212154426_oauth2_response_mode_null.sql
│ │ │ ├── 20241213180524_upstream_oauth_optional_issuer.sql
│ │ │ ├── 20250109105709_user_email_authentication_codes.sql
│ │ │ ├── 20250113102144_user_registrations.sql
│ │ │ ├── 20250114135939_allow_deviceless_compat_sessions.sql
│ │ │ ├── 20250115155255_cleanup_unverified_emails.sql
│ │ │ ├── 20250124151529_unsupported_threepids_table.sql
│ │ │ ├── 20250129154003_compat_sessions_device_name.sql
│ │ │ ├── 20250130170011_user_is_guest.sql
│ │ │ ├── 20250225091000_dynamic_policy_data.sql
│ │ │ ├── 20250311093145_user_deactivated_at.sql
│ │ │ ├── 20250312094013_upstream_oauth2_providers_order.sql
│ │ │ ├── 20250317151803_upstream_oauth_session_unlinked_at.sql
│ │ │ ├── 20250325102310_oauth2_clients_hash.sql
│ │ │ ├── 20250404105103_compat_sso_login_browser_session.sql
│ │ │ ├── 20250410000000_idx_compat_access_tokens_session_fk.sql
│ │ │ ├── 20250410000001_idx_compat_refresh_tokens_session_fk.sql
│ │ │ ├── 20250410000002_idx_compat_refresh_tokens_access_token_fk.sql
│ │ │ ├── 20250410000003_idx_compat_sessions_user_fk.sql
│ │ │ ├── 20250410000004_idx_compat_sessions_user_session_fk.sql
│ │ │ ├── 20250410000005_drop_compat_sessions_user_id_last_active_at.sql
│ │ │ ├── 20250410000006_idx_compat_sso_logins_session_fk.sql
│ │ │ ├── 20250410000007_idx_oauth2_access_tokens_session_fk.sql
│ │ │ ├── 20250410000008_idx_oauth2_authorization_grants_session_fk.sql
│ │ │ ├── 20250410000009_idx_oauth2_authorization_grants_client_fk.sql
│ │ │ ├── 20250410000010_idx_oauth2_consents_client_fk.sql
│ │ │ ├── 20250410000011_idx_oauth2_consents_user_fk.sql
│ │ │ ├── 20250410000012_idx_oauth2_device_code_grants_client_fk.sql
│ │ │ ├── 20250410000013_idx_oauth2_device_code_grants_session_fk.sql
│ │ │ ├── 20250410000014_idx_oauth2_device_code_grants_user_session_fk.sql
│ │ │ ├── 20250410000015_idx_oauth2_refresh_tokens_session_fk.sql
│ │ │ ├── 20250410000016_idx_oauth2_refresh_tokens_access_token_fk.sql
│ │ │ ├── 20250410000017_idx_oauth2_refresh_tokens_next_refresh_token_fk.sql
│ │ │ ├── 20250410000018_idx_oauth2_sessions_user_session_fk.sql
│ │ │ ├── 20250410000019_idx_oauth2_sessions_client_fk.sql
│ │ │ ├── 20250410000020_idx_oauth2_sessions_user_fk.sql
│ │ │ ├── 20250410000021_drop_oauth2_sessions_user_id_last_active_at.sql
│ │ │ ├── 20250410000022_idx_queue_jobs_started_by_fk.sql
│ │ │ ├── 20250410000023_idx_queue_jobs_next_attempt_fk.sql
│ │ │ ├── 20250410000024_idx_queue_jobs_schedule_name_fk.sql
│ │ │ ├── 20250410000025_idx_upstream_oauth_authorization_sessions_provider_fk.sql
│ │ │ ├── 20250410000026_idx_upstream_oauth_authorization_sessions_link_fk.sql
│ │ │ ├── 20250410000027_idx_upstream_oauth_links_provider_fk.sql
│ │ │ ├── 20250410000028_idx_upstream_oauth_links_user_fk.sql
│ │ │ ├── 20250410000029_idx_user_email_authentication_codes_authentication_fk.sql
│ │ │ ├── 20250410000030_idx_user_email_authentications_user_session_fk.sql
│ │ │ ├── 20250410000031_idx_user_email_authentications_user_registration_fk.sql
│ │ │ ├── 20250410000032_idx_user_emails_user_fk.sql
│ │ │ ├── 20250410000033_idx_user_emails_email_idx.sql
│ │ │ ├── 20250410000034_idx_user_passwords_user_fk.sql
│ │ │ ├── 20250410000035_idx_user_recovery_tickets_session_fk.sql
│ │ │ ├── 20250410000036_idx_user_recovery_tickets_user_email_fk.sql
│ │ │ ├── 20250410000037_idx_user_registrations_email_authentication_fk.sql
│ │ │ ├── 20250410000038_idx_user_session_authentications_user_session_fk.sql
│ │ │ ├── 20250410000039_idx_user_session_authentications_user_password_fk.sql
│ │ │ ├── 20250410000040_idx_user_session_authentications_upstream_oauth_session_fk.sql
│ │ │ ├── 20250410000041_idx_user_sessions_user_fk.sql
│ │ │ ├── 20250410000042_drop_user_sessions_user_id_last_active_at.sql
│ │ │ ├── 20250410000043_idx_user_terms_user_fk.sql
│ │ │ ├── 20250410000044_idx_users_primary_email_fk.sql
│ │ │ ├── 20250410000045_idx_user_recovery_tickets_ticket_idx.sql
│ │ │ ├── 20250410121612_users_lower_username_idx.sql
│ │ │ ├── 20250410174306_oauth2_authorization_default_requires_consent.sql
│ │ │ ├── 20250424150930_oauth2_grants_locale.sql
│ │ │ ├── 20250425113717_oauth2_session_human_name.sql
│ │ │ ├── 20250506161158_upstream_oauth2_forward_login_hint.sql
│ │ │ ├── 20250507131948_upstream_oauth_session_optional_nonce.sql
│ │ │ ├── 20250602212100_user_registration_tokens.sql
│ │ │ ├── 20250602212101_idx_user_registration_token.sql
│ │ │ ├── 20250602212102_upstream_oauth2_id_token_claims.sql
│ │ │ ├── 20250602212103_upstream_oauth2_id_token_claims_sub_sid_index.sql
│ │ │ ├── 20250602212104_upstream_oauth2_id_token_claims_sid_sub_index.sql
│ │ │ ├── 20250630120643_upstream_oauth_on_backchannel_logout.sql
│ │ │ ├── 20250708155857_idx_user_emails_lower_email.sql
│ │ │ ├── 20250709142230_id_token_claims_trigger.sql
│ │ │ ├── 20250709142240_backfill_id_token_claims.sql
│ │ │ ├── 20250915092000_pgtrgm_extension.sql
│ │ │ ├── 20250915092635_users_username_trgm_idx.sql
│ │ │ ├── 20250924132713_personal_access_tokens.sql
│ │ │ ├── 20251023134634_personal_access_tokens_unique_fix.sql
│ │ │ ├── 20251121145458_user_registration_upstream_oauth_session.sql
│ │ │ ├── 20251127145951_user_registration_upstream_oauth_session_idx.sql
│ │ │ ├── 20260108111542_remove_apalis.sql
│ │ │ ├── 20260108120030_remove_user_emails_old_confirmation.sql
│ │ │ ├── 20260108121127_cleanup_oauth2_consents.sql
│ │ │ ├── 20260108121952_cleanup_id_token_claims_trigger.sql
│ │ │ ├── 20260108144040_remove_deactivated_unsupported_threepids.sql
│ │ │ ├── 20260108145240_drop_oauth2_consents.sql
│ │ │ ├── 20260108175627_oauth_access_tokens_revoked_at_idx.sql
│ │ │ ├── 20260109115009_oauth_access_tokens_expires_at_idx.sql
│ │ │ ├── 20260109172537_oauth_refresh_token_revoked_at.sql
│ │ │ ├── 20260109172950_oauth_refresh_token_next_token_set_null.sql
│ │ │ ├── 20260109172954_oauth_refresh_token_next_token_set_null_validate.sql
│ │ │ ├── 20260112094550_oauth_refresh_token_not_consumed_idx.sql
│ │ │ ├── 20260112094837_oauth_refresh_token_consumed_at_idx.sql
│ │ │ ├── 20260115111313_idx_compat_sessions_finished_at.sql
│ │ │ ├── 20260116000002_idx_upstream_oauth_links_orphaned.sql
│ │ │ ├── 20260116000003_queue_jobs_next_attempt_set_null.sql
│ │ │ ├── 20260116000004_queue_jobs_next_attempt_set_null_validate.sql
│ │ │ ├── 20260121103025_upstream_oauth_track_user_session.sql
│ │ │ ├── 20260121104214_upstream_auth_user_session_fk_idx.sql
│ │ │ ├── 20260121112201_upstream_oauth_sessions_orphan_index.sql
│ │ │ ├── 20260121121140_upstream_oauth_track_user_session_trigger.sql
│ │ │ ├── 20260121121150_upstream_oauth_track_user_session_backfill.sql
│ │ │ ├── 20260122113523_compat_sessions_user_session_no_action.sql
│ │ │ ├── 20260122114353_compat_sessions_user_session_validate.sql
│ │ │ ├── 20260122123211_idx_oauth2_sessions_finished_at.sql
│ │ │ ├── 20260122124231_idx_user_sessions_finished_at.sql
│ │ │ ├── 20260123090000_idx_oauth2_sessions_inactive_ips.sql
│ │ │ ├── 20260123090001_idx_compat_sessions_inactive_ips.sql
│ │ │ ├── 20260123090002_idx_user_sessions_inactive_ips.sql
│ │ │ └── 20260324000001_queue_schedules_last_job_set_null.sql
│ │ └── src/
│ │ ├── app_session.rs
│ │ ├── compat/
│ │ │ ├── access_token.rs
│ │ │ ├── mod.rs
│ │ │ ├── refresh_token.rs
│ │ │ ├── session.rs
│ │ │ └── sso_login.rs
│ │ ├── errors.rs
│ │ ├── filter.rs
│ │ ├── iden.rs
│ │ ├── lib.rs
│ │ ├── oauth2/
│ │ │ ├── access_token.rs
│ │ │ ├── authorization_grant.rs
│ │ │ ├── client.rs
│ │ │ ├── device_code_grant.rs
│ │ │ ├── mod.rs
│ │ │ ├── refresh_token.rs
│ │ │ └── session.rs
│ │ ├── pagination.rs
│ │ ├── personal/
│ │ │ ├── access_token.rs
│ │ │ ├── mod.rs
│ │ │ └── session.rs
│ │ ├── policy_data.rs
│ │ ├── queue/
│ │ │ ├── job.rs
│ │ │ ├── mod.rs
│ │ │ ├── schedule.rs
│ │ │ └── worker.rs
│ │ ├── repository.rs
│ │ ├── telemetry.rs
│ │ ├── tracing.rs
│ │ ├── upstream_oauth2/
│ │ │ ├── link.rs
│ │ │ ├── mod.rs
│ │ │ ├── provider.rs
│ │ │ └── session.rs
│ │ └── user/
│ │ ├── email.rs
│ │ ├── mod.rs
│ │ ├── password.rs
│ │ ├── recovery.rs
│ │ ├── registration.rs
│ │ ├── registration_token.rs
│ │ ├── session.rs
│ │ ├── terms.rs
│ │ └── tests.rs
│ ├── syn2mas/
│ │ ├── .sqlx/
│ │ │ ├── query-026adeffc646b41ebc096bb874d110039b9a4a0425fd566e401f56ea215de0dd.json
│ │ │ ├── query-07ec66733b67a9990cc9d483b564c8d05c577cf8f049d8822746c7d1dbd23752.json
│ │ │ ├── query-09db58b250c20ab9d1701653165233e5c9aabfdae1f0ee9b77c909b2bb2f3e25.json
│ │ │ ├── query-12112011318abc0bdd7f722ed8c5d4a86bf5758f8c32d9d41a22999b2f0698ca.json
│ │ │ ├── query-1d1004d0fb5939fbf30c1986b80b986b1b4864a778525d0b8b0ad6678aef3e9f.json
│ │ │ ├── query-204cf4811150a7fdeafa9373647a9cd62ac3c9e58155882858c6056e2ef6c30d.json
│ │ │ ├── query-207b880ec2dd484ad05a7138ba485277958b66e4534561686c073e282fafaf2a.json
│ │ │ ├── query-24f6ce6280dc6675ab1ebdde0c5e3db8ff7a686180d71052911879f186ed1c8e.json
│ │ │ ├── query-486f3177dcf6117c6b966954a44d9f96a754eba64912566e81a90bd4cbd186f0.json
│ │ │ ├── query-5b4840f42ae00c5dc9f59f2745d664b16ebd813dfa0aa32a6d39dd5c393af299.json
│ │ │ ├── query-69aa96208513c3ea64a446c7739747fcb5e79d7e8c1212b2a679c3bde908ce93.json
│ │ │ ├── query-78ed3bf1032cd678b42230d68fb2b8e3d74161c8b6c5fe1a746b6958ccd2fd84.json
│ │ │ ├── query-86b2b02fbb6350100d794e4d0fa3c67bf00fd3e411f769b9f25dec27428489ed.json
│ │ │ ├── query-979bedd942b4f71c58f3672f2917cee05ac1a628e51fe61ba6dfed253e0c63c2.json
│ │ │ ├── query-b27828d7510d52456b50b4c4b9712878ee329ca72070d849eb61ac9c8f9d1c76.json
│ │ │ └── query-ebf68b70b3e22a04b57b5587b4b099255155193dafbbd185cd8f26d93ff423a7.json
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── lib.rs
│ │ │ ├── mas_writer/
│ │ │ │ ├── checks.rs
│ │ │ │ ├── constraint_pausing.rs
│ │ │ │ ├── fixtures/
│ │ │ │ │ └── upstream_provider.sql
│ │ │ │ ├── locking.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── snapshots/
│ │ │ │ │ ├── syn2mas__mas_writer__test__write_user.snap
│ │ │ │ │ ├── syn2mas__mas_writer__test__write_user_with_access_token.snap
│ │ │ │ │ ├── syn2mas__mas_writer__test__write_user_with_device.snap
│ │ │ │ │ ├── syn2mas__mas_writer__test__write_user_with_email.snap
│ │ │ │ │ ├── syn2mas__mas_writer__test__write_user_with_password.snap
│ │ │ │ │ ├── syn2mas__mas_writer__test__write_user_with_refresh_token.snap
│ │ │ │ │ ├── syn2mas__mas_writer__test__write_user_with_unsupported_threepid.snap
│ │ │ │ │ └── syn2mas__mas_writer__test__write_user_with_upstream_provider_link.snap
│ │ │ │ ├── syn2mas_revert_temporary_tables.sql
│ │ │ │ └── syn2mas_temporary_tables.sql
│ │ │ ├── migration.rs
│ │ │ ├── progress.rs
│ │ │ ├── synapse_reader/
│ │ │ │ ├── checks.rs
│ │ │ │ ├── config/
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ └── oidc.rs
│ │ │ │ ├── fixtures/
│ │ │ │ │ ├── access_token_alice.sql
│ │ │ │ │ ├── access_token_alice_with_puppet.sql
│ │ │ │ │ ├── access_token_alice_with_refresh_token.sql
│ │ │ │ │ ├── access_token_alice_with_unused_refresh_token.sql
│ │ │ │ │ ├── devices_alice.sql
│ │ │ │ │ ├── external_ids_alice.sql
│ │ │ │ │ ├── threepids_alice.sql
│ │ │ │ │ └── user_alice.sql
│ │ │ │ ├── mod.rs
│ │ │ │ └── snapshots/
│ │ │ │ ├── syn2mas__synapse_reader__test__read_access_and_refresh_tokens.snap
│ │ │ │ ├── syn2mas__synapse_reader__test__read_access_and_unused_refresh_tokens.snap
│ │ │ │ ├── syn2mas__synapse_reader__test__read_access_token.snap
│ │ │ │ ├── syn2mas__synapse_reader__test__read_devices.snap
│ │ │ │ ├── syn2mas__synapse_reader__test__read_external_ids.snap
│ │ │ │ ├── syn2mas__synapse_reader__test__read_threepids.snap
│ │ │ │ └── syn2mas__synapse_reader__test__read_users.snap
│ │ │ └── telemetry.rs
│ │ └── test_synapse_migrations/
│ │ ├── 20250117064958_users.sql
│ │ ├── 20250128141011_threepids.sql
│ │ ├── 20250128162513_external_ids.sql
│ │ ├── 20250128201100_access_and_refresh_tokens.sql
│ │ └── 20250129140230_devices.sql
│ ├── tasks/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── cleanup/
│ │ │ ├── misc.rs
│ │ │ ├── mod.rs
│ │ │ ├── oauth.rs
│ │ │ ├── sessions.rs
│ │ │ ├── tokens.rs
│ │ │ └── user.rs
│ │ ├── email.rs
│ │ ├── lib.rs
│ │ ├── matrix.rs
│ │ ├── new_queue.rs
│ │ ├── recovery.rs
│ │ ├── sessions.rs
│ │ └── user.rs
│ ├── templates/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── context/
│ │ │ ├── branding.rs
│ │ │ ├── captcha.rs
│ │ │ ├── ext.rs
│ │ │ └── features.rs
│ │ ├── context.rs
│ │ ├── forms.rs
│ │ ├── functions.rs
│ │ ├── lib.rs
│ │ └── macros.rs
│ └── tower/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ ├── metrics/
│ │ ├── duration.rs
│ │ ├── in_flight.rs
│ │ ├── make_attributes.rs
│ │ └── mod.rs
│ ├── trace_context.rs
│ ├── tracing/
│ │ ├── enrich_span.rs
│ │ ├── future.rs
│ │ ├── layer.rs
│ │ ├── make_span.rs
│ │ ├── mod.rs
│ │ └── service.rs
│ └── utils.rs
├── deny.toml
├── docker-bake.hcl
├── docs/
│ ├── README.md
│ ├── SUMMARY.md
│ ├── api/
│ │ ├── index.html
│ │ ├── oauth2-redirect.html
│ │ └── spec.json
│ ├── as-login.md
│ ├── config.schema.json
│ ├── development/
│ │ ├── architecture.md
│ │ ├── cleanup-jobs.md
│ │ ├── contributing.md
│ │ ├── database.md
│ │ ├── graphql.md
│ │ └── releasing.md
│ ├── reference/
│ │ ├── cli/
│ │ │ ├── README.md
│ │ │ ├── config.md
│ │ │ ├── database.md
│ │ │ ├── doctor.md
│ │ │ ├── manage.md
│ │ │ ├── server.md
│ │ │ ├── syn2mas.md
│ │ │ ├── templates.md
│ │ │ └── worker.md
│ │ ├── configuration.md
│ │ └── scopes.md
│ ├── rustdoc/
│ │ └── mas_handlers/
│ │ └── README.md
│ ├── setup/
│ │ ├── README.md
│ │ ├── database.md
│ │ ├── general.md
│ │ ├── homeserver.md
│ │ ├── installation.md
│ │ ├── migration.md
│ │ ├── reverse-proxy.md
│ │ ├── running.md
│ │ └── sso.md
│ ├── storybook/
│ │ └── README.md
│ └── topics/
│ ├── access-token.md
│ ├── admin-api.md
│ ├── authorization.md
│ └── policy.md
├── frontend/
│ ├── .browserlistrc
│ ├── .gitignore
│ ├── .npmrc
│ ├── .postcssrc.json
│ ├── .storybook/
│ │ ├── locales.ts
│ │ ├── main.ts
│ │ ├── preview-head.html
│ │ ├── preview.tsx
│ │ └── public/
│ │ └── mockServiceWorker.js
│ ├── codegen.ts
│ ├── graphql.config.json
│ ├── i18next.config.ts
│ ├── index.html
│ ├── knip.config.ts
│ ├── locales/
│ │ ├── cs.json
│ │ ├── da.json
│ │ ├── de.json
│ │ ├── en.json
│ │ ├── et.json
│ │ ├── fi.json
│ │ ├── fr.json
│ │ ├── hu.json
│ │ ├── nb-NO.json
│ │ ├── nl.json
│ │ ├── pl.json
│ │ ├── pt-BR.json
│ │ ├── pt.json
│ │ ├── ru.json
│ │ ├── sk.json
│ │ ├── sv.json
│ │ ├── uk.json
│ │ ├── uz.json
│ │ └── zh-Hans.json
│ ├── package.json
│ ├── schema.graphql
│ ├── src/
│ │ ├── @types/
│ │ │ └── i18next.d.ts
│ │ ├── components/
│ │ │ ├── AccountDeleteButton.tsx
│ │ │ ├── AccountManagementPasswordPreview/
│ │ │ │ ├── AccountManagementPasswordPreview.module.css
│ │ │ │ ├── AccountManagementPasswordPreview.tsx
│ │ │ │ └── index.ts
│ │ │ ├── BrowserSession.tsx
│ │ │ ├── ButtonLink.module.css
│ │ │ ├── ButtonLink.tsx
│ │ │ ├── Client/
│ │ │ │ ├── OAuth2ClientDetail.test.tsx
│ │ │ │ ├── OAuth2ClientDetail.tsx
│ │ │ │ └── __snapshots__/
│ │ │ │ └── OAuth2ClientDetail.test.tsx.snap
│ │ │ ├── Collapsible/
│ │ │ │ ├── Collapsible.module.css
│ │ │ │ ├── Collapsible.stories.tsx
│ │ │ │ ├── Collapsible.tsx
│ │ │ │ └── index.ts
│ │ │ ├── CompatSession.test.tsx
│ │ │ ├── CompatSession.tsx
│ │ │ ├── DateTime.stories.tsx
│ │ │ ├── DateTime.tsx
│ │ │ ├── Dialog/
│ │ │ │ ├── Dialog.module.css
│ │ │ │ ├── Dialog.stories.tsx
│ │ │ │ ├── Dialog.tsx
│ │ │ │ └── index.ts
│ │ │ ├── EmptyState/
│ │ │ │ ├── EmptyState.module.css
│ │ │ │ ├── EmptyState.stories.tsx
│ │ │ │ ├── EmptyState.tsx
│ │ │ │ └── index.ts
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── ExternalLink/
│ │ │ │ ├── ExternalLink.module.css
│ │ │ │ └── ExternalLink.tsx
│ │ │ ├── Filter/
│ │ │ │ ├── Filter.module.css
│ │ │ │ ├── Filter.stories.tsx
│ │ │ │ ├── Filter.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Footer/
│ │ │ │ ├── Footer.module.css
│ │ │ │ ├── Footer.stories.tsx
│ │ │ │ ├── Footer.tsx
│ │ │ │ └── index.ts
│ │ │ ├── GenericError.module.css
│ │ │ ├── GenericError.tsx
│ │ │ ├── Layout/
│ │ │ │ ├── Layout.module.css
│ │ │ │ ├── Layout.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Link.tsx
│ │ │ ├── LoadingScreen/
│ │ │ │ ├── LoadingScreen.module.css
│ │ │ │ ├── LoadingScreen.stories.tsx
│ │ │ │ ├── LoadingScreen.test.tsx
│ │ │ │ ├── LoadingScreen.tsx
│ │ │ │ ├── __snapshots__/
│ │ │ │ │ └── LoadingScreen.test.tsx.snap
│ │ │ │ └── index.ts
│ │ │ ├── LoadingSpinner/
│ │ │ │ ├── LoadingSpinner.module.css
│ │ │ │ ├── LoadingSpinner.stories.tsx
│ │ │ │ ├── LoadingSpinner.tsx
│ │ │ │ └── index.ts
│ │ │ ├── NavBar/
│ │ │ │ ├── NavBar.module.css
│ │ │ │ ├── NavBar.stories.tsx
│ │ │ │ ├── NavBar.tsx
│ │ │ │ └── index.ts
│ │ │ ├── NavItem/
│ │ │ │ ├── NavItem.module.css
│ │ │ │ ├── NavItem.tsx
│ │ │ │ ├── NavItemErrorIcon.tsx
│ │ │ │ ├── __snapshots__/
│ │ │ │ │ └── NavItem.test.tsx.snap
│ │ │ │ └── index.ts
│ │ │ ├── NotFound.tsx
│ │ │ ├── OAuth2Session.test.tsx
│ │ │ ├── OAuth2Session.tsx
│ │ │ ├── PageHeading/
│ │ │ │ ├── PageHeading.module.css
│ │ │ │ ├── PageHeading.tsx
│ │ │ │ └── index.ts
│ │ │ ├── PaginationControls.tsx
│ │ │ ├── PasswordConfirmation.tsx
│ │ │ ├── PasswordCreationDoubleInput.tsx
│ │ │ ├── Separator/
│ │ │ │ ├── Separator.module.css
│ │ │ │ ├── Separator.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── Session/
│ │ │ │ ├── ClientAvatar.module.css
│ │ │ │ ├── ClientAvatar.test.tsx
│ │ │ │ ├── ClientAvatar.tsx
│ │ │ │ ├── DeviceTypeIcon.module.css
│ │ │ │ ├── DeviceTypeIcon.stories.tsx
│ │ │ │ ├── DeviceTypeIcon.test.tsx
│ │ │ │ ├── DeviceTypeIcon.tsx
│ │ │ │ ├── EndBrowserSessionButton.tsx
│ │ │ │ ├── EndCompatSessionButton.tsx
│ │ │ │ ├── EndOAuth2SessionButton.tsx
│ │ │ │ ├── EndSessionButton.tsx
│ │ │ │ ├── LastActive.module.css
│ │ │ │ ├── LastActive.stories.tsx
│ │ │ │ ├── LastActive.test.tsx
│ │ │ │ ├── LastActive.tsx
│ │ │ │ └── __snapshots__/
│ │ │ │ ├── ClientAvatar.test.tsx.snap
│ │ │ │ ├── DeviceTypeIcon.test.tsx.snap
│ │ │ │ ├── LastActive.test.tsx.snap
│ │ │ │ └── Session.test.tsx.snap
│ │ │ ├── SessionCard/
│ │ │ │ ├── SessionCard.module.css
│ │ │ │ ├── SessionCard.stories.tsx
│ │ │ │ ├── SessionCard.tsx
│ │ │ │ └── index.ts
│ │ │ ├── SessionDetail/
│ │ │ │ ├── BrowserSessionDetail.tsx
│ │ │ │ ├── CompatSessionDetail.test.tsx
│ │ │ │ ├── CompatSessionDetail.tsx
│ │ │ │ ├── EditSessionName.tsx
│ │ │ │ ├── OAuth2SessionDetail.test.tsx
│ │ │ │ ├── OAuth2SessionDetail.tsx
│ │ │ │ ├── SessionHeader.module.css
│ │ │ │ ├── SessionHeader.stories.tsx
│ │ │ │ ├── SessionHeader.test.tsx
│ │ │ │ ├── SessionHeader.tsx
│ │ │ │ ├── SessionInfo.tsx
│ │ │ │ └── __snapshots__/
│ │ │ │ ├── CompatSessionDetail.test.tsx.snap
│ │ │ │ ├── OAuth2SessionDetail.test.tsx.snap
│ │ │ │ └── SessionHeader.test.tsx.snap
│ │ │ ├── Typography.stories.tsx
│ │ │ ├── Typography.tsx
│ │ │ ├── UserEmail/
│ │ │ │ ├── UserEmail.module.css
│ │ │ │ ├── UserEmail.tsx
│ │ │ │ └── index.ts
│ │ │ ├── UserGreeting/
│ │ │ │ ├── UserGreeting.module.css
│ │ │ │ ├── UserGreeting.stories.tsx
│ │ │ │ ├── UserGreeting.tsx
│ │ │ │ └── index.ts
│ │ │ ├── UserProfile/
│ │ │ │ ├── AddEmailForm.tsx
│ │ │ │ └── UserEmailList.tsx
│ │ │ ├── UserSessionsOverview/
│ │ │ │ ├── BrowserSessionsOverview.module.css
│ │ │ │ ├── BrowserSessionsOverview.stories.tsx
│ │ │ │ ├── BrowserSessionsOverview.test.tsx
│ │ │ │ ├── BrowserSessionsOverview.tsx
│ │ │ │ └── __snapshots__/
│ │ │ │ ├── BrowserSessionsOverview.test.tsx.snap
│ │ │ │ └── UserSessionsOverview.test.tsx.snap
│ │ │ └── __snapshots__/
│ │ │ ├── CompatSession.test.tsx.snap
│ │ │ ├── LoadingScreen.test.tsx.snap
│ │ │ └── OAuth2Session.test.tsx.snap
│ │ ├── config.ts
│ │ ├── entrypoints/
│ │ │ ├── main.tsx
│ │ │ ├── shared.css
│ │ │ ├── swagger.ts
│ │ │ ├── templates.css
│ │ │ └── templates.ts
│ │ ├── gql/
│ │ │ ├── fragment-masking.ts
│ │ │ ├── gql.ts
│ │ │ ├── graphql.ts
│ │ │ └── index.ts
│ │ ├── graphql.ts
│ │ ├── i18n/
│ │ │ └── password_changes.ts
│ │ ├── i18n.ts
│ │ ├── pagination.ts
│ │ ├── routeTree.gen.ts
│ │ ├── router.tsx
│ │ ├── routes/
│ │ │ ├── __root.tsx
│ │ │ ├── _account.index.tsx
│ │ │ ├── _account.plan.index.tsx
│ │ │ ├── _account.sessions.browsers.tsx
│ │ │ ├── _account.sessions.index.tsx
│ │ │ ├── _account.tsx
│ │ │ ├── clients.$id.tsx
│ │ │ ├── devices.$.tsx
│ │ │ ├── emails.$id.in-use.tsx
│ │ │ ├── emails.$id.verify.tsx
│ │ │ ├── password.change.index.tsx
│ │ │ ├── password.change.success.tsx
│ │ │ ├── password.recovery.index.tsx
│ │ │ ├── reset-cross-signing.cancelled.tsx
│ │ │ ├── reset-cross-signing.index.tsx
│ │ │ ├── reset-cross-signing.success.tsx
│ │ │ ├── reset-cross-signing.tsx
│ │ │ └── sessions.$id.tsx
│ │ ├── styles/
│ │ │ ├── cpd-button.css
│ │ │ ├── cpd-checkbox-control.css
│ │ │ ├── cpd-form.css
│ │ │ ├── cpd-link.css
│ │ │ ├── cpd-mfa-control.css
│ │ │ └── cpd-text-control.css
│ │ ├── test-utils/
│ │ │ ├── mockLocale.ts
│ │ │ ├── render.tsx
│ │ │ └── router.tsx
│ │ ├── utils/
│ │ │ ├── dates.ts
│ │ │ ├── deviceIdFromScope.test.ts
│ │ │ ├── deviceIdFromScope.ts
│ │ │ ├── password_complexity/
│ │ │ │ ├── enwiki.json
│ │ │ │ ├── index.ts
│ │ │ │ ├── namesf.json
│ │ │ │ ├── namesm.json
│ │ │ │ ├── namess.json
│ │ │ │ ├── passwords.json
│ │ │ │ └── ustvfilm.json
│ │ │ └── simplifyUrl.ts
│ │ └── vite-env.d.ts
│ ├── stories/
│ │ └── routes/
│ │ ├── app.tsx
│ │ ├── index.stories.tsx
│ │ └── reset-cross-signing.stories.tsx
│ ├── tailwind.config.cjs
│ ├── tests/
│ │ ├── mocks/
│ │ │ └── handlers.ts
│ │ └── routes/
│ │ ├── __snapshots__/
│ │ │ └── reset-cross-signing.test.tsx.snap
│ │ ├── account/
│ │ │ ├── __snapshots__/
│ │ │ │ ├── index.test.tsx.snap
│ │ │ │ └── sessions.test.tsx.snap
│ │ │ ├── index.test.tsx
│ │ │ └── sessions.test.tsx
│ │ ├── render.tsx
│ │ ├── reset-cross-signing.test.tsx
│ │ └── types.d.ts
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ ├── vite.config.ts
│ ├── vitest.global-setup.ts
│ └── vitest.setup.ts
├── localazy.json
├── misc/
│ ├── build-docs.sh
│ ├── device-code-grant.sh
│ ├── sqlx_update.sh
│ └── update.sh
├── policies/
│ ├── .gitignore
│ ├── .regal/
│ │ └── config.yaml
│ ├── Makefile
│ ├── authorization_grant/
│ │ ├── authorization_grant.rego
│ │ └── authorization_grant_test.rego
│ ├── client_registration/
│ │ ├── client_registration.rego
│ │ └── client_registration_test.rego
│ ├── common/
│ │ ├── common.rego
│ │ └── common_test.rego
│ ├── compat_login/
│ │ ├── compat_login.rego
│ │ └── compat_login_test.rego
│ ├── email/
│ │ ├── email.rego
│ │ └── email_test.rego
│ ├── register/
│ │ ├── register.rego
│ │ └── register_test.rego
│ ├── schema/
│ │ ├── authorization_grant_input.json
│ │ ├── client_registration_input.json
│ │ ├── compat_login_input.json
│ │ ├── email_input.json
│ │ └── register_input.json
│ └── util/
│ └── coveralls.rego
├── templates/
│ ├── app.html
│ ├── base.html
│ ├── components/
│ │ ├── back_to_client.html
│ │ ├── button.html
│ │ ├── captcha.html
│ │ ├── errors.html
│ │ ├── field.html
│ │ ├── footer.html
│ │ ├── icon.html
│ │ ├── idp_brand.html
│ │ ├── logout.html
│ │ └── scope.html
│ ├── device_name.txt
│ ├── emails/
│ │ ├── recovery.html
│ │ ├── recovery.subject
│ │ ├── recovery.txt
│ │ ├── verification.html
│ │ ├── verification.subject
│ │ └── verification.txt
│ ├── form_post.html
│ ├── pages/
│ │ ├── 404.html
│ │ ├── account/
│ │ │ ├── deactivated.html
│ │ │ ├── locked.html
│ │ │ └── logged_out.html
│ │ ├── compat_login_policy_violation.html
│ │ ├── consent.html
│ │ ├── device_consent.html
│ │ ├── device_link.html
│ │ ├── error.html
│ │ ├── index.html
│ │ ├── login.html
│ │ ├── policy_violation.html
│ │ ├── reauth.html
│ │ ├── recovery/
│ │ │ ├── consumed.html
│ │ │ ├── disabled.html
│ │ │ ├── expired.html
│ │ │ ├── finish.html
│ │ │ ├── progress.html
│ │ │ └── start.html
│ │ ├── register/
│ │ │ ├── index.html
│ │ │ ├── password.html
│ │ │ └── steps/
│ │ │ ├── display_name.html
│ │ │ ├── email_in_use.html
│ │ │ ├── registration_token.html
│ │ │ └── verify_email.html
│ │ ├── sso.html
│ │ └── upstream_oauth2/
│ │ ├── do_register.html
│ │ ├── link_mismatch.html
│ │ └── suggest_link.html
│ └── swagger/
│ ├── doc.html
│ └── oauth2-redirect.html
└── translations/
├── cs.json
├── da.json
├── de.json
├── en.json
├── et.json
├── fi.json
├── fr.json
├── hu.json
├── nb-NO.json
├── nl.json
├── pl.json
├── pt-BR.json
├── pt.json
├── ru.json
├── sk.json
├── sv.json
├── uk.json
├── uz.json
└── zh-Hans.json
Showing preview only (553K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (6550 symbols across 605 files)
FILE: crates/axum-utils/src/client_authorization.rs
type AuthorizedForm (line 35) | struct AuthorizedForm<F = ()> {
type Credentials (line 46) | pub enum Credentials {
method client_id (line 70) | pub fn client_id(&self) -> Option<&str> {
method bearer_token (line 82) | pub fn bearer_token(&self) -> Option<&str> {
method fetch (line 95) | pub async fn fetch<E>(
method verify (line 116) | pub async fn verify(
function fetch_jwks (line 194) | async fn fetch_jwks(
type CredentialsVerificationError (line 215) | pub enum CredentialsVerificationError {
method is_internal (line 238) | pub fn is_internal(&self) -> bool {
type ClientAuthorization (line 247) | pub struct ClientAuthorization<F = ()> {
function client_id (line 255) | pub fn client_id(&self) -> Option<&str> {
type ClientAuthorizationError (line 261) | pub enum ClientAuthorizationError {
method into_response (line 288) | fn into_response(self) -> axum::response::Response {
type Rejection (line 369) | type Rejection = ClientAuthorizationError;
function from_request (line 371) | async fn from_request(
function none_test (line 543) | async fn none_test() {
function client_secret_basic_test (line 567) | async fn client_secret_basic_test() {
function client_secret_post_test (line 658) | async fn client_secret_post_test() {
function client_assertion_test (line 685) | async fn client_assertion_test() {
function bearer_token_test (line 716) | async fn bearer_token_test() {
FILE: crates/axum-utils/src/cookies.rs
type CookieDecodeError (line 23) | pub enum CookieDecodeError {
type CookieManager (line 32) | pub struct CookieManager {
method new (line 39) | pub const fn new(base_url: Url, key: Key) -> Self {
method derive_from (line 45) | pub fn derive_from(base_url: Url, key: &[u8]) -> Self {
method cookie_jar (line 51) | pub fn cookie_jar(&self) -> CookieJar {
method cookie_jar_from_headers (line 59) | pub fn cookie_jar_from_headers(&self, headers: &http::HeaderMap) -> Co...
type CookieOption (line 81) | struct CookieOption {
method new (line 86) | const fn new(base_url: Url) -> Self {
method secure (line 90) | fn secure(&self) -> bool {
method path (line 94) | fn path(&self) -> &str {
method apply (line 98) | fn apply<'a>(&self, mut cookie: Cookie<'a>) -> Cookie<'a> {
type CookieJar (line 108) | pub struct CookieJar {
type Rejection (line 72) | type Rejection = Infallible;
method from_request_parts (line 74) | async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Se...
method save (line 122) | pub fn save<T: Serialize>(mut self, key: &str, payload: &T, permanent:...
method remove (line 141) | pub fn remove(mut self, key: &str) -> Self {
method load (line 153) | pub fn load<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>...
type Error (line 164) | type Error = Infallible;
method into_response_parts (line 166) | fn into_response_parts(self, res: ResponseParts) -> Result<ResponseParts...
FILE: crates/axum-utils/src/csrf.rs
type CsrfError (line 19) | pub enum CsrfError {
type CsrfToken (line 44) | pub struct CsrfToken {
method new (line 52) | fn new(token: [u8; 32], now: DateTime<Utc>, ttl: Duration) -> Self {
method generate (line 58) | fn generate(now: DateTime<Utc>, mut rng: impl Rng, ttl: Duration) -> S...
method refresh (line 64) | fn refresh(self, now: DateTime<Utc>, ttl: Duration) -> Self {
method form_value (line 70) | pub fn form_value(&self) -> String {
method verify_form_value (line 79) | pub fn verify_form_value(&self, form_value: &str) -> Result<(), CsrfEr...
method verify_expiration (line 88) | fn verify_expiration(self, now: DateTime<Utc>) -> Result<Self, CsrfErr...
type ProtectedForm (line 99) | pub struct ProtectedForm<T> {
type CsrfExt (line 106) | pub trait CsrfExt {
method csrf_token (line 109) | fn csrf_token<C, R>(self, clock: &C, rng: R) -> (CsrfToken, Self)
method verify_form (line 121) | fn verify_form<C, T>(&self, clock: &C, form: ProtectedForm<T>) -> Resu...
method csrf_token (line 127) | fn csrf_token<C, R>(self, clock: &C, rng: R) -> (CsrfToken, Self)
method verify_form (line 156) | fn verify_form<C, T>(&self, clock: &C, form: ProtectedForm<T>) -> Resu...
FILE: crates/axum-utils/src/error_wrapper.rs
type ErrorWrapper (line 14) | pub struct ErrorWrapper<T>(#[from] pub T);
method into_response (line 20) | fn into_response(self) -> Response {
FILE: crates/axum-utils/src/fancy_error.rs
function build_context (line 18) | fn build_context(mut err: &dyn std::error::Error) -> ErrorContext {
type GenericError (line 31) | pub struct GenericError {
method new (line 53) | pub fn new(code: StatusCode, err: impl std::error::Error + 'static) ->...
method into_response (line 37) | fn into_response(self) -> Response {
type InternalError (line 61) | pub struct InternalError {
method from (line 84) | fn from(err: E) -> Self {
method new (line 94) | pub fn new(error: Box<dyn std::error::Error + 'static>) -> Self {
method from_anyhow (line 100) | pub fn from_anyhow(err: anyhow::Error) -> Self {
method into_response (line 66) | fn into_response(self) -> Response {
FILE: crates/axum-utils/src/jwt.rs
type JwtResponse (line 13) | pub struct JwtResponse<T>(pub Jwt<'static, T>);
method into_response (line 16) | fn into_response(self) -> Response {
FILE: crates/axum-utils/src/language_detection.rs
type AcceptLanguagePart (line 14) | struct AcceptLanguagePart {
method partial_cmp (line 24) | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
method cmp (line 30) | fn cmp(&self, other: &Self) -> std::cmp::Ordering {
type AcceptLanguage (line 39) | pub struct AcceptLanguage {
method iter (line 44) | pub fn iter(&self) -> impl Iterator<Item = &Locale> {
function trim_bytes (line 51) | const fn trim_bytes(mut bytes: &[u8]) -> &[u8] {
method name (line 73) | fn name() -> &'static HeaderName {
method decode (line 77) | fn decode<'i, I>(values: &mut I) -> Result<Self, Error>
method encode (line 128) | fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {
function test_decode (line 164) | fn test_decode() {
function test_decode_order (line 206) | fn test_decode_order() {
function test_encode (line 246) | fn test_encode() {
FILE: crates/axum-utils/src/sentry.rs
type SentryEventID (line 14) | pub struct SentryEventID(Uuid);
method for_last_event (line 18) | pub fn for_last_event() -> Option<Self> {
method from (line 24) | fn from(uuid: Uuid) -> Self {
type Error (line 30) | type Error = Infallible;
method into_response_parts (line 31) | fn into_response_parts(self, mut res: ResponseParts) -> Result<ResponseP...
FILE: crates/axum-utils/src/session.rs
type SessionInfo (line 16) | pub struct SessionInfo {
method from_session (line 23) | pub fn from_session(session: &BrowserSession) -> Self {
method mark_session_ended (line 31) | pub fn mark_session_ended(mut self) -> Self {
method load_active_session (line 41) | pub async fn load_active_session<E>(
method current_session_id (line 61) | pub fn current_session_id(&self) -> Option<Ulid> {
type SessionInfoExt (line 66) | pub trait SessionInfoExt {
method session_info (line 68) | fn session_info(self) -> (SessionInfo, Self);
method update_session_info (line 71) | fn update_session_info(self, info: &SessionInfo) -> Self;
method set_session (line 74) | fn set_session(self, session: &BrowserSession) -> Self
method session_info (line 84) | fn session_info(self) -> (SessionInfo, Self) {
method update_session_info (line 98) | fn update_session_info(self, info: &SessionInfo) -> Self {
FILE: crates/axum-utils/src/user_authorization.rs
type AuthorizedForm (line 28) | struct AuthorizedForm<F> {
type AccessToken (line 37) | enum AccessToken {
method fetch (line 44) | async fn fetch<E>(
type UserAuthorization (line 70) | pub struct UserAuthorization<F = ()> {
function protected_form (line 84) | pub async fn protected_form<E>(
function protected (line 108) | pub async fn protected<E>(
type UserAuthorizationError (line 128) | pub enum UserAuthorizationError {
type AuthorizationVerificationError (line 136) | pub enum AuthorizationVerificationError<E> {
type BearerError (line 150) | enum BearerError {
method error (line 160) | fn error(&self) -> HeaderValue {
method params (line 168) | fn params(&self) -> HashMap<&'static str, HeaderValue> {
type WwwAuthenticate (line 180) | enum WwwAuthenticate {
method name (line 191) | fn name() -> &'static HeaderName {
method decode (line 195) | fn decode<'i, I>(_values: &mut I) -> Result<Self, headers::Error>
method encode (line 203) | fn encode<E: Extend<http::HeaderValue>>(&self, values: &mut E) {
method into_response (line 238) | fn into_response(self) -> Response {
method into_response (line 259) | fn into_response(self) -> Response {
type Rejection (line 291) | type Rejection = UserAuthorizationError;
function from_request (line 293) | async fn from_request(
FILE: crates/cli/build.rs
function main (line 8) | fn main() -> anyhow::Result<()> {
FILE: crates/cli/src/app_state.rs
type AppState (line 33) | pub struct AppState {
method init_metrics (line 54) | pub fn init_metrics(&mut self) {
method init_metadata_cache (line 81) | pub fn init_metadata_cache(&self) {
method from_ref (line 122) | fn from_ref(input: &AppState) -> Self {
method from_ref (line 128) | fn from_ref(input: &AppState) -> Self {
method from_ref (line 134) | fn from_ref(input: &AppState) -> Self {
method from_ref (line 140) | fn from_ref(input: &AppState) -> Self {
function from_ref (line 146) | fn from_ref(input: &AppState) -> Self {
method from_ref (line 152) | fn from_ref(input: &AppState) -> Self {
method from_ref (line 158) | fn from_ref(input: &AppState) -> Self {
method from_ref (line 164) | fn from_ref(input: &AppState) -> Self {
function from_ref (line 170) | fn from_ref(input: &AppState) -> Self {
method from_ref (line 176) | fn from_ref(input: &AppState) -> Self {
method from_ref (line 182) | fn from_ref(input: &AppState) -> Self {
method from_ref (line 188) | fn from_ref(input: &AppState) -> Self {
method from_ref (line 194) | fn from_ref(input: &AppState) -> Self {
method from_ref (line 200) | fn from_ref(input: &AppState) -> Self {
function from_ref (line 206) | fn from_ref(input: &AppState) -> Self {
function from_ref (line 212) | fn from_ref(input: &AppState) -> Self {
method from_ref (line 218) | fn from_ref(_input: &AppState) -> Self {
type Rejection (line 224) | type Rejection = Infallible;
method from_request_parts (line 226) | async fn from_request_parts(
type Rejection (line 236) | type Rejection = Infallible;
method from_request_parts (line 238) | async fn from_request_parts(
type Rejection (line 252) | type Rejection = ErrorWrapper<mas_policy::InstantiateError>;
method from_request_parts (line 254) | async fn from_request_parts(
type Rejection (line 264) | type Rejection = Infallible;
method from_request_parts (line 266) | async fn from_request_parts(
function infer_client_ip (line 274) | fn infer_client_ip(
type Rejection (line 328) | type Rejection = Infallible;
method from_request_parts (line 330) | async fn from_request_parts(
type Rejection (line 342) | type Rejection = Infallible;
method from_request_parts (line 344) | async fn from_request_parts(
type Rejection (line 365) | type Rejection = ErrorWrapper<mas_storage::RepositoryError>;
method from_request_parts (line 367) | async fn from_request_parts(
FILE: crates/cli/src/commands/config.rs
type Options (line 22) | pub(super) struct Options {
method run (line 68) | pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
type Subcommand (line 28) | enum Subcommand {
FILE: crates/cli/src/commands/database.rs
type Options (line 18) | pub(super) struct Options {
method run (line 30) | pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
type Subcommand (line 24) | enum Subcommand {
FILE: crates/cli/src/commands/debug.rs
type Options (line 23) | pub(super) struct Options {
method run (line 40) | pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
type Subcommand (line 29) | enum Subcommand {
FILE: crates/cli/src/commands/doctor.rs
constant DOCS_BASE (line 24) | const DOCS_BASE: &str = "https://element-hq.github.io/matrix-authenticat...
type Options (line 27) | pub(super) struct Options {}
method run (line 30) | pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
FILE: crates/cli/src/commands/manage.rs
constant USER_ATTRIBUTES_HEADING (line 48) | const USER_ATTRIBUTES_HEADING: &str = "User attributes";
type UpstreamProviderMapping (line 51) | struct UpstreamProviderMapping {
function parse_upstream_provider_mapping (line 56) | fn parse_upstream_provider_mapping(s: &str) -> Result<UpstreamProviderMa...
type Options (line 68) | pub(super) struct Options {
method run (line 222) | pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
type Subcommand (line 74) | enum Subcommand {
type HumanReadable (line 949) | struct HumanReadable<T>(T);
function fmt (line 952) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
function check_and_normalize_username (line 964) | async fn check_and_normalize_username<'a>(
type UserCreationRequest (line 993) | struct UserCreationRequest<'a> {
function possible_actions (line 1004) | fn possible_actions(
function prompt_action (line 1031) | async fn prompt_action(
function show (line 1049) | fn show(&self, term: &Term, homeserver: &dyn HomeserverConnection) -> st...
function do_register (line 1112) | async fn do_register<E: std::error::Error + Send + Sync + 'static>(
type Action (line 1170) | enum Action {
method fmt (line 1181) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type UserCreationCommand (line 1195) | struct UserCreationCommand<'a>(&'a UserCreationRequest<'a>);
function fmt (line 1198) | fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
FILE: crates/cli/src/commands/mod.rs
type Subcommand (line 27) | enum Subcommand {
type Options (line 61) | pub struct Options {
method run (line 71) | pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
method figment (line 90) | pub fn figment(&self) -> Figment {
FILE: crates/cli/src/commands/server.rs
type Options (line 37) | pub(super) struct Options {
method run (line 57) | pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
FILE: crates/cli/src/commands/syn2mas.rs
constant EXIT_CODE_CHECK_ERRORS (line 28) | const EXIT_CODE_CHECK_ERRORS: u8 = 10;
constant EXIT_CODE_CHECK_WARNINGS (line 32) | const EXIT_CODE_CHECK_WARNINGS: u8 = 11;
type Options (line 35) | pub(super) struct Options {
method run (line 98) | pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
type Subcommand (line 75) | enum Subcommand {
constant NUM_WRITER_CONNECTIONS (line 94) | const NUM_WRITER_CONNECTIONS: usize = 8;
function occasional_progress_logger (line 293) | async fn occasional_progress_logger(progress: Progress) {
FILE: crates/cli/src/commands/templates.rs
type Options (line 25) | pub(super) struct Options {
method run (line 48) | pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
type Subcommand (line 31) | enum Subcommand {
FILE: crates/cli/src/commands/worker.rs
type Options (line 26) | pub(super) struct Options {}
method run (line 29) | pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
FILE: crates/cli/src/lifecycle.rs
type LifecycleManager (line 31) | pub struct LifecycleManager {
method new (line 80) | pub fn new() -> Result<Self, std::io::Error> {
method register_reloadable (line 104) | pub fn register_reloadable(&mut self, reloadable: &(impl Reloadable + ...
method task_tracker (line 114) | pub fn task_tracker(&self) -> &TaskTracker {
method hard_shutdown_token (line 120) | pub fn hard_shutdown_token(&self) -> CancellationToken {
method soft_shutdown_token (line 126) | pub fn soft_shutdown_token(&self) -> CancellationToken {
method run (line 131) | pub async fn run(mut self) -> ExitCode {
type Reloadable (line 43) | pub trait Reloadable: Clone + Send {
method reload (line 44) | fn reload(&self) -> impl Future<Output = ()> + Send;
method reload (line 48) | async fn reload(&self) {
method reload (line 54) | async fn reload(&self) {
function notify (line 65) | fn notify(states: &[sd_notify::NotifyState]) {
FILE: crates/cli/src/main.rs
type SentryTransportFactory (line 34) | struct SentryTransportFactory {
method new (line 39) | fn new() -> Self {
method create_transport (line 47) | fn create_transport(&self, options: &sentry::ClientOptions) -> Arc<dyn...
function main (line 55) | fn main() -> anyhow::Result<ExitCode> {
function async_main (line 71) | async fn async_main() -> anyhow::Result<ExitCode> {
function try_main (line 82) | async fn try_main() -> anyhow::Result<ExitCode> {
FILE: crates/cli/src/server.rs
constant MAS_LISTENER_NAME (line 45) | const MAS_LISTENER_NAME: Key = Key::from_static_str("mas.listener.name");
function otel_http_method (line 48) | fn otel_http_method<B>(request: &Request<B>) -> &'static str {
function otel_net_protocol_version (line 64) | fn otel_net_protocol_version<B>(request: &Request<B>) -> &'static str {
function otel_http_route (line 75) | fn otel_http_route<B>(request: &Request<B>) -> Option<&str> {
function otel_url_scheme (line 82) | fn otel_url_scheme<B>(request: &Request<B>) -> &'static str {
function make_http_span (line 97) | fn make_http_span<B>(req: &Request<B>) -> Span {
function on_http_request_labels (line 161) | fn on_http_request_labels<B>(request: &Request<B>) -> Vec<KeyValue> {
function on_http_response_labels (line 174) | fn on_http_response_labels<B>(res: &Response<B>) -> Vec<KeyValue> {
function log_response_middleware (line 181) | async fn log_response_middleware(
function build_router (line 218) | pub fn build_router(
function build_tls_server_config (line 350) | pub fn build_tls_server_config(config: &HttpTlsConfig) -> Result<ServerC...
function build_listeners (line 362) | pub fn build_listeners(
FILE: crates/cli/src/sync.rs
function map_import_action (line 22) | fn map_import_action(
function map_import_on_conflict (line 41) | fn map_import_on_conflict(
function map_claims_imports (line 60) | fn map_claims_imports(
function config_sync (line 88) | pub async fn config_sync(
FILE: crates/cli/src/telemetry.rs
function setup (line 51) | pub fn setup(config: &TelemetryConfig) -> anyhow::Result<()> {
function shutdown (line 69) | pub fn shutdown() -> opentelemetry_sdk::error::OTelSdkResult {
function match_propagator (line 81) | fn match_propagator(propagator: Propagator) -> Box<dyn TextMapPropagator...
function propagator (line 90) | fn propagator(propagators: &[Propagator]) -> TextMapCompositePropagator {
type InvalidIdGenerator (line 101) | struct InvalidIdGenerator;
method new_trace_id (line 103) | fn new_trace_id(&self) -> opentelemetry::TraceId {
method new_span_id (line 106) | fn new_span_id(&self) -> opentelemetry::SpanId {
function init_tracer (line 111) | fn init_tracer(config: &TracingConfig) -> anyhow::Result<()> {
function otlp_metric_reader (line 169) | fn otlp_metric_reader(
function stdout_metric_reader (line 186) | fn stdout_metric_reader() -> PeriodicReader<opentelemetry_stdout::Metric...
type PromServiceFuture (line 191) | type PromServiceFuture =
function prometheus_service_fn (line 195) | fn prometheus_service_fn<T>(_req: T) -> PromServiceFuture {
function prometheus_service (line 233) | pub fn prometheus_service<T>() -> tower::util::ServiceFn<fn(T) -> PromSe...
function prometheus_metric_reader (line 243) | fn prometheus_metric_reader() -> anyhow::Result<PrometheusExporter> {
function init_meter (line 253) | fn init_meter(config: &MetricsConfig) -> anyhow::Result<()> {
function resource (line 276) | fn resource() -> Resource {
FILE: crates/cli/src/util.rs
function password_manager_from_config (line 32) | pub async fn password_manager_from_config(
function mailer_from_config (line 61) | pub fn mailer_from_config(
function test_mailer_in_background (line 112) | pub fn test_mailer_in_background(mailer: &Mailer, timeout: Duration) {
function policy_factory_from_config (line 135) | pub async fn policy_factory_from_config(
function captcha_config_from_config (line 174) | pub fn captcha_config_from_config(
function site_config_from_config (line 202) | pub fn site_config_from_config(
function templates_from_config (line 258) | pub async fn templates_from_config(
function database_connect_options_from_config (line 278) | fn database_connect_options_from_config(
function database_pool_from_config (line 376) | pub async fn database_pool_from_config(config: &DatabaseConfig) -> Resul...
type DatabaseConnectOptions (line 398) | pub struct DatabaseConnectOptions {
method default (line 403) | fn default() -> Self {
function database_connection_from_config (line 412) | pub async fn database_connection_from_config(
function database_connection_from_config_with_options (line 424) | pub async fn database_connection_from_config_with_options(
function load_policy_factory_dynamic_data_continuously (line 437) | pub async fn load_policy_factory_dynamic_data_continuously(
function load_policy_factory_dynamic_data (line 476) | pub async fn load_policy_factory_dynamic_data(
function homeserver_connection_from_config (line 498) | pub async fn homeserver_connection_from_config(
function test_password_manager_from_config (line 538) | async fn test_password_manager_from_config() {
FILE: crates/config/src/bin/schema.rs
function main (line 9) | fn main() {
FILE: crates/config/src/schema.rs
type Hostname (line 14) | pub struct Hostname;
method schema_name (line 17) | fn schema_name() -> Cow<'static, str> {
method json_schema (line 21) | fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
FILE: crates/config/src/sections/account.rs
function default_true (line 12) | const fn default_true() -> bool {
function is_default_true (line 17) | const fn is_default_true(value: &bool) -> bool {
function default_false (line 21) | const fn default_false() -> bool {
function is_default_false (line 26) | const fn is_default_false(value: &bool) -> bool {
type AccountConfig (line 33) | pub struct AccountConfig {
method is_default (line 111) | pub(crate) fn is_default(&self) -> bool {
method default (line 94) | fn default() -> Self {
constant PATH (line 124) | const PATH: Option<&'static str> = Some("account");
FILE: crates/config/src/sections/branding.rs
type BrandingConfig (line 15) | pub struct BrandingConfig {
method is_default (line 44) | pub(crate) fn is_default(&self) -> bool {
constant PATH (line 54) | const PATH: Option<&'static str> = Some("branding");
FILE: crates/config/src/sections/captcha.rs
type CaptchaServiceKind (line 14) | pub enum CaptchaServiceKind {
type CaptchaConfig (line 30) | pub struct CaptchaConfig {
method is_default (line 46) | pub(crate) fn is_default(&self) -> bool {
constant PATH (line 52) | const PATH: Option<&'static str> = Some("captcha");
method validate (line 54) | fn validate(
FILE: crates/config/src/sections/clients.rs
type ClientAuthMethodConfig (line 22) | pub enum ClientAuthMethodConfig {
method fmt (line 44) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type ClientConfig (line 58) | pub struct ClientConfig {
method validate (line 97) | fn validate(&self) -> Result<(), Box<figment::error::Error>> {
method client_auth_method (line 176) | pub fn client_auth_method(&self) -> OAuthClientAuthenticationMethod {
method client_secret (line 199) | pub async fn client_secret(&self) -> anyhow::Result<Option<String>> {
type ClientsConfig (line 210) | pub struct ClientsConfig(#[schemars(with = "Vec::<ClientConfig>")] Vec<C...
method is_default (line 214) | pub(crate) fn is_default(&self) -> bool {
type Target (line 220) | type Target = Vec<ClientConfig>;
method deref (line 222) | fn deref(&self) -> &Self::Target {
type Item (line 228) | type Item = ClientConfig;
type IntoIter (line 229) | type IntoIter = std::vec::IntoIter<ClientConfig>;
method into_iter (line 231) | fn into_iter(self) -> Self::IntoIter {
constant PATH (line 237) | const PATH: Option<&'static str> = Some("clients");
method validate (line 239) | fn validate(
function load_config (line 271) | async fn load_config() {
FILE: crates/config/src/sections/database.rs
function default_connection_string (line 18) | fn default_connection_string() -> Option<String> {
function default_max_connections (line 22) | fn default_max_connections() -> NonZeroU32 {
function default_connect_timeout (line 26) | fn default_connect_timeout() -> Duration {
function default_idle_timeout (line 31) | fn default_idle_timeout() -> Option<Duration> {
function default_max_lifetime (line 36) | fn default_max_lifetime() -> Option<Duration> {
method default (line 41) | fn default() -> Self {
type PgSslMode (line 70) | pub enum PgSslMode {
type DatabaseConfig (line 97) | pub struct DatabaseConfig {
constant PATH (line 223) | const PATH: Option<&'static str> = Some("database");
method validate (line 225) | fn validate(
function load_config (line 297) | fn load_config() {
FILE: crates/config/src/sections/email.rs
type EmailSmtpMode (line 20) | pub enum EmailSmtpMode {
type EmailTransportKind (line 34) | pub enum EmailTransportKind {
function default_email (line 46) | fn default_email() -> String {
function default_sendmail_command (line 51) | fn default_sendmail_command() -> Option<String> {
type EmailConfig (line 57) | pub struct EmailConfig {
method transport (line 109) | pub fn transport(&self) -> EmailTransportKind {
method mode (line 115) | pub fn mode(&self) -> Option<EmailSmtpMode> {
method hostname (line 121) | pub fn hostname(&self) -> Option<&str> {
method port (line 127) | pub fn port(&self) -> Option<NonZeroU16> {
method username (line 133) | pub fn username(&self) -> Option<&str> {
method password (line 139) | pub fn password(&self) -> Option<&str> {
method command (line 145) | pub fn command(&self) -> Option<&str> {
method default (line 151) | fn default() -> Self {
constant PATH (line 167) | const PATH: Option<&'static str> = Some("email");
method validate (line 169) | fn validate(
FILE: crates/config/src/sections/experimental.rs
function default_true (line 16) | fn default_true() -> bool {
function default_false (line 20) | fn default_false() -> bool {
function default_token_ttl (line 24) | fn default_token_ttl() -> Duration {
function is_default_token_ttl (line 28) | fn is_default_token_ttl(value: &Duration) -> bool {
type InactiveSessionExpirationConfig (line 35) | pub struct InactiveSessionExpirationConfig {
type ExperimentalConfig (line 59) | pub struct ExperimentalConfig {
method is_default (line 112) | pub(crate) fn is_default(&self) -> bool {
method default (line 100) | fn default() -> Self {
constant PATH (line 122) | const PATH: Option<&'static str> = Some("experimental");
method validate (line 124) | fn validate(
type SessionLimitConfig (line 144) | pub struct SessionLimitConfig {
method validate (line 215) | fn validate(&self) -> Result<(), Box<figment::error::Error>> {
FILE: crates/config/src/sections/http.rs
function default_public_base (line 22) | fn default_public_base() -> Url {
function http_listener_assets_path_default (line 27) | fn http_listener_assets_path_default() -> Utf8PathBuf {
function http_listener_assets_path_default (line 32) | fn http_listener_assets_path_default() -> Utf8PathBuf {
function http_listener_assets_path_default (line 37) | fn http_listener_assets_path_default() -> Utf8PathBuf {
function is_default_http_listener_assets_path (line 41) | fn is_default_http_listener_assets_path(value: &Utf8PathBuf) -> bool {
function default_trusted_proxies (line 45) | fn default_trusted_proxies() -> Vec<IpNetwork> {
type UnixOrTcp (line 59) | pub enum UnixOrTcp {
method unix (line 70) | pub const fn unix() -> Self {
method tcp (line 76) | pub const fn tcp() -> Self {
type BindConfig (line 84) | pub enum BindConfig {
type TlsConfig (line 137) | pub struct TlsConfig {
method load (line 192) | pub fn load(
type Resource (line 255) | pub enum Resource {
type ListenerConfig (line 307) | pub struct ListenerConfig {
type HttpConfig (line 334) | pub struct HttpConfig {
method default (line 354) | fn default() -> Self {
constant PATH (line 399) | const PATH: Option<&'static str> = Some("http");
method validate (line 401) | fn validate(
FILE: crates/config/src/sections/matrix.rs
function default_homeserver (line 20) | fn default_homeserver() -> String {
function default_endpoint (line 24) | fn default_endpoint() -> Url {
type HomeserverKind (line 31) | pub enum HomeserverKind {
type Secret (line 54) | pub enum Secret {
type Error (line 70) | type Error = anyhow::Error;
method try_from (line 72) | fn try_from(value: SecretRaw) -> Result<Self, Self::Error> {
type SecretRaw (line 61) | struct SecretRaw {
method from (line 83) | fn from(value: Secret) -> Self {
type MatrixConfig (line 100) | pub struct MatrixConfig {
method secret (line 132) | pub async fn secret(&self) -> anyhow::Result<String> {
method generate (line 143) | pub(crate) fn generate<R>(mut rng: R) -> Self
method test (line 155) | pub(crate) fn test() -> Self {
constant PATH (line 121) | const PATH: Option<&'static str> = Some("matrix");
function load_config (line 176) | async fn load_config() {
function load_config_inline_secrets (line 207) | async fn load_config_inline_secrets() {
FILE: crates/config/src/sections/mod.rs
type RootConfig (line 70) | pub struct RootConfig {
method generate (line 173) | pub async fn generate<R>(mut rng: R) -> anyhow::Result<Self>
method test (line 200) | pub fn test() -> Self {
method validate (line 141) | fn validate(
type AppConfig (line 226) | pub struct AppConfig {
method validate (line 269) | fn validate(
type SyncConfig (line 295) | pub struct SyncConfig {
method validate (line 309) | fn validate(
type ClientSecret (line 327) | pub enum ClientSecret {
method value (line 359) | pub async fn value(&self) -> anyhow::Result<String> {
type ClientSecretRaw (line 337) | pub struct ClientSecretRaw {
method from (line 383) | fn from(value: Option<ClientSecret>) -> Self {
type Error (line 368) | type Error = anyhow::Error;
function try_from (line 370) | fn try_from(value: ClientSecretRaw) -> Result<Self, Self::Error> {
FILE: crates/config/src/sections/oauth.rs
function default_true (line 11) | const fn default_true() -> bool {
function is_default_true (line 16) | const fn is_default_true(value: &bool) -> bool {
type OAuthConfig (line 22) | pub struct OAuthConfig {
method is_default (line 45) | pub(crate) fn is_default(&self) -> bool {
method default (line 36) | fn default() -> Self {
constant PATH (line 51) | const PATH: Option<&'static str> = Some("oauth");
FILE: crates/config/src/sections/passwords.rs
function default_schemes (line 16) | fn default_schemes() -> Vec<HashingScheme> {
function default_enabled (line 27) | fn default_enabled() -> bool {
function default_minimum_complexity (line 31) | fn default_minimum_complexity() -> u8 {
type PasswordsConfig (line 37) | pub struct PasswordsConfig {
method enabled (line 114) | pub fn enabled(&self) -> bool {
method minimum_complexity (line 121) | pub fn minimum_complexity(&self) -> u8 {
method load (line 131) | pub async fn load(
method default (line 63) | fn default() -> Self {
constant PATH (line 73) | const PATH: Option<&'static str> = Some("passwords");
method validate (line 75) | fn validate(
function is_default_false (line 174) | const fn is_default_false(value: &bool) -> bool {
type HashingScheme (line 180) | pub struct HashingScheme {
function default_bcrypt_cost (line 213) | fn default_bcrypt_cost() -> Option<u32> {
type Algorithm (line 220) | pub enum Algorithm {
FILE: crates/config/src/sections/policy.rs
function default_policy_path (line 15) | fn default_policy_path() -> Utf8PathBuf {
function default_policy_path (line 20) | fn default_policy_path() -> Utf8PathBuf {
function default_policy_path (line 25) | fn default_policy_path() -> Utf8PathBuf {
function is_default_policy_path (line 29) | fn is_default_policy_path(value: &Utf8PathBuf) -> bool {
function default_client_registration_entrypoint (line 33) | fn default_client_registration_entrypoint() -> String {
function is_default_client_registration_entrypoint (line 37) | fn is_default_client_registration_entrypoint(value: &String) -> bool {
function default_register_entrypoint (line 41) | fn default_register_entrypoint() -> String {
function is_default_register_entrypoint (line 45) | fn is_default_register_entrypoint(value: &String) -> bool {
function default_authorization_grant_entrypoint (line 49) | fn default_authorization_grant_entrypoint() -> String {
function is_default_authorization_grant_entrypoint (line 53) | fn is_default_authorization_grant_entrypoint(value: &String) -> bool {
function default_password_entrypoint (line 57) | fn default_password_entrypoint() -> String {
function is_default_password_entrypoint (line 61) | fn is_default_password_entrypoint(value: &String) -> bool {
function default_compat_login_entrypoint (line 65) | fn default_compat_login_entrypoint() -> String {
function is_default_compat_login_entrypoint (line 69) | fn is_default_compat_login_entrypoint(value: &String) -> bool {
function default_email_entrypoint (line 73) | fn default_email_entrypoint() -> String {
function is_default_email_entrypoint (line 77) | fn is_default_email_entrypoint(value: &String) -> bool {
function default_data (line 81) | fn default_data() -> serde_json::Value {
function is_default_data (line 85) | fn is_default_data(value: &serde_json::Value) -> bool {
type PolicyConfig (line 92) | pub struct PolicyConfig {
method is_default (line 165) | pub(crate) fn is_default(&self) -> bool {
method default (line 149) | fn default() -> Self {
constant PATH (line 177) | const PATH: Option<&'static str> = Some("policy");
FILE: crates/config/src/sections/rate_limiting.rs
type RateLimitingConfig (line 17) | pub struct RateLimitingConfig {
method is_default (line 182) | pub(crate) fn is_default(config: &RateLimitingConfig) -> bool {
type LoginRateLimitingConfig (line 37) | pub struct LoginRateLimitingConfig {
type AccountRecoveryRateLimitingConfig (line 60) | pub struct AccountRecoveryRateLimitingConfig {
type EmailauthenticationRateLimitingConfig (line 79) | pub struct EmailauthenticationRateLimitingConfig {
type RateLimiterConfiguration (line 108) | pub struct RateLimiterConfiguration {
method to_quota (line 188) | pub fn to_quota(self) -> Option<Quota> {
constant PATH (line 118) | const PATH: Option<&'static str> = Some("rate_limiting");
method validate (line 120) | fn validate(
function default_login_per_ip (line 197) | fn default_login_per_ip() -> RateLimiterConfiguration {
function default_login_per_account (line 204) | fn default_login_per_account() -> RateLimiterConfiguration {
function default_registration (line 211) | fn default_registration() -> RateLimiterConfiguration {
function default_account_recovery_per_ip (line 218) | fn default_account_recovery_per_ip() -> RateLimiterConfiguration {
function default_account_recovery_per_address (line 225) | fn default_account_recovery_per_address() -> RateLimiterConfiguration {
function default_email_authentication_per_ip (line 232) | fn default_email_authentication_per_ip() -> RateLimiterConfiguration {
function default_email_authentication_per_address (line 239) | fn default_email_authentication_per_address() -> RateLimiterConfiguration {
function default_email_authentication_emails_per_session (line 246) | fn default_email_authentication_emails_per_session() -> RateLimiterConfi...
function default_email_authentication_attempt_per_session (line 253) | fn default_email_authentication_attempt_per_session() -> RateLimiterConf...
method default (line 261) | fn default() -> Self {
method default (line 272) | fn default() -> Self {
method default (line 281) | fn default() -> Self {
method default (line 290) | fn default() -> Self {
FILE: crates/config/src/sections/secrets.rs
type Password (line 28) | pub enum Password {
type PasswordRaw (line 35) | struct PasswordRaw {
method from (line 57) | fn from(value: Option<Password>) -> Self {
type Error (line 44) | type Error = anyhow::Error;
function try_from (line 46) | fn try_from(value: PasswordRaw) -> Result<Self, Self::Error> {
type Key (line 80) | pub enum Key {
type Error (line 96) | type Error = anyhow::Error;
method try_from (line 98) | fn try_from(value: KeyRaw) -> Result<Key, Self::Error> {
type KeyRaw (line 87) | struct KeyRaw {
method from (line 109) | fn from(value: Key) -> Self {
type KeyConfig (line 126) | pub struct KeyConfig {
method password (line 148) | async fn password(&self) -> anyhow::Result<Option<Cow<'_, [u8]>>> {
method key (line 159) | async fn key(&self) -> anyhow::Result<Cow<'_, [u8]>> {
method json_web_key (line 169) | async fn json_web_key(&self) -> anyhow::Result<JsonWebKey<mas_keystore...
type Encryption (line 190) | pub enum Encryption {
type Error (line 216) | type Error = anyhow::Error;
method try_from (line 218) | fn try_from(value: EncryptionRaw) -> Result<Encryption, Self::Error> {
type EncryptionRaw (line 198) | struct EncryptionRaw {
method from (line 229) | fn from(value: Encryption) -> Self {
function key_configs_from_path (line 244) | async fn key_configs_from_path(path: &Utf8PathBuf) -> anyhow::Result<Vec...
type SecretsConfig (line 263) | pub struct SecretsConfig {
method key_store (line 287) | pub async fn key_store(&self) -> anyhow::Result<Keystore> {
method encrypter (line 299) | pub async fn encrypter(&self) -> anyhow::Result<Encrypter> {
method encryption (line 308) | pub async fn encryption(&self) -> anyhow::Result<[u8; 32]> {
method key_configs (line 327) | async fn key_configs(&self) -> anyhow::Result<Vec<KeyConfig>> {
method generate (line 347) | pub(crate) async fn generate<R>(mut rng: R) -> anyhow::Result<Self>
method test (line 424) | pub(crate) fn test() -> Self {
constant PATH (line 341) | const PATH: Option<&'static str> = Some("secrets");
function load_config (line 479) | async fn load_config() {
function load_config_inline_secrets (line 605) | async fn load_config_inline_secrets() {
function load_config_mixed_key_sources (line 658) | async fn load_config_mixed_key_sources() {
FILE: crates/config/src/sections/telemetry.rs
type Propagator (line 17) | pub enum Propagator {
function otlp_endpoint_default (line 29) | fn otlp_endpoint_default() -> Option<String> {
type TracingExporterKind (line 37) | pub enum TracingExporterKind {
type TracingConfig (line 51) | pub struct TracingConfig {
method is_default (line 75) | fn is_default(&self) -> bool {
type MetricsExporterKind (line 86) | pub enum MetricsExporterKind {
type MetricsConfig (line 104) | pub struct MetricsConfig {
method is_default (line 117) | fn is_default(&self) -> bool {
type SentryConfig (line 124) | pub struct SentryConfig {
method is_default (line 154) | fn is_default(&self) -> bool {
type TelemetryConfig (line 161) | pub struct TelemetryConfig {
method is_default (line 177) | pub(crate) fn is_default(&self) -> bool {
constant PATH (line 183) | const PATH: Option<&'static str> = Some("telemetry");
method validate (line 185) | fn validate(
FILE: crates/config/src/sections/templates.rs
function default_path (line 14) | fn default_path() -> Utf8PathBuf {
function default_path (line 19) | fn default_path() -> Utf8PathBuf {
function default_path (line 24) | fn default_path() -> Utf8PathBuf {
function is_default_path (line 28) | fn is_default_path(value: &Utf8PathBuf) -> bool {
function default_assets_path (line 33) | fn default_assets_path() -> Utf8PathBuf {
function default_assets_path (line 38) | fn default_assets_path() -> Utf8PathBuf {
function default_assets_path (line 43) | fn default_assets_path() -> Utf8PathBuf {
function is_default_assets_path (line 47) | fn is_default_assets_path(value: &Utf8PathBuf) -> bool {
function default_translations_path (line 52) | fn default_translations_path() -> Utf8PathBuf {
function default_translations_path (line 57) | fn default_translations_path() -> Utf8PathBuf {
function default_translations_path (line 62) | fn default_translations_path() -> Utf8PathBuf {
function is_default_translations_path (line 66) | fn is_default_translations_path(value: &Utf8PathBuf) -> bool {
type TemplatesConfig (line 72) | pub struct TemplatesConfig {
method is_default (line 107) | pub(crate) fn is_default(&self) -> bool {
method default (line 96) | fn default() -> Self {
constant PATH (line 115) | const PATH: Option<&'static str> = Some("templates");
FILE: crates/config/src/sections/upstream_oauth2.rs
type UpstreamOAuth2Config (line 21) | pub struct UpstreamOAuth2Config {
method is_default (line 28) | pub(crate) fn is_default(&self) -> bool {
constant PATH (line 34) | const PATH: Option<&'static str> = Some("upstream_oauth2");
method validate (line 36) | fn validate(
type ResponseMode (line 161) | pub enum ResponseMode {
type TokenAuthMethod (line 176) | pub enum TokenAuthMethod {
type ImportAction (line 203) | pub enum ImportAction {
method is_default (line 220) | const fn is_default(&self) -> bool {
type OnConflict (line 228) | pub enum OnConflict {
method is_default (line 247) | const fn is_default(&self) -> bool {
type SubjectImportPreference (line 254) | pub struct SubjectImportPreference {
method is_default (line 263) | const fn is_default(&self) -> bool {
type LocalpartImportPreference (line 270) | pub struct LocalpartImportPreference {
method is_default (line 287) | const fn is_default(&self) -> bool {
type DisplaynameImportPreference (line 294) | pub struct DisplaynameImportPreference {
method is_default (line 307) | const fn is_default(&self) -> bool {
type EmailImportPreference (line 314) | pub struct EmailImportPreference {
method is_default (line 327) | const fn is_default(&self) -> bool {
type AccountNameImportPreference (line 334) | pub struct AccountNameImportPreference {
method is_default (line 344) | const fn is_default(&self) -> bool {
type ClaimsImports (line 351) | pub struct ClaimsImports {
method is_default (line 387) | const fn is_default(&self) -> bool {
type DiscoveryMode (line 400) | pub enum DiscoveryMode {
method is_default (line 414) | const fn is_default(&self) -> bool {
type PkceMethod (line 423) | pub enum PkceMethod {
method is_default (line 439) | const fn is_default(&self) -> bool {
function default_true (line 444) | fn default_true() -> bool {
function is_default_true (line 449) | fn is_default_true(value: &bool) -> bool {
function is_signed_response_alg_default (line 454) | fn is_signed_response_alg_default(signed_response_alg: &JsonWebSignature...
function signed_response_alg_default (line 459) | fn signed_response_alg_default() -> JsonWebSignatureAlg {
type SignInWithApple (line 464) | pub struct SignInWithApple {
function default_scope (line 481) | fn default_scope() -> String {
function is_default_scope (line 485) | fn is_default_scope(scope: &str) -> bool {
type OnBackchannelLogout (line 492) | pub enum OnBackchannelLogout {
method is_default (line 507) | const fn is_default(&self) -> bool {
type Provider (line 516) | pub struct Provider {
method client_secret (line 706) | pub async fn client_secret(&self) -> anyhow::Result<Option<String>> {
function load_config (line 727) | async fn load_config() {
FILE: crates/config/src/util.rs
type ConfigurationSection (line 12) | pub trait ConfigurationSection: Sized + DeserializeOwned {
constant PATH (line 14) | const PATH: Option<&'static str> = None;
method validate (line 21) | fn validate(
method extract (line 33) | fn extract(
type ConfigurationSectionExt (line 50) | pub trait ConfigurationSectionExt: ConfigurationSection + Default {
method extract_or_default (line 57) | fn extract_or_default(
FILE: crates/context/src/fmt.rs
type EventFormatter (line 25) | pub struct EventFormatter;
method format_event (line 92) | fn format_event(
type FmtLevel (line 27) | struct FmtLevel<'a> {
function new (line 33) | pub(crate) fn new(level: &'a Level, ansi: bool) -> Self {
constant TRACE_STR (line 38) | const TRACE_STR: &str = "TRACE";
constant DEBUG_STR (line 39) | const DEBUG_STR: &str = "DEBUG";
constant INFO_STR (line 40) | const INFO_STR: &str = " INFO";
constant WARN_STR (line 41) | const WARN_STR: &str = " WARN";
constant ERROR_STR (line 42) | const ERROR_STR: &str = "ERROR";
constant TRACE_STYLE (line 44) | const TRACE_STYLE: Style = Style::new().fg(Color::Magenta);
constant DEBUG_STYLE (line 45) | const DEBUG_STYLE: Style = Style::new().fg(Color::Blue);
constant INFO_STYLE (line 46) | const INFO_STYLE: Style = Style::new().fg(Color::Green);
constant WARN_STYLE (line 47) | const WARN_STYLE: Style = Style::new().fg(Color::Yellow);
constant ERROR_STYLE (line 48) | const ERROR_STYLE: Style = Style::new().fg(Color::Red);
function fmt (line 51) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type TargetFmt (line 63) | struct TargetFmt<'a> {
function new (line 69) | pub(crate) fn new(metadata: &tracing::Metadata<'a>) -> Self {
function fmt (line 78) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
FILE: crates/context/src/future.rs
type LogContextFuture (line 17) | pub type LogContextFuture<F> = TaskLocalFuture<crate::LogContext, PollRe...
method wrap_future (line 21) | pub(crate) fn wrap_future<F: Future>(&self, future: F) -> LogContextFutu...
function new (line 37) | pub(crate) fn new(inner: F) -> Self {
type Output (line 43) | type Output = F::Output;
method poll (line 45) | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
FILE: crates/context/src/layer.rs
type LogContextLayer (line 14) | pub struct LogContextLayer<R> {
method clone (line 19) | fn clone(&self) -> Self {
function new (line 27) | pub fn new(tagger: fn(&R) -> Cow<'static, str>) -> Self {
type Service (line 36) | type Service = LogContextService<S, R>;
function layer (line 38) | fn layer(&self, inner: S) -> Self::Service {
FILE: crates/context/src/lib.rs
type LogContext (line 40) | pub struct LogContext {
method new (line 64) | pub fn new(tag: impl Into<Cow<'static, str>>) -> Self {
method maybe_with (line 80) | pub fn maybe_with<F, R>(f: F) -> Option<R>
method run (line 89) | pub fn run<F: FnOnce() -> Fut, Fut: Future>(&self, f: F) -> LogContext...
method run_sync (line 96) | pub fn run_sync<F: FnOnce() -> R, R>(&self, f: F) -> R {
method stats (line 106) | pub fn stats(&self) -> LogContextStats {
method fmt (line 120) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type LogContextInner (line 44) | struct LogContextInner {
type LogContextStats (line 129) | pub struct LogContextStats {
method fmt (line 141) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
FILE: crates/context/src/service.rs
type LogContextService (line 17) | pub struct LogContextService<S, R> {
method clone (line 23) | fn clone(&self) -> Self {
function new (line 32) | pub fn new(inner: S, tagger: fn(&R) -> Cow<'static, str>) -> Self {
type Response (line 41) | type Response = S::Response;
type Error (line 42) | type Error = S::Error;
type Future (line 43) | type Future = LogContextFuture<S::Future>;
function poll_ready (line 45) | fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::...
function call (line 49) | fn call(&mut self, req: R) -> Self::Future {
FILE: crates/data-model/examples/ua-parser.rs
function main (line 12) | fn main() {
FILE: crates/data-model/src/clock.rs
type Clock (line 18) | pub trait Clock: Send + Sync {
method now (line 20) | fn now(&self) -> DateTime<Utc>;
method now (line 24) | fn now(&self) -> DateTime<Utc> {
method now (line 30) | fn now(&self) -> DateTime<Utc> {
method now (line 42) | fn now(&self) -> DateTime<Utc> {
method now (line 78) | fn now(&self) -> DateTime<Utc> {
type SystemClock (line 37) | pub struct SystemClock {
type MockClock (line 51) | pub struct MockClock {
method new (line 65) | pub fn new(datetime: DateTime<Utc>) -> Self {
method advance (line 71) | pub fn advance(&self, duration: chrono::Duration) {
method default (line 56) | fn default() -> Self {
function test_mocked_clock (line 91) | fn test_mocked_clock() {
function test_real_clock (line 108) | fn test_real_clock() {
FILE: crates/data-model/src/compat/device.rs
type Device (line 21) | pub struct Device {
method to_scope_token (line 38) | pub fn to_scope_token(&self) -> Result<[ScopeToken; 2], ToScopeTokenEr...
method from_scope_token (line 53) | pub fn from_scope_token(token: &ScopeToken) -> Option<Self> {
method generate (line 61) | pub fn generate<R: RngCore + ?Sized>(rng: &mut R) -> Self {
method as_str (line 68) | pub fn as_str(&self) -> &str {
method from (line 74) | fn from(id: String) -> Self {
method fmt (line 86) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type ToScopeTokenError (line 26) | pub enum ToScopeTokenError {
method from (line 80) | fn from(device: Device) -> Self {
function test_device_id_to_from_scope_token (line 98) | fn test_device_id_to_from_scope_token() {
FILE: crates/data-model/src/compat/mod.rs
type CompatAccessToken (line 22) | pub struct CompatAccessToken {
method is_valid (line 32) | pub fn is_valid(&self, now: DateTime<Utc>) -> bool {
type CompatRefreshTokenState (line 42) | pub enum CompatRefreshTokenState {
method is_valid (line 55) | pub fn is_valid(&self) -> bool {
method is_consumed (line 63) | pub fn is_consumed(&self) -> bool {
method consume (line 72) | pub fn consume(self, consumed_at: DateTime<Utc>) -> Result<Self, Inval...
type CompatRefreshToken (line 81) | pub struct CompatRefreshToken {
type Target (line 91) | type Target = CompatRefreshTokenState;
method deref (line 93) | fn deref(&self) -> &Self::Target {
method consume (line 104) | pub fn consume(mut self, consumed_at: DateTime<Utc>) -> Result<Self, I...
FILE: crates/data-model/src/compat/session.rs
type CompatSessionState (line 17) | pub enum CompatSessionState {
method is_valid (line 30) | pub fn is_valid(&self) -> bool {
method is_finished (line 38) | pub fn is_finished(&self) -> bool {
method finish (line 53) | pub fn finish(self, finished_at: DateTime<Utc>) -> Result<Self, Invali...
method finished_at (line 61) | pub fn finished_at(&self) -> Option<DateTime<Utc>> {
type CompatSession (line 70) | pub struct CompatSession {
type Target (line 85) | type Target = CompatSessionState;
method deref (line 87) | fn deref(&self) -> &Self::Target {
method finish (line 102) | pub fn finish(mut self, finished_at: DateTime<Utc>) -> Result<Self, In...
FILE: crates/data-model/src/compat/sso_login.rs
type CompatSsoLoginState (line 16) | pub enum CompatSsoLoginState {
method is_pending (line 35) | pub fn is_pending(&self) -> bool {
method is_fulfilled (line 43) | pub fn is_fulfilled(&self) -> bool {
method is_exchanged (line 51) | pub fn is_exchanged(&self) -> bool {
method fulfilled_at (line 61) | pub fn fulfilled_at(&self) -> Option<DateTime<Utc>> {
method exchanged_at (line 76) | pub fn exchanged_at(&self) -> Option<DateTime<Utc>> {
method session_id (line 91) | pub fn session_id(&self) -> Option<Ulid> {
method fulfill (line 109) | pub fn fulfill(
method exchange (line 132) | pub fn exchange(
type CompatSsoLogin (line 152) | pub struct CompatSsoLogin {
type Target (line 161) | type Target = CompatSsoLoginState;
method deref (line 163) | fn deref(&self) -> &Self::Target {
method fulfill (line 178) | pub fn fulfill(
method exchange (line 196) | pub fn exchange(
FILE: crates/data-model/src/lib.rs
type InvalidTransitionError (line 27) | pub struct InvalidTransitionError;
FILE: crates/data-model/src/oauth2/authorization_grant.rs
type Pkce (line 26) | pub struct Pkce {
method new (line 34) | pub fn new(challenge_method: PkceCodeChallengeMethod, challenge: Strin...
method verify (line 46) | pub fn verify(&self, verifier: &str) -> Result<(), CodeChallengeError> {
type AuthorizationCode (line 52) | pub struct AuthorizationCode {
type AuthorizationGrantStage (line 59) | pub enum AuthorizationGrantStage {
method new (line 78) | pub fn new() -> Self {
method fulfill (line 82) | fn fulfill(
method exchange (line 96) | fn exchange(self, exchanged_at: DateTime<Utc>) -> Result<Self, Invalid...
method cancel (line 110) | fn cancel(self, cancelled_at: DateTime<Utc>) -> Result<Self, InvalidTr...
method is_pending (line 121) | pub fn is_pending(&self) -> bool {
method is_fulfilled (line 129) | pub fn is_fulfilled(&self) -> bool {
method is_exchanged (line 137) | pub fn is_exchanged(&self) -> bool {
type AuthorizationGrant (line 143) | pub struct AuthorizationGrant {
type Target (line 161) | type Target = AuthorizationGrantStage;
method deref (line 163) | fn deref(&self) -> &Self::Target {
method exchange (line 176) | pub fn exchange(mut self, exchanged_at: DateTime<Utc>) -> Result<Self,...
method fulfill (line 188) | pub fn fulfill(
method cancel (line 208) | pub fn cancel(mut self, canceld_at: DateTime<Utc>) -> Result<Self, Inv...
method sample (line 214) | pub fn sample(now: DateTime<Utc>, rng: &mut impl RngCore) -> Self {
FILE: crates/data-model/src/oauth2/client.rs
type JwksOrJwksUri (line 23) | pub enum JwksOrJwksUri {
type Client (line 32) | pub struct Client {
method resolve_redirect_uri (line 113) | pub fn resolve_redirect_uri<'a>(
method into_metadata (line 128) | pub fn into_metadata(self) -> ClientMetadata {
method samples (line 177) | pub fn samples(now: DateTime<Utc>, rng: &mut impl RngCore) -> Vec<Clie...
type InvalidRedirectUriError (line 92) | pub enum InvalidRedirectUriError {
constant LOCAL_HOSTS (line 231) | const LOCAL_HOSTS: &[&str] = &["localhost", "127.0.0.1", "[::1]"];
function uri_matches_one_of (line 237) | fn uri_matches_one_of(uri: &Url, registered_uris: &[Url]) -> bool {
function test_uri_matches_one_of (line 256) | fn test_uri_matches_one_of() {
FILE: crates/data-model/src/oauth2/device_code_grant.rs
type DeviceCodeGrantState (line 18) | pub enum DeviceCodeGrantState {
method fulfill (line 68) | pub fn fulfill(
method reject (line 90) | pub fn reject(
method exchange (line 112) | pub fn exchange(
method is_pending (line 136) | pub fn is_pending(&self) -> bool {
method is_fulfilled (line 144) | pub fn is_fulfilled(&self) -> bool {
method is_rejected (line 152) | pub fn is_rejected(&self) -> bool {
method is_exchanged (line 160) | pub fn is_exchanged(&self) -> bool {
type DeviceCodeGrant (line 166) | pub struct DeviceCodeGrant {
type Target (line 199) | type Target = DeviceCodeGrantState;
method deref (line 201) | fn deref(&self) -> &Self::Target {
method fulfill (line 215) | pub fn fulfill(
method reject (line 233) | pub fn reject(
method exchange (line 252) | pub fn exchange(
FILE: crates/data-model/src/oauth2/session.rs
type SessionState (line 17) | pub enum SessionState {
method is_valid (line 30) | pub fn is_valid(&self) -> bool {
method is_finished (line 38) | pub fn is_finished(&self) -> bool {
method finish (line 53) | pub fn finish(self, finished_at: DateTime<Utc>) -> Result<Self, Invali...
method finished_at (line 66) | pub fn finished_at(&self) -> Option<DateTime<Utc>> {
type Session (line 75) | pub struct Session {
type Target (line 90) | type Target = SessionState;
method deref (line 92) | fn deref(&self) -> &Self::Target {
method finish (line 107) | pub fn finish(mut self, finished_at: DateTime<Utc>) -> Result<Self, In...
FILE: crates/data-model/src/personal/mod.rs
type PersonalAccessToken (line 12) | pub struct PersonalAccessToken {
method is_valid (line 22) | pub fn is_valid(&self, now: DateTime<Utc>) -> bool {
FILE: crates/data-model/src/personal/session.rs
type SessionState (line 16) | pub enum SessionState {
method is_valid (line 29) | pub fn is_valid(&self) -> bool {
method is_revoked (line 37) | pub fn is_revoked(&self) -> bool {
method revoke (line 52) | pub fn revoke(self, revoked_at: DateTime<Utc>) -> Result<Self, Invalid...
method revoked_at (line 65) | pub fn revoked_at(&self) -> Option<DateTime<Utc>> {
type PersonalSession (line 74) | pub struct PersonalSession {
type Target (line 111) | type Target = SessionState;
method deref (line 113) | fn deref(&self) -> &Self::Target {
method finish (line 128) | pub fn finish(mut self, revoked_at: DateTime<Utc>) -> Result<Self, Inv...
method has_device (line 136) | pub fn has_device(&self) -> bool {
type PersonalSessionOwner (line 90) | pub enum PersonalSessionOwner {
method from (line 99) | fn from(value: &'a User) -> Self {
method from (line 105) | fn from(value: &'a Client) -> Self {
FILE: crates/data-model/src/policy_data.rs
type PolicyData (line 11) | pub struct PolicyData {
FILE: crates/data-model/src/site_config.rs
type CaptchaService (line 15) | pub enum CaptchaService {
type CaptchaConfig (line 23) | pub struct CaptchaConfig {
type SessionExpirationConfig (line 36) | pub struct SessionExpirationConfig {
type SessionLimitConfig (line 44) | pub struct SessionLimitConfig {
type SiteConfig (line 54) | pub struct SiteConfig {
FILE: crates/data-model/src/tokens.rs
type AccessTokenState (line 18) | pub enum AccessTokenState {
method revoke (line 27) | fn revoke(self, revoked_at: DateTime<Utc>) -> Result<Self, InvalidTran...
method is_valid (line 38) | pub fn is_valid(&self) -> bool {
method is_revoked (line 46) | pub fn is_revoked(&self) -> bool {
type AccessToken (line 52) | pub struct AccessToken {
method jti (line 64) | pub fn jti(&self) -> String {
method is_valid (line 74) | pub fn is_valid(&self, now: DateTime<Utc>) -> bool {
method is_expired (line 86) | pub fn is_expired(&self, now: DateTime<Utc>) -> bool {
method is_used (line 95) | pub fn is_used(&self) -> bool {
method revoke (line 108) | pub fn revoke(mut self, revoked_at: DateTime<Utc>) -> Result<Self, Inv...
type RefreshTokenState (line 115) | pub enum RefreshTokenState {
method consume (line 133) | fn consume(
method revoke (line 152) | pub fn revoke(self, revoked_at: DateTime<Utc>) -> Result<Self, Invalid...
method is_valid (line 163) | pub fn is_valid(&self) -> bool {
method next_refresh_token_id (line 169) | pub fn next_refresh_token_id(&self) -> Option<Ulid> {
type RefreshToken (line 181) | pub struct RefreshToken {
type Target (line 191) | type Target = RefreshTokenState;
method deref (line 193) | fn deref(&self) -> &Self::Target {
method jti (line 200) | pub fn jti(&self) -> String {
method consume (line 209) | pub fn consume(
method revoke (line 223) | pub fn revoke(mut self, revoked_at: DateTime<Utc>) -> Result<Self, Inv...
type TokenType (line 231) | pub enum TokenType {
method fmt (line 249) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method prefix (line 261) | fn prefix(self) -> &'static str {
method match_prefix (line 271) | fn match_prefix(prefix: &str) -> Option<Self> {
method generate (line 283) | pub fn generate(self, rng: &mut (impl RngCore + ?Sized)) -> String {
method check (line 301) | pub fn check(token: &str) -> Result<TokenType, TokenFormatError> {
method eq (line 340) | fn eq(&self, other: &OAuthTokenTypeHint) -> bool {
function is_likely_synapse_macaroon (line 363) | fn is_likely_synapse_macaroon(token: &str) -> bool {
constant NUM (line 370) | const NUM: [u8; 62] = *b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk...
function base62_encode (line 372) | fn base62_encode(mut num: u32) -> String {
constant CRC (line 382) | const CRC: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
type TokenFormatError (line 386) | pub enum TokenFormatError {
function test_prefix_match (line 417) | fn test_prefix_match() {
function test_is_likely_synapse_macaroon (line 447) | fn test_is_likely_synapse_macaroon() {
function test_generate_and_check (line 468) | fn test_generate_and_check() {
FILE: crates/data-model/src/upstream_oauth2/link.rs
type UpstreamOAuthLink (line 12) | pub struct UpstreamOAuthLink {
FILE: crates/data-model/src/upstream_oauth2/provider.rs
type DiscoveryMode (line 17) | pub enum DiscoveryMode {
method is_disabled (line 32) | pub fn is_disabled(&self) -> bool {
type Err (line 42) | type Err = InvalidDiscoveryModeError;
method from_str (line 44) | fn from_str(s: &str) -> Result<Self, Self::Err> {
method as_str (line 56) | pub fn as_str(self) -> &'static str {
method fmt (line 66) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type InvalidDiscoveryModeError (line 39) | pub struct InvalidDiscoveryModeError(String);
type PkceMode (line 73) | pub enum PkceMode {
type Err (line 90) | type Err = InvalidPkceModeError;
method from_str (line 92) | fn from_str(s: &str) -> Result<Self, Self::Err> {
method as_str (line 104) | pub fn as_str(self) -> &'static str {
method fmt (line 114) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type InvalidPkceModeError (line 87) | pub struct InvalidPkceModeError(String);
type InvalidResponseModeError (line 121) | pub struct InvalidResponseModeError(String);
type ResponseMode (line 125) | pub enum ResponseMode {
method as_str (line 142) | pub fn as_str(self) -> &'static str {
method fmt (line 151) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Err (line 157) | type Err = InvalidResponseModeError;
method from_str (line 159) | fn from_str(s: &str) -> Result<Self, Self::Err> {
function from (line 132) | fn from(value: ResponseMode) -> Self {
type TokenAuthMethod (line 170) | pub enum TokenAuthMethod {
method as_str (line 181) | pub fn as_str(self) -> &'static str {
method fmt (line 194) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Err (line 200) | type Err = InvalidUpstreamOAuth2TokenAuthMethod;
method from_str (line 202) | fn from_str(s: &str) -> Result<Self, Self::Err> {
type InvalidUpstreamOAuth2TokenAuthMethod (line 217) | pub struct InvalidUpstreamOAuth2TokenAuthMethod(String);
type OnBackchannelLogout (line 221) | pub enum OnBackchannelLogout {
method as_str (line 229) | pub fn as_str(self) -> &'static str {
method fmt (line 239) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Err (line 245) | type Err = InvalidUpstreamOAuth2OnBackchannelLogout;
method from_str (line 247) | fn from_str(s: &str) -> Result<Self, Self::Err> {
type InvalidUpstreamOAuth2OnBackchannelLogout (line 259) | pub struct InvalidUpstreamOAuth2OnBackchannelLogout(String);
type UpstreamOAuthProvider (line 262) | pub struct UpstreamOAuthProvider {
method enabled (line 305) | pub const fn enabled(&self) -> bool {
method partial_cmp (line 291) | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
method cmp (line 297) | fn cmp(&self, other: &Self) -> std::cmp::Ordering {
type ClaimsImports (line 311) | pub struct ClaimsImports {
type SubjectPreference (line 333) | pub struct SubjectPreference {
type LocalpartPreference (line 339) | pub struct LocalpartPreference {
type Target (line 351) | type Target = ImportAction;
method deref (line 353) | fn deref(&self) -> &Self::Target {
type ImportPreference (line 359) | pub struct ImportPreference {
type Target (line 368) | type Target = ImportAction;
method deref (line 370) | fn deref(&self) -> &Self::Target {
type ImportAction (line 377) | pub enum ImportAction {
method is_forced_or_required (line 394) | pub fn is_forced_or_required(&self) -> bool {
method ignore (line 399) | pub fn ignore(&self) -> bool {
method is_required (line 404) | pub fn is_required(&self) -> bool {
method should_import (line 409) | pub fn should_import(&self, user_preference: bool) -> bool {
type OnConflict (line 420) | pub enum OnConflict {
FILE: crates/data-model/src/upstream_oauth2/session.rs
type UpstreamOAuthAuthorizationSessionState (line 15) | pub enum UpstreamOAuthAuthorizationSessionState {
method complete (line 53) | pub fn complete(
method consume (line 85) | pub fn consume(self, consumed_at: DateTime<Utc>) -> Result<Self, Inval...
method link_id (line 116) | pub fn link_id(&self) -> Option<Ulid> {
method completed_at (line 131) | pub fn completed_at(&self) -> Option<DateTime<Utc>> {
method id_token (line 147) | pub fn id_token(&self) -> Option<&str> {
method id_token_claims (line 164) | pub fn id_token_claims(&self) -> Option<&serde_json::Value> {
method extra_callback_parameters (line 186) | pub fn extra_callback_parameters(&self) -> Option<&serde_json::Value> {
method userinfo (line 201) | pub fn userinfo(&self) -> Option<&serde_json::Value> {
method consumed_at (line 216) | pub fn consumed_at(&self) -> Option<DateTime<Utc>> {
method unlinked_at (line 232) | pub fn unlinked_at(&self) -> Option<DateTime<Utc>> {
method is_pending (line 244) | pub fn is_pending(&self) -> bool {
method is_completed (line 253) | pub fn is_completed(&self) -> bool {
method is_consumed (line 262) | pub fn is_consumed(&self) -> bool {
method is_unlinked (line 271) | pub fn is_unlinked(&self) -> bool {
type UpstreamOAuthAuthorizationSession (line 277) | pub struct UpstreamOAuthAuthorizationSession {
type Target (line 288) | type Target = UpstreamOAuthAuthorizationSessionState;
method deref (line 290) | fn deref(&self) -> &Self::Target {
method complete (line 305) | pub fn complete(
method consume (line 334) | pub fn consume(mut self, consumed_at: DateTime<Utc>) -> Result<Self, I...
FILE: crates/data-model/src/user_agent.rs
type DeviceType (line 21) | pub enum DeviceType {
type UserAgent (line 29) | pub struct UserAgent {
type Target (line 40) | type Target = str;
method deref (line 42) | fn deref(&self) -> &Self::Target {
method parse_custom (line 48) | fn parse_custom(user_agent: &str) -> Option<(&str, &str, &str, &str, O...
method parse_electron (line 81) | fn parse_electron(user_agent: &str) -> Option<(&str, &str)> {
method parse (line 90) | pub fn parse(user_agent: String) -> Self {
FILE: crates/data-model/src/users.rs
type MatrixUser (line 16) | pub struct MatrixUser {
type User (line 22) | pub struct User {
method is_valid (line 36) | pub fn is_valid(&self) -> bool {
method is_valid_actor (line 50) | pub fn is_valid_actor(&self) -> bool {
method samples (line 58) | pub fn samples(now: chrono::DateTime<Utc>, rng: &mut impl Rng) -> Vec<...
type Password (line 73) | pub struct Password {
type Authentication (line 82) | pub struct Authentication {
type AuthenticationMethod (line 89) | pub enum AuthenticationMethod {
type UserRecoverySession (line 101) | pub struct UserRecoverySession {
type UserRecoveryTicket (line 117) | pub struct UserRecoveryTicket {
method active (line 128) | pub fn active(&self, now: DateTime<Utc>) -> bool {
type UserEmailAuthentication (line 135) | pub struct UserEmailAuthentication {
type UserEmailAuthenticationCode (line 146) | pub struct UserEmailAuthenticationCode {
type BrowserSession (line 155) | pub struct BrowserSession {
method active (line 167) | pub fn active(&self) -> bool {
method samples (line 174) | pub fn samples(now: chrono::DateTime<Utc>, rng: &mut impl Rng) -> Vec<...
type UserEmail (line 193) | pub struct UserEmail {
method samples (line 202) | pub fn samples(now: chrono::DateTime<Utc>, rng: &mut impl Rng) -> Vec<...
type UserRegistrationPassword (line 221) | pub struct UserRegistrationPassword {
type UserRegistrationToken (line 227) | pub struct UserRegistrationToken {
method is_valid (line 241) | pub fn is_valid(&self, now: DateTime<Utc>) -> bool {
method can_be_used (line 267) | pub fn can_be_used(&self, now: DateTime<Utc>) -> bool {
type UserRegistration (line 273) | pub struct UserRegistration {
FILE: crates/data-model/src/utils.rs
type BoxClock (line 11) | pub type BoxClock = Box<dyn Clock + Send>;
type BoxRng (line 13) | pub type BoxRng = Box<dyn CryptoRngCore + Send>;
FILE: crates/data-model/src/version.rs
type AppVersion (line 8) | pub struct AppVersion(pub &'static str);
FILE: crates/email/src/mailer.rs
type Mailer (line 20) | pub struct Mailer {
method new (line 38) | pub fn new(
method base_message (line 52) | fn base_message(&self) -> MessageBuilder {
method prepare_verification_email (line 61) | fn prepare_verification_email(
method prepare_recovery_email (line 83) | fn prepare_recovery_email(
method send_verification_email (line 118) | pub async fn send_verification_email(
method send_recovery_email (line 143) | pub async fn send_recovery_email(
method test_connection (line 159) | pub async fn test_connection(&self) -> Result<(), crate::transport::Er...
type Error (line 29) | pub enum Error {
FILE: crates/email/src/transport.rs
type SmtpMode (line 24) | pub enum SmtpMode {
type Transport (line 35) | pub struct Transport {
method new (line 48) | fn new(inner: TransportInner) -> Self {
method blackhole (line 55) | pub fn blackhole() -> Self {
method smtp (line 64) | pub fn smtp(
method sendmail (line 89) | pub fn sendmail(command: Option<impl Into<OsString>>) -> Self {
method test_connection (line 106) | pub async fn test_connection(&self) -> Result<(), Error> {
type TransportInner (line 40) | enum TransportInner {
type Error (line 120) | pub enum Error {
type Ok (line 127) | type Ok = ();
type Error (line 128) | type Error = Error;
method send_raw (line 130) | async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Se...
FILE: crates/handlers/src/activity_tracker/bound.rs
type Bound (line 17) | pub struct Bound {
method new (line 25) | pub fn new(tracker: ActivityTracker, ip: Option<IpAddr>) -> Self {
method ip (line 31) | pub fn ip(&self) -> Option<IpAddr> {
method record_oauth2_session (line 36) | pub async fn record_oauth2_session(&self, clock: &dyn Clock, session: ...
method record_personal_session (line 43) | pub async fn record_personal_session(&self, clock: &dyn Clock, session...
method record_compat_session (line 50) | pub async fn record_compat_session(&self, clock: &dyn Clock, session: ...
method record_browser_session (line 57) | pub async fn record_browser_session(&self, clock: &dyn Clock, session:...
FILE: crates/handlers/src/activity_tracker/mod.rs
type SessionKind (line 26) | enum SessionKind {
method as_str (line 35) | const fn as_str(self) -> &'static str {
type Message (line 45) | enum Message {
type ActivityTracker (line 56) | pub struct ActivityTracker {
method new (line 67) | pub fn new(
method bind (line 90) | pub fn bind(self, ip: Option<IpAddr>) -> Bound {
method record_oauth2_session (line 95) | pub async fn record_oauth2_session(
method record_personal_session (line 117) | pub async fn record_personal_session(
method record_compat_session (line 139) | pub async fn record_compat_session(
method record_browser_session (line 161) | pub async fn record_browser_session(
method flush (line 183) | pub async fn flush(&self) {
method flush_loop (line 206) | async fn flush_loop(
FILE: crates/handlers/src/activity_tracker/worker.rs
constant TYPE (line 32) | const TYPE: Key = Key::from_static_str("type");
constant SESSION_KIND (line 33) | const SESSION_KIND: Key = Key::from_static_str("session_kind");
constant RESULT (line 34) | const RESULT: Key = Key::from_static_str("result");
type ActivityRecord (line 37) | struct ActivityRecord {
type Worker (line 46) | pub struct Worker {
method new (line 55) | pub(crate) fn new(repository_factory: BoxRepositoryFactory) -> Self {
method run (line 101) | pub(super) async fn run(
method flush (line 189) | async fn flush(&mut self) {
method try_flush (line 220) | async fn try_flush(&mut self) -> Result<(), RepositoryError> {
FILE: crates/handlers/src/admin/call_context.rs
type Rejection (line 31) | pub enum Rejection {
method into_response (line 82) | fn into_response(self) -> Response {
type CallContext (line 121) | pub struct CallContext {
type Rejection (line 137) | type Rejection = Rejection;
method from_request_parts (line 139) | async fn from_request_parts(
type CallerSession (line 300) | pub enum CallerSession {
method scope (line 306) | pub fn scope(&self) -> &Scope {
method user_id (line 313) | pub fn user_id(&self) -> Option<Ulid> {
FILE: crates/handlers/src/admin/mod.rs
function finish (line 45) | fn finish(t: TransformOpenApi) -> TransformOpenApi {
function oauth_security_scheme (line 114) | fn oauth_security_scheme(url_builder: Option<&UrlBuilder>) -> SecuritySc...
function router (line 157) | pub fn router<S>() -> (OpenApi, Router<S>)
function swagger (line 235) | async fn swagger(
function swagger_callback (line 244) | async fn swagger_callback(
FILE: crates/handlers/src/admin/model.rs
type Resource (line 24) | pub trait Resource {
constant KIND (line 26) | const KIND: &'static str;
constant PATH (line 29) | const PATH: &'static str;
method id (line 32) | fn id(&self) -> Ulid;
method path (line 37) | fn path(&self) -> String {
constant KIND (line 117) | const KIND: &'static str = "user";
constant PATH (line 118) | const PATH: &'static str = "/api/admin/v1/users";
method id (line 120) | fn id(&self) -> Ulid {
constant KIND (line 143) | const KIND: &'static str = "user-email";
constant PATH (line 144) | const PATH: &'static str = "/api/admin/v1/user-emails";
method id (line 146) | fn id(&self) -> Ulid {
constant KIND (line 243) | const KIND: &'static str = "compat-session";
constant PATH (line 244) | const PATH: &'static str = "/api/admin/v1/compat-sessions";
method id (line 246) | fn id(&self) -> Ulid {
constant KIND (line 403) | const KIND: &'static str = "oauth2-session";
constant PATH (line 404) | const PATH: &'static str = "/api/admin/v1/oauth2-sessions";
method id (line 406) | fn id(&self) -> Ulid {
constant KIND (line 487) | const KIND: &'static str = "user-session";
constant PATH (line 488) | const PATH: &'static str = "/api/admin/v1/user-sessions";
method id (line 490) | fn id(&self) -> Ulid {
constant KIND (line 520) | const KIND: &'static str = "upstream-oauth-link";
constant PATH (line 521) | const PATH: &'static str = "/api/admin/v1/upstream-oauth-links";
method id (line 523) | fn id(&self) -> Ulid {
constant KIND (line 597) | const KIND: &'static str = "policy-data";
constant PATH (line 598) | const PATH: &'static str = "/api/admin/v1/policy-data";
method id (line 600) | fn id(&self) -> Ulid {
constant KIND (line 668) | const KIND: &'static str = "user-registration_token";
constant PATH (line 669) | const PATH: &'static str = "/api/admin/v1/user-registration-tokens";
method id (line 671) | fn id(&self) -> Ulid {
constant KIND (line 742) | const KIND: &'static str = "upstream-oauth-provider";
constant PATH (line 743) | const PATH: &'static str = "/api/admin/v1/upstream-oauth-providers";
method id (line 745) | fn id(&self) -> Ulid {
constant KIND (line 891) | const KIND: &'static str = "personal-session";
constant PATH (line 892) | const PATH: &'static str = "/api/admin/v1/personal-sessions";
method id (line 894) | fn id(&self) -> Ulid {
type User (line 44) | pub struct User {
method samples (line 69) | pub fn samples() -> [Self; 3] {
method from (line 103) | fn from(user: mas_data_model::User) -> Self {
type UserEmail (line 127) | pub struct UserEmail {
method from (line 152) | fn from(value: mas_data_model::UserEmail) -> Self {
method samples (line 163) | pub fn samples() -> [Self; 1] {
type CompatSession (line 175) | pub struct CompatSession {
method from (line 219) | fn from(
method samples (line 252) | pub fn samples() -> [Self; 3] {
type OAuth2Session (line 299) | pub struct OAuth2Session {
method from (line 338) | fn from(session: mas_data_model::Session) -> Self {
method samples (line 357) | pub fn samples() -> [Self; 3] {
type UserSession (line 413) | pub struct UserSession {
method from (line 438) | fn from(value: mas_data_model::BrowserSession) -> Self {
method samples (line 453) | pub fn samples() -> [Self; 3] {
type UpstreamOAuthLink (line 497) | pub struct UpstreamOAuthLink {
method from (line 529) | fn from(value: mas_data_model::UpstreamOAuthLink) -> Self {
method samples (line 543) | pub fn samples() -> [Self; 3] {
type PolicyData (line 575) | pub struct PolicyData {
method from (line 587) | fn from(policy_data: mas_data_model::PolicyData) -> Self {
method samples (line 607) | pub fn samples() -> [Self; 1] {
type UserRegistrationToken (line 622) | pub struct UserRegistrationToken {
method new (line 652) | pub fn new(token: mas_data_model::UserRegistrationToken, now: DateTime...
method samples (line 678) | pub fn samples() -> [Self; 2] {
type UpstreamOAuthProvider (line 708) | pub struct UpstreamOAuthProvider {
method from (line 729) | fn from(provider: mas_data_model::UpstreamOAuthProvider) -> Self {
method samples (line 752) | pub fn samples() -> [Self; 3] {
type InconsistentPersonalSession (line 788) | pub struct InconsistentPersonalSession {
type PersonalSession (line 796) | pub struct PersonalSession {
type Error (line 847) | type Error = InconsistentPersonalSession;
method try_from (line 849) | fn try_from(
method samples (line 901) | pub fn samples() -> [Self; 3] {
method with_token (line 952) | pub fn with_token(mut self, access_token: String) -> Self {
FILE: crates/handlers/src/admin/params.rs
type UlidPathParamRejection (line 30) | pub struct UlidPathParamRejection(#[from] PathRejection);
method into_response (line 33) | fn into_response(self) -> axum::response::Response {
type UlidInPath (line 43) | struct UlidInPath {
type UlidPathParam (line 52) | pub struct UlidPathParam(#[from_request(via(Path))] UlidInPath);
type Target (line 55) | type Target = Ulid;
method deref (line 57) | fn deref(&self) -> &Self::Target {
constant DEFAULT_PAGE_SIZE (line 63) | const DEFAULT_PAGE_SIZE: usize = 10;
type IncludeCount (line 66) | pub enum IncludeCount {
method add_to_base (line 82) | pub(crate) fn add_to_base(self, base: &str) -> Cow<'_, str> {
type PaginationParams (line 94) | struct PaginationParams {
type PaginationRejection (line 119) | pub enum PaginationRejection {
method into_response (line 128) | fn into_response(self) -> axum::response::Response {
type Pagination (line 140) | pub struct Pagination(pub mas_storage::Pagination, pub IncludeCount);
type Rejection (line 143) | type Rejection = PaginationRejection;
method from_request_parts (line 145) | async fn from_request_parts(
FILE: crates/handlers/src/admin/response.rs
type PaginationLinks (line 18) | struct PaginationLinks {
type PaginationMeta (line 45) | struct PaginationMeta {
method is_empty (line 52) | fn is_empty(&self) -> bool {
type PaginatedResponse (line 59) | pub struct PaginatedResponse<T> {
function url_with_pagination (line 73) | fn url_with_pagination(base: &str, pagination: Pagination) -> String {
function for_page (line 102) | pub fn for_page(
function for_count_only (line 151) | pub fn for_count_only(count: usize, base: &str) -> Self {
type SingleResource (line 170) | struct SingleResource<T> {
type SingleResourceMeta (line 193) | struct SingleResourceMeta {
method is_empty (line 200) | fn is_empty(&self) -> bool {
type SingleResourceMetaPage (line 207) | struct SingleResourceMetaPage {
function new (line 213) | fn new(resource: T) -> Self {
function from_edge (line 224) | fn from_edge<C: ToString>(edge: Edge<T, C>) -> Self {
type SelfLinks (line 234) | struct SelfLinks {
type SingleResponse (line 242) | pub struct SingleResponse<T> {
function new (line 249) | pub fn new(resource: T, self_: String) -> Self {
function new_canonical (line 257) | pub fn new_canonical(resource: T) -> Self {
type Error (line 265) | struct Error {
method from_error (line 271) | fn from_error(error: &(dyn std::error::Error + 'static)) -> Self {
type ErrorResponse (line 280) | pub struct ErrorResponse {
method from_error (line 287) | pub fn from_error(error: &(dyn std::error::Error + 'static)) -> Self {
FILE: crates/handlers/src/admin/schema.rs
type Ulid (line 16) | pub struct Ulid;
method schema_name (line 19) | fn schema_name() -> Cow<'static, str> {
method json_schema (line 23) | fn json_schema(_gen: &mut SchemaGenerator) -> Schema {
type Device (line 40) | pub struct Device;
method schema_name (line 43) | fn schema_name() -> Cow<'static, str> {
method json_schema (line 47) | fn json_schema(_gen: &mut SchemaGenerator) -> Schema {
FILE: crates/handlers/src/admin/v1/compat_sessions/finish.rs
type RouteError (line 26) | pub enum RouteError {
method into_response (line 40) | fn into_response(self) -> axum::response::Response {
function doc (line 52) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 83) | pub async fn handler(
function test_finish_session (line 136) | async fn test_finish_session(pool: PgPool) {
function test_finish_already_finished_session (line 175) | async fn test_finish_already_finished_session(pool: PgPool) {
function test_finish_unknown_session (line 226) | async fn test_finish_unknown_session(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/compat_sessions/get.rs
type RouteError (line 24) | pub enum RouteError {
method into_response (line 35) | fn into_response(self) -> axum::response::Response {
function doc (line 47) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 66) | pub async fn handler(
function test_get (line 94) | async fn test_get(pool: PgPool) {
function test_not_found (line 151) | async fn test_not_found(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/compat_sessions/list.rs
type CompatSessionStatus (line 29) | enum CompatSessionStatus {
method fmt (line 35) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type FilterParams (line 47) | pub struct FilterParams {
method fmt (line 70) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type RouteError (line 95) | pub enum RouteError {
method into_response (line 112) | fn into_response(self) -> axum::response::Response {
function doc (line 125) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 163) | pub async fn handler(
function test_compat_session_list (line 251) | async fn test_compat_session_list(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/mod.rs
function router (line 34) | pub fn router<S>() -> ApiRouter<S>
FILE: crates/handlers/src/admin/v1/oauth2_sessions/finish.rs
type RouteError (line 26) | pub enum RouteError {
method into_response (line 40) | fn into_response(self) -> axum::response::Response {
function doc (line 52) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 83) | pub async fn handler(
function test_finish_session (line 130) | async fn test_finish_session(pool: PgPool) {
function test_finish_already_finished_session (line 160) | async fn test_finish_already_finished_session(pool: PgPool) {
function test_finish_unknown_session (line 217) | async fn test_finish_unknown_session(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/oauth2_sessions/get.rs
type RouteError (line 25) | pub enum RouteError {
method into_response (line 36) | fn into_response(self) -> axum::response::Response {
function doc (line 47) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 66) | pub async fn handler(
function test_get (line 91) | async fn test_get(pool: PgPool) {
function test_not_found (line 142) | async fn test_not_found(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/oauth2_sessions/list.rs
type OAuth2SessionStatus (line 33) | enum OAuth2SessionStatus {
method fmt (line 39) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type OAuth2ClientKind (line 49) | enum OAuth2ClientKind {
method fmt (line 55) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type FilterParams (line 67) | pub struct FilterParams {
method fmt (line 103) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type RouteError (line 143) | pub enum RouteError {
method into_response (line 166) | fn into_response(self) -> axum::response::Response {
function doc (line 180) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 222) | pub async fn handler(
function test_oauth2_simple_session_list (line 342) | async fn test_oauth2_simple_session_list(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/personal_sessions/add.rs
type RouteError (line 33) | pub enum RouteError {
method into_response (line 51) | fn into_response(self) -> axum::response::Response {
type Request (line 67) | pub struct Request {
function doc (line 83) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 102) | pub async fn handler(
function test_create_personal_session_with_token (line 198) | async fn test_create_personal_session_with_token(pool: PgPool) {
function test_create_personal_session_invalid_user (line 260) | async fn test_create_personal_session_invalid_user(pool: PgPool) {
function test_create_personal_session_invalid_scope (line 281) | async fn test_create_personal_session_invalid_scope(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/personal_sessions/get.rs
type RouteError (line 23) | pub enum RouteError {
method into_response (line 35) | fn into_response(self) -> axum::response::Response {
function doc (line 46) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 68) | pub async fn handler(
function test_get (line 105) | async fn test_get(pool: PgPool) {
function test_not_found (line 177) | async fn test_not_found(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/personal_sessions/list.rs
type PersonalSessionStatus (line 33) | enum PersonalSessionStatus {
method fmt (line 39) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type FilterParams (line 51) | pub struct FilterParams {
method fmt (line 89) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type RouteError (line 140) | pub enum RouteError {
method into_response (line 161) | fn into_response(self) -> axum::response::Response {
function doc (line 173) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 215) | pub async fn handler(
function test_list (line 352) | async fn test_list(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/personal_sessions/mod.rs
function personal_session_owner_from_caller (line 26) | fn personal_session_owner_from_caller(caller: &CallerSession) -> Persona...
FILE: crates/handlers/src/admin/v1/personal_sessions/regenerate.rs
type RouteError (line 29) | pub enum RouteError {
method into_response (line 50) | fn into_response(self) -> axum::response::Response {
type Request (line 66) | pub struct Request {
function doc (line 72) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 89) | pub async fn handler(
function test_regenerate_personal_session (line 172) | async fn test_regenerate_personal_session(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/personal_sessions/revoke.rs
type RouteError (line 26) | pub enum RouteError {
method into_response (line 41) | fn into_response(self) -> axum::response::Response {
function doc (line 53) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 81) | pub async fn handler(
function test_revoke_session (line 131) | async fn test_revoke_session(pool: PgPool) {
function test_revoke_already_revoked_session (line 178) | async fn test_revoke_already_revoked_session(pool: PgPool) {
function test_revoke_unknown_session (line 233) | async fn test_revoke_unknown_session(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/policy_data/get.rs
type RouteError (line 24) | pub enum RouteError {
method into_response (line 35) | fn into_response(self) -> axum::response::Response {
function doc (line 46) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 63) | pub async fn handler(
function test_get (line 86) | async fn test_get(pool: PgPool) {
function test_get_not_found (line 135) | async fn test_get_not_found(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/policy_data/get_latest.rs
type RouteError (line 22) | pub enum RouteError {
method into_response (line 33) | fn into_response(self) -> axum::response::Response {
function doc (line 44) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 62) | pub async fn handler(
function test_get_latest (line 83) | async fn test_get_latest(pool: PgPool) {
function test_get_no_latest (line 131) | async fn test_get_no_latest(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/policy_data/set.rs
type RouteError (line 28) | pub enum RouteError {
method into_response (line 39) | fn into_response(self) -> axum::response::Response {
function data_example (line 50) | fn data_example() -> serde_json::Value {
type SetPolicyDataRequest (line 61) | pub struct SetPolicyDataRequest {
function doc (line 66) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 86) | pub async fn handler(
function test_create (line 119) | async fn test_create(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/site_config.rs
type SiteConfig (line 15) | pub struct SiteConfig {
function doc (line 55) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 79) | pub async fn handler(
FILE: crates/handlers/src/admin/v1/upstream_oauth_links/add.rs
type RouteError (line 26) | pub enum RouteError {
method into_response (line 43) | fn into_response(self) -> axum::response::Response {
type Request (line 58) | pub struct Request {
function doc (line 74) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 108) | pub async fn handler(
function test_create (line 189) | async fn test_create(pool: PgPool) {
function test_association (line 248) | async fn test_association(pool: PgPool) {
function test_link_already_exists (line 319) | async fn test_link_already_exists(pool: PgPool) {
function test_user_not_found (line 389) | async fn test_user_not_found(pool: PgPool) {
function test_provider_not_found (line 430) | async fn test_provider_not_found(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/upstream_oauth_links/delete.rs
type RouteError (line 19) | pub enum RouteError {
method into_response (line 30) | fn into_response(self) -> axum::response::Response {
function doc (line 41) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 55) | pub async fn handler(
function test_delete (line 85) | async fn test_delete(pool: PgPool) {
function test_not_found (line 168) | async fn test_not_found(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/upstream_oauth_links/get.rs
type RouteError (line 24) | pub enum RouteError {
method into_response (line 35) | fn into_response(self) -> axum::response::Response {
function doc (line 46) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 65) | pub async fn handler(
function test_get (line 91) | async fn test_get(pool: PgPool) {
function test_not_found (line 161) | async fn test_not_found(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/upstream_oauth_links/list.rs
type FilterParams (line 31) | pub struct FilterParams {
method fmt (line 48) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type RouteError (line 73) | pub enum RouteError {
method into_response (line 90) | fn into_response(self) -> axum::response::Response {
function doc (line 102) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 139) | pub async fn handler(
function test_list (line 227) | async fn test_list(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/upstream_oauth_links/mod.rs
function oidc_provider_params (line 29) | pub(crate) fn oidc_provider_params(name: &str) -> UpstreamOAuthProviderP...
FILE: crates/handlers/src/admin/v1/upstream_oauth_providers/get.rs
type RouteError (line 24) | pub enum RouteError {
method into_response (line 35) | fn into_response(self) -> axum::response::Response {
function doc (line 47) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 61) | pub async fn handler(
function create_test_provider (line 95) | async fn create_test_provider(state: &mut TestState) -> UpstreamOAuthPro...
function test_get_provider (line 136) | async fn test_get_provider(pool: PgPool) {
function test_not_found (line 181) | async fn test_not_found(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/upstream_oauth_providers/list.rs
type FilterParams (line 30) | pub struct FilterParams {
method fmt (line 37) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type RouteError (line 52) | pub enum RouteError {
method into_response (line 63) | fn into_response(self) -> axum::response::Response {
function doc (line 75) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 106) | pub async fn handler(
function create_test_providers (line 166) | async fn create_test_providers(state: &mut TestState) {
function test_list_all_providers (line 276) | async fn test_list_all_providers(pool: PgPool) {
function test_filter_by_enabled_true (line 367) | async fn test_filter_by_enabled_true(pool: PgPool) {
function test_filter_by_enabled_false (line 436) | async fn test_filter_by_enabled_false(pool: PgPool) {
function test_pagination (line 486) | async fn test_pagination(pool: PgPool) {
function test_invalid_filter (line 603) | async fn test_invalid_filter(pool: PgPool) {
function test_count_parameter (line 618) | async fn test_count_parameter(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/user_emails/add.rs
type RouteError (line 32) | pub enum RouteError {
method into_response (line 54) | fn into_response(self) -> axum::response::Response {
type Request (line 70) | pub struct Request {
function doc (line 80) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 112) | pub async fn handler(
function test_create (line 172) | async fn test_create(pool: PgPool) {
function test_user_not_found (line 218) | async fn test_user_not_found(pool: PgPool) {
function test_email_already_exists (line 244) | async fn test_email_already_exists(pool: PgPool) {
function test_invalid_email (line 288) | async fn test_invalid_email(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/user_emails/delete.rs
type RouteError (line 21) | pub enum RouteError {
method into_response (line 32) | fn into_response(self) -> axum::response::Response {
function doc (line 43) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 56) | pub async fn handler(
function test_delete (line 88) | async fn test_delete(pool: PgPool) {
function test_not_found (line 129) | async fn test_not_found(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/user_emails/get.rs
type RouteError (line 24) | pub enum RouteError {
method into_response (line 35) | fn into_response(self) -> axum::response::Response {
function doc (line 46) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 63) | pub async fn handler(
function test_get (line 85) | async fn test_get(pool: PgPool) {
function test_not_found (line 140) | async fn test_not_found(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/user_emails/list.rs
type FilterParams (line 31) | pub struct FilterParams {
method fmt (line 43) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type RouteError (line 63) | pub enum RouteError {
method into_response (line 77) | fn into_response(self) -> axum::response::Response {
function doc (line 89) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 125) | pub async fn handler(
function test_list (line 192) | async fn test_list(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/user_registration_tokens/add.rs
type RouteError (line 28) | pub enum RouteError {
method into_response (line 39) | fn into_response(self) -> axum::response::Response {
type Request (line 53) | pub struct Request {
function doc (line 65) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 79) | pub async fn handler(
function test_create (line 128) | async fn test_create(pool: PgPool) {
function test_create_auto_token (line 170) | async fn test_create_auto_token(pool: PgPool) {
function test_create_conflict (line 212) | async fn test_create_conflict(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/user_registration_tokens/get.rs
type RouteError (line 25) | pub enum RouteError {
method into_response (line 36) | fn into_response(self) -> axum::response::Response {
function doc (line 47) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 66) | pub async fn handler(
function test_get_token (line 93) | async fn test_get_token(pool: PgPool) {
function test_get_nonexistent_token (line 149) | async fn test_get_nonexistent_token(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/user_registration_tokens/list.rs
type FilterParams (line 31) | pub struct FilterParams {
method fmt (line 53) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type RouteError (line 80) | pub enum RouteError {
method into_response (line 91) | fn into_response(self) -> axum::response::Response {
function doc (line 103) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 134) | pub async fn handler(
function create_test_tokens (line 198) | async fn create_test_tokens(state: &mut TestState) {
function test_list_all_tokens (line 286) | async fn test_list_all_tokens(pool: PgPool) {
function test_filter_by_used (line 426) | async fn test_filter_by_used(pool: PgPool) {
function test_filter_by_revoked (line 590) | async fn test_filter_by_revoked(pool: PgPool) {
function test_filter_by_expired (line 754) | async fn test_filter_by_expired(pool: PgPool) {
function test_filter_by_valid (line 918) | async fn test_filter_by_valid(pool: PgPool) {
function test_combined_filters (line 1082) | async fn test_combined_filters(pool: PgPool) {
function test_pagination (line 1137) | async fn test_pagination(pool: PgPool) {
function test_invalid_filter (line 1327) | async fn test_invalid_filter(pool: PgPool) {
function test_count_parameter (line 1349) | async fn test_count_parameter(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/user_registration_tokens/revoke.rs
type RouteError (line 25) | pub enum RouteError {
method into_response (line 39) | fn into_response(self) -> axum::response::Response {
function doc (line 51) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 75) | pub async fn handler(
function test_revoke_token (line 114) | async fn test_revoke_token(pool: PgPool) {
function test_revoke_already_revoked_token (line 151) | async fn test_revoke_already_revoked_token(pool: PgPool) {
function test_revoke_unknown_token (line 200) | async fn test_revoke_unknown_token(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/user_registration_tokens/unrevoke.rs
type RouteError (line 25) | pub enum RouteError {
method into_response (line 39) | fn into_response(self) -> axum::response::Response {
function doc (line 51) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 75) | pub async fn handler(
function test_unrevoke_token (line 112) | async fn test_unrevoke_token(pool: PgPool) {
function test_unrevoke_not_revoked_token (line 180) | async fn test_unrevoke_not_revoked_token(pool: PgPool) {
function test_unrevoke_unknown_token (line 220) | async fn test_unrevoke_unknown_token(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/user_registration_tokens/update.rs
function deserialize_some (line 27) | fn deserialize_some<'de, T, D>(deserializer: D) -> Result<Option<T>, D::...
type Request (line 38) | pub struct Request {
type RouteError (line 60) | pub enum RouteError {
method into_response (line 71) | fn into_response(self) -> axum::response::Response {
function doc (line 82) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 102) | pub async fn handler(
function test_update_expiry (line 153) | async fn test_update_expiry(pool: PgPool) {
function test_update_usage_limit (line 258) | async fn test_update_usage_limit(pool: PgPool) {
function test_update_multiple_fields (line 362) | async fn test_update_multiple_fields(pool: PgPool) {
function test_update_no_fields (line 428) | async fn test_update_no_fields(pool: PgPool) {
function test_update_unknown_token (line 490) | async fn test_update_unknown_token(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/user_sessions/finish.rs
type RouteError (line 24) | pub enum RouteError {
method into_response (line 38) | fn into_response(self) -> axum::response::Response {
function doc (line 50) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 81) | pub async fn handler(
function test_finish_session (line 120) | async fn test_finish_session(pool: PgPool) {
function test_finish_already_finished_session (line 155) | async fn test_finish_already_finished_session(pool: PgPool) {
function test_finish_unknown_session (line 199) | async fn test_finish_unknown_session(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/user_sessions/get.rs
type RouteError (line 24) | pub enum RouteError {
method into_response (line 35) | fn into_response(self) -> axum::response::Response {
function doc (line 46) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 64) | pub async fn handler(
function test_get (line 88) | async fn test_get(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/user_sessions/list.rs
type UserSessionStatus (line 29) | enum UserSessionStatus {
method fmt (line 35) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type FilterParams (line 47) | pub struct FilterParams {
method fmt (line 65) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type RouteError (line 85) | pub enum RouteError {
method into_response (line 99) | fn into_response(self) -> axum::response::Response {
function doc (line 111) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 149) | pub async fn handler(
function test_user_session_list (line 219) | async fn test_user_session_list(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/users/add.rs
function valid_username_character (line 28) | fn valid_username_character(c: char) -> bool {
function username_valid (line 40) | fn username_valid(username: &str) -> bool {
type RouteError (line 60) | pub enum RouteError {
method into_response (line 80) | fn into_response(self) -> axum::response::Response {
type Request (line 95) | pub struct Request {
function doc (line 114) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 140) | pub async fn handler(
function test_add_user (line 205) | async fn test_add_user(pool: PgPool) {
function test_add_user_invalid_username (line 252) | async fn test_add_user_invalid_username(pool: PgPool) {
function test_add_user_exists (line 271) | async fn test_add_user_exists(pool: PgPool) {
function test_add_user_reserved (line 303) | async fn test_add_user_reserved(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/users/by_username.rs
type RouteError (line 25) | pub enum RouteError {
method into_response (line 36) | fn into_response(self) -> axum::response::Response {
type UsernamePathParam (line 48) | pub struct UsernamePathParam {
function doc (line 53) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 71) | pub async fn handler(
FILE: crates/handlers/src/admin/v1/users/deactivate.rs
type RouteError (line 30) | pub enum RouteError {
method into_response (line 41) | fn into_response(self) -> axum::response::Response {
type Request (line 55) | pub struct Request {
function doc (line 62) | pub fn doc(mut operation: TransformOperation) -> TransformOperation {
function handler (line 95) | pub async fn handler(
function test_deactivate_user_helper (line 141) | async fn test_deactivate_user_helper(pool: PgPool, skip_erase: Option<bo...
function test_deactivate_user (line 227) | async fn test_deactivate_user(pool: PgPool) {
function test_deactivate_user_skip_erase (line 232) | async fn test_deactivate_user_skip_erase(pool: PgPool) {
function test_deactivate_locked_user (line 237) | async fn test_deactivate_locked_user(pool: PgPool) {
function test_deactivate_unknown_user (line 308) | async fn test_deactivate_unknown_user(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/users/get.rs
type RouteError (line 25) | pub enum RouteError {
method into_response (line 36) | fn into_response(self) -> axum::response::Response {
function doc (line 47) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 64) | pub async fn handler(
FILE: crates/handlers/src/admin/v1/users/list.rs
type UserStatus (line 29) | enum UserStatus {
method fmt (line 36) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type FilterParams (line 49) | pub struct FilterParams {
method fmt (line 79) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type RouteError (line 106) | pub enum RouteError {
method into_response (line 117) | fn into_response(self) -> axum::response::Response {
function doc (line 128) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 159) | pub async fn handler(
function test_list_users (line 219) | async fn test_list_users(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/users/lock.rs
type RouteError (line 27) | pub enum RouteError {
method into_response (line 38) | fn into_response(self) -> axum::response::Response {
function doc (line 49) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 70) | pub async fn handler(
function test_lock_user (line 115) | async fn test_lock_user(pool: PgPool) {
function test_lock_user_twice (line 171) | async fn test_lock_user_twice(pool: PgPool) {
function test_lock_unknown_user (line 207) | async fn test_lock_unknown_user(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/users/reactivate.rs
type RouteError (line 27) | pub enum RouteError {
method into_response (line 41) | fn into_response(self) -> axum::response::Response {
function doc (line 52) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 73) | pub async fn handler(
function test_reactivate_deactivated_user (line 113) | async fn test_reactivate_deactivated_user(pool: PgPool) {
function test_reactivate_active_user (line 168) | async fn test_reactivate_active_user(pool: PgPool) {
function test_reactivate_unknown_user (line 206) | async fn test_reactivate_unknown_user(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/users/set_admin.rs
type RouteError (line 27) | pub enum RouteError {
method into_response (line 38) | fn into_response(self) -> axum::response::Response {
type Request (line 52) | pub struct Request {
function doc (line 57) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 77) | pub async fn handler(
function test_change_can_request_admin (line 111) | async fn test_change_can_request_admin(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/users/set_password.rs
type RouteError (line 25) | pub enum RouteError {
method into_response (line 45) | fn into_response(self) -> axum::response::Response {
type Request (line 61) | pub struct Request {
function doc (line 70) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 92) | pub async fn handler(
function test_set_password (line 149) | async fn test_set_password(pool: PgPool) {
function test_weak_password (line 197) | async fn test_weak_password(pool: PgPool) {
function test_unknown_user (line 257) | async fn test_unknown_user(pool: PgPool) {
function test_disabled (line 280) | async fn test_disabled(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/users/unlock.rs
type RouteError (line 27) | pub enum RouteError {
method into_response (line 38) | fn into_response(self) -> axum::response::Response {
function doc (line 49) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 70) | pub async fn handler(
function test_unlock_user (line 115) | async fn test_unlock_user(pool: PgPool) {
function test_unlock_deactivated_user (line 173) | async fn test_unlock_deactivated_user(pool: PgPool) {
function test_lock_unknown_user (line 234) | async fn test_lock_unknown_user(pool: PgPool) {
FILE: crates/handlers/src/admin/v1/version.rs
type Version (line 15) | pub struct Version {
function doc (line 20) | pub fn doc(operation: TransformOperation) -> TransformOperation {
function handler (line 29) | pub async fn handler(
function test_add_user (line 45) | async fn test_add_user(pool: PgPool) {
FILE: crates/handlers/src/bin/api-schema.rs
type DummyState (line 25) | struct DummyState;
function main (line 65) | fn main() -> Result<(), Box<dyn std::error::Error>> {
FILE: crates/handlers/src/bin/graphql-schema.rs
function main (line 16) | fn main() {
FILE: crates/handlers/src/captcha.rs
constant RECAPTCHA_VERIFY_URL (line 17) | const RECAPTCHA_VERIFY_URL: &str = "https://www.recaptcha.net/recaptcha/...
constant HCAPTCHA_VERIFY_URL (line 20) | const HCAPTCHA_VERIFY_URL: &str = "https://api.hcaptcha.com/siteverify";
constant CF_TURNSTILE_VERIFY_URL (line 23) | const CF_TURNSTILE_VERIFY_URL: &str = "https://challenges.cloudflare.com...
type Error (line 26) | pub enum Error {
type Form (line 54) | pub struct Form {
method verify (line 160) | pub async fn verify(
type VerificationRequest (line 61) | struct VerificationRequest<'a> {
type VerificationResponse (line 68) | struct VerificationResponse {
type ErrorCode (line 79) | pub enum ErrorCode {
FILE: crates/handlers/src/cleanup_tests.rs
type TestSessionHierarchy (line 50) | struct TestSessionHierarchy {
constant UPSTREAM_OAUTH_ISSUER (line 63) | const UPSTREAM_OAUTH_ISSUER: &str = "https://idp.example.com";
constant UPSTREAM_OAUTH_CLIENT_ID (line 64) | const UPSTREAM_OAUTH_CLIENT_ID: &str = "test-client";
constant UPSTREAM_OAUTH_SESSION_ID (line 65) | const UPSTREAM_OAUTH_SESSION_ID: &str = "upstream-oauth-session-id";
constant UPSTREAM_OAUTH_SUBJECT (line 66) | const UPSTREAM_OAUTH_SUBJECT: &str = "upstream-oauth-sub";
function create_session_hierarchy (line 76) | async fn create_session_hierarchy(
function test_cleanup_sessions_within_retention_preserved (line 292) | async fn test_cleanup_sessions_within_retention_preserved(pool: PgPool) {
function test_cleanup_deactivated_users (line 356) | async fn test_cleanup_deactivated_users(pool: PgPool) {
function test_cleanup_sessions_after_retention_deleted (line 467) | async fn test_cleanup_sessions_after_retention_deleted(pool: PgPool) {
function test_cleanup_user_session_blocked_by_child_sessions (line 532) | async fn test_cleanup_user_session_blocked_by_child_sessions(pool: PgPoo...
function test_backchannel_logout_works_before_cleanup (line 580) | async fn test_backchannel_logout_works_before_cleanup(pool: PgPool) {
function test_active_sessions_not_cleaned_up (line 729) | async fn test_active_sessions_not_cleaned_up(pool: PgPool) {
FILE: crates/handlers/src/compat/login.rs
constant TYPE (line 54) | const TYPE: Key = Key::from_static_str("type");
constant RESULT (line 55) | const RESULT: Key = Key::from_static_str("result");
constant INACTIVE_SESSION_THRESHOLD (line 59) | const INACTIVE_SESSION_THRESHOLD: chrono::TimeDelta = Duration::days(90);
type LoginType (line 63) | enum LoginType {
type SsoIdentityProvider (line 85) | struct SsoIdentityProvider {
type LoginTypes (line 91) | struct LoginTypes {
function get (line 96) | pub(crate) async fn get(State(password_manager): State<PasswordManager>)...
type RequestBody (line 124) | pub struct RequestBody {
type Credentials (line 145) | pub enum Credentials {
method login_type (line 162) | fn login_type(&self) -> &'static str {
type Identifier (line 173) | pub enum Identifier {
type ResponseBody (line 184) | pub struct ResponseBody {
type RouteError (line 194) | pub enum RouteError {
method from (line 242) | fn from(err: anyhow::Error) -> Self {
method into_response (line 248) | fn into_response(self) -> axum::response::Response {
function post (line 315) | pub(crate) async fn post(
function process_violations_for_compat_login (line 516) | async fn process_violations_for_compat_login(
constant MINIMUM_SESSIONS_TO_FETCH (line 664) | const MINIMUM_SESSIONS_TO_FETCH: usize = 2160;
function find_lru_compat_sessions_flawed (line 673) | async fn find_lru_compat_sessions_flawed(
function token_login (line 755) | async fn token_login(
function user_password_login (line 896) | async fn user_password_login(
function test_get_login (line 1030) | async fn test_get_login(pool: PgPool) {
function test_bad_body (line 1061) | async fn test_bad_body(pool: PgPool) {
function test_password_disabled (line 1111) | async fn test_password_disabled(pool: PgPool) {
function user_with_password (line 1165) | async fn user_with_password(
function test_user_password_login (line 1208) | async fn test_user_password_login(pool: PgPool) {
function test_no_content_type (line 1380) | async fn test_no_content_type(pool: PgPool) {
function test_user_password_login_mxid (line 1412) | async fn test_user_password_login_mxid(pool: PgPool) {
function test_password_login_rate_limit (line 1478) | async fn test_password_login_rate_limit(pool: PgPool) {
function test_unsupported_login_identifier (line 1532) | async fn test_unsupported_login_identifier(pool: PgPool) {
function test_unsupported_login (line 1559) | async fn test_unsupported_login(pool: PgPool) {
function test_login_token_login (line 1581) | async fn test_login_token_login(pool: PgPool) {
function get_login_token (line 1732) | async fn get_login_token(state: &TestState, user: &User) -> String {
type ConsentForbiddenPageType (line 1772) | pub enum ConsentForbiddenPageType {
type MatrixCompatSsoLoginError (line 1784) | pub enum MatrixCompatSsoLoginError {
function extract_csrf_token_from_page_html (line 1799) | fn extract_csrf_token_from_page_html(body: &str) -> Option<String> {
function matrix_compat_sso_login (line 1816) | async fn matrix_compat_sso_login(
function test_session_soft_limit_interactive_login (line 2014) | async fn test_session_soft_limit_interactive_login(pool: PgPool) {
function test_session_soft_limit_does_not_affect_non_interactive_login (line 2079) | async fn test_session_soft_limit_does_not_affect_non_interactive_login(p...
function test_session_hard_limit_compat_login (line 2133) | async fn test_session_hard_limit_compat_login(pool: PgPool) {
function test_session_limit_under_max_session_threshold_interactive (line 2192) | async fn test_session_limit_under_max_session_threshold_interactive(pool...
function test_session_limit_under_max_session_threshold_non_interactive (line 2253) | async fn test_session_limit_under_max_session_threshold_non_interactive(...
function test_session_limit_past_max_session_threshold_interactive (line 2318) | async fn test_session_limit_past_max_session_threshold_interactive(pool:...
function test_session_limit_past_max_session_threshold_non_interactive (line 2394) | async fn test_session_limit_past_max_session_threshold_non_interactive(p...
function test_session_dangerous_hard_limit_eviction_old_compat_login (line 2461) | async fn test_session_dangerous_hard_limit_eviction_old_compat_login(poo...
function test_session_dangerous_hard_limit_eviction_recent_compat_login (line 2629) | async fn test_session_dangerous_hard_limit_eviction_recent_compat_login(...
FILE: crates/handlers/src/compat/login_sso_complete.rs
type Params (line 39) | pub struct Params {
function get (line 48) | pub async fn get(
function post (line 189) | pub async fn post(
FILE: crates/handlers/src/compat/login_sso_redirect.rs
type Params (line 22) | pub struct Params {
method action (line 33) | fn action(&self) -> Option<CompatLoginSsoAction> {
type RouteError (line 41) | pub enum RouteError {
method into_response (line 55) | fn into_response(self) -> axum::response::Response {
function get (line 66) | pub async fn get(
function test_unstable_action_fallback (line 108) | async fn test_unstable_action_fallback(pool: PgPool) {
function test_unknown_action (line 133) | async fn test_unknown_action(pool: PgPool) {
FILE: crates/handlers/src/compat/logout.rs
constant RESULT (line 33) | const RESULT: Key = Key::from_static_str("result");
type RouteError (line 36) | pub enum RouteError {
method into_response (line 53) | fn into_response(self) -> axum::response::Response {
function post (line 79) | pub(crate) async fn post(
FILE: crates/handlers/src/compat/logout_all.rs
constant RESULT (line 37) | const RESULT: Key = Key::from_static_str("result");
type RouteError (line 40) | pub enum RouteError {
method into_response (line 75) | fn into_response(self) -> axum::response::Response {
type RequestBody (line 122) | pub(crate) struct RequestBody {
function post (line 128) | pub(crate) async fn post(
FILE: crates/handlers/src/compat/mod.rs
type MatrixError (line 32) | struct MatrixError {
method into_response (line 40) | fn into_response(self) -> axum::response::Response {
type MatrixJsonBody (line 47) | pub struct MatrixJsonBody<T>(pub T);
type MatrixJsonBodyRejection (line 50) | pub enum MatrixJsonBodyRejection {
method into_response (line 65) | fn into_response(self) -> axum::response::Response {
type Rejection (line 118) | type Rejection = MatrixJsonBodyRejection;
function from_request (line 120) | async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rej...
type Rejection (line 153) | type Rejection = MatrixJsonBodyRejection;
function from_request (line 155) | async fn from_request(req: Request, state: &S) -> Result<Option<Self>, S...
FILE: crates/handlers/src/compat/refresh.rs
type RequestBody (line 25) | pub struct RequestBody {
type RouteError (line 30) | pub enum RouteError {
method into_response (line 54) | fn into_response(self) -> axum::response::Response {
type ResponseBody (line 81) | pub struct ResponseBody {
function post (line 89) | pub(crate) async fn post(
FILE: crates/handlers/src/compat/tests.rs
type LoginCredentials (line 15) | enum LoginCredentials {
type LoginIdentifier (line 25) | enum LoginIdentifier {
type LoginRequest (line 31) | struct LoginRequest {
type LoginResponse (line 39) | struct LoginResponse {
type RefreshRequest (line 50) | struct RefreshRequest {
type RefreshResponse (line 55) | struct RefreshResponse {
function test_compat_refresh (line 63) | async fn test_compat_refresh(pool: sqlx::PgPool) {
function test_refresh_with_invalid_token (line 134) | async fn test_refresh_with_invalid_token(pool: sqlx::PgPool) {
function test_refresh_with_consumed_token (line 149) | async fn test_refresh_with_consumed_token(pool: sqlx::PgPool) {
function create_test_user (line 194) | async fn create_test_user(state: &TestState, username: &str) -> mas_data...
FILE: crates/handlers/src/graphql/mod.rs
type ExtraRouterParameters (line 67) | pub struct ExtraRouterParameters {
type GraphQLState (line 71) | struct GraphQLState {
method repository (line 83) | async fn repository(&self) -> Result<BoxRepository, RepositoryError> {
method policy (line 87) | async fn policy(&self) -> Result<Policy, InstantiateError> {
method password_manager (line 91) | fn password_manager(&self) -> PasswordManager {
method site_config (line 95) | fn site_config(&self) -> &SiteConfig {
method homeserver_connection (line 99) | fn homeserver_connection(&self) -> &dyn HomeserverConnection {
method url_builder (line 103) | fn url_builder(&self) -> &UrlBuilder {
method limiter (line 107) | fn limiter(&self) -> &Limiter {
method clock (line 111) | fn clock(&self) -> BoxClock {
method rng (line 116) | fn rng(&self) -> BoxRng {
function schema (line 126) | pub fn schema(
function span_for_graphql_request (line 149) | fn span_for_graphql_request(request: &async_graphql::Request) -> tracing...
type RouteError (line 167) | pub enum RouteError {
method into_response (line 187) | fn into_response(self) -> Response {
function get_requester (line 232) | async fn get_requester(
function post (line 309) | pub async fn post(
function get (line 369) | pub async fn get(
function playground (line 420) | pub async fn playground() -> impl IntoResponse {
type Schema (line 426) | pub type Schema = async_graphql::Schema<Query, Mutation, EmptySubscripti...
type SchemaBuilder (line 427) | pub type SchemaBuilder = async_graphql::SchemaBuilder<Query, Mutation, E...
function schema_builder (line 430) | pub fn schema_builder() -> SchemaBuilder {
type Requester (line 436) | pub struct Requester {
method fingerprint (line 443) | pub fn fingerprint(&self) -> RequesterFingerprint {
method for_policy (line 451) | pub fn for_policy(&self) -> mas_policy::Requester {
type Target (line 460) | type Target = RequestingEntity;
method deref (line 462) | fn deref(&self) -> &Self::Target {
type RequestingEntity (line 469) | pub enum RequestingEntity {
method browser_session (line 531) | fn browser_session(&self) -> Option<&BrowserSession> {
method user (line 538) | fn user(&self) -> Option<&User> {
method oauth2_session (line 546) | fn oauth2_session(&self) -> Option<&Session> {
method is_owner_or_admin (line 554) | fn is_owner_or_admin(&self, resource: &impl OwnerId) -> bool {
method is_admin (line 572) | fn is_admin(&self) -> bool {
method is_unauthenticated (line 583) | fn is_unauthenticated(&self) -> bool {
method from (line 589) | fn from(session: BrowserSession) -> Self {
method from (line 598) | fn from(session: Option<T>) -> Self {
type OwnerId (line 481) | trait OwnerId {
method owner_id (line 482) | fn owner_id(&self) -> Option<Ulid>;
method owner_id (line 486) | fn owner_id(&self) -> Option<Ulid> {
method owner_id (line 492) | fn owner_id(&self) -> Option<Ulid> {
method owner_id (line 498) | fn owner_id(&self) -> Option<Ulid> {
method owner_id (line 504) | fn owner_id(&self) -> Option<Ulid> {
method owner_id (line 510) | fn owner_id(&self) -> Option<Ulid> {
method owner_id (line 516) | fn owner_id(&self) -> Option<Ulid> {
method owner_id (line 525) | fn owner_id(&self) -> Option<Ulid> {
type UserId (line 522) | pub struct UserId(Ulid);
type DateFilter (line 605) | pub struct DateFilter {
FILE: crates/handlers/src/graphql/model/browser_sessions.rs
type BrowserSession (line 25) | pub struct BrowserSession(pub mas_data_model::BrowserSession);
method from (line 28) | fn from(v: mas_data_model::BrowserSession) -> Self {
method id (line 36) | pub async fn id(&self) -> ID {
method user (line 41) | async fn user(&self) -> User {
method last_authentication (line 46) | async fn last_authentication(
method created_at (line 64) | pub async fn created_at(&self) -> DateTime<Utc> {
method finished_at (line 69) | pub async fn finished_at(&self) -> Option<DateTime<Utc>> {
method state (line 74) | pub async fn state(&self) -> SessionState {
method user_agent (line 83) | pub async fn user_agent(&self) -> Option<UserAgent> {
method last_active_ip (line 92) | pub async fn last_active_ip(&self) -> Option<String> {
method last_active_at (line 97) | pub async fn last_active_at(&self) -> Option<DateTime<Utc>> {
method app_sessions (line 104) | async fn app_sessions(
type Authentication (line 196) | pub struct Authentication(pub mas_data_model::Authentication);
method id (line 201) | pub async fn id(&self) -> ID {
method created_at (line 206) | pub async fn created_at(&self) -> DateTime<Utc> {
FILE: crates/handlers/src/graphql/model/compat_sessions.rs
type ReverseReference (line 21) | enum ReverseReference<T> {
type CompatSession (line 30) | pub struct CompatSession {
method new (line 36) | pub fn new(session: mas_data_model::CompatSession) -> Self {
method with_loaded_sso_login (line 44) | pub fn with_loaded_sso_login(
method id (line 66) | pub async fn id(&self) -> ID {
method user (line 71) | async fn user(&self, ctx: &Context<'_>) -> Result<User, async_graphql:...
method device_id (line 85) | async fn device_id(&self) -> Option<&str> {
method created_at (line 90) | pub async fn created_at(&self) -> DateTime<Utc> {
method finished_at (line 95) | pub async fn finished_at(&self) -> Option<DateTime<Utc>> {
method user_agent (line 100) | pub async fn user_agent(&self) -> Option<UserAgent> {
method sso_login (line 109) | pub async fn sso_login(
method browser_session (line 131) | pub async fn browser_session(
method state (line 152) | pub async fn state(&self) -> SessionState {
method last_active_ip (line 160) | pub async fn last_active_ip(&self) -> Option<String> {
method last_active_at (line 165) | pub async fn last_active_at(&self) -> Option<DateTime<Utc>> {
method human_name (line 170) | pub async fn human_name(&self) -> Option<&str> {
type CompatSessionType (line 55) | pub enum CompatSessionType {
type CompatSsoLogin (line 178) | pub struct CompatSsoLogin(pub mas_data_model::CompatSsoLogin);
method id (line 183) | pub async fn id(&self) -> ID {
method created_at (line 188) | pub async fn created_at(&self) -> DateTime<Utc> {
method redirect_uri (line 193) | async fn redirect_uri(&self) -> &Url {
method fulfilled_at (line 199) | async fn fulfilled_at(&self) -> Option<DateTime<Utc>> {
method exchanged_at (line 204) | async fn exchanged_at(&self) -> Option<DateTime<Utc>> {
method session (line 209) | async fn session(
FILE: crates/handlers/src/graphql/model/cursor.rs
type NodeCursor (line 14) | pub struct NodeCursor(pub NodeType, pub Ulid);
method extract_for_types (line 17) | pub fn extract_for_types(&self, node_types: &[NodeType]) -> Result<Uli...
method extract_for_type (line 25) | pub fn extract_for_type(&self, node_type: NodeType) -> Result<Ulid, as...
type Cursor (line 34) | pub type Cursor = OpaqueCursor<NodeCursor>;
FILE: crates/handlers/src/graphql/model/matrix.rs
type MatrixUser (line 11) | pub struct MatrixUser {
method load (line 26) | pub(crate) async fn load<C: HomeserverConnection + ?Sized>(
FILE: crates/handlers/src/graphql/model/mod.rs
type CreationEvent (line 40) | pub enum CreationEvent {
type PreloadedTotalCount (line 52) | pub struct PreloadedTotalCount(pub Option<usize>);
method total_count (line 57) | async fn total_count(&self) -> Result<usize, async_graphql::Error> {
type SessionState (line 65) | pub enum SessionState {
type DeviceType (line 75) | pub enum DeviceType {
method from (line 90) | fn from(device_type: mas_data_model::DeviceType) -> Self {
type UserAgent (line 102) | pub struct UserAgent {
method from (line 126) | fn from(ua: mas_data_model::UserAgent) -> Self {
FILE: crates/handlers/src/graphql/model/node.rs
type NodeType (line 19) | pub enum NodeType {
method to_prefix (line 44) | fn to_prefix(self) -> &'static str {
method from_prefix (line 61) | fn from_prefix(prefix: &str) -> Option<Self> {
method serialize (line 79) | pub fn serialize(self, id: impl Into<Ulid>) -> String {
method id (line 85) | pub fn id(self, id: impl Into<Ulid>) -> ID {
method deserialize (line 89) | pub fn deserialize(serialized: &str) -> Result<(Self, Ulid), InvalidID> {
method from_id (line 96) | pub fn from_id(id: &ID) -> Result<(Self, Ulid), InvalidID> {
method extract_ulid (line 100) | pub fn extract_ulid(self, id: &ID) -> Result<Ulid, InvalidID> {
type InvalidID (line 36) | pub enum InvalidID {
type Node (line 117) | pub enum Node {
FILE: crates/handlers/src/graphql/model/oauth.rs
type OAuth2Session (line 20) | pub struct OAuth2Session(pub mas_data_model::Session);
method id (line 25) | pub async fn id(&self) -> ID {
method client (line 30) | pub async fn client(&self, ctx: &Context<'_>) -> Result<OAuth2Client, ...
method scope (line 44) | pub async fn scope(&self) -> String {
method created_at (line 49) | pub async fn created_at(&self) -> DateTime<Utc> {
method finished_at (line 54) | pub async fn finished_at(&self) -> Option<DateTime<Utc>> {
method user_agent (line 62) | pub async fn user_agent(&self) -> Option<UserAgent> {
method state (line 71) | pub async fn state(&self) -> SessionState {
method browser_session (line 79) | pub async fn browser_session(
method user (line 100) | pub async fn user(&self, ctx: &Context<'_>) -> Result<Option<User>, as...
method last_active_ip (line 122) | pub async fn last_active_ip(&self) -> Option<String> {
method last_active_at (line 127) | pub async fn last_active_at(&self) -> Option<DateTime<Utc>> {
method human_name (line 132) | pub async fn human_name(&self) -> Option<&str> {
type OAuth2ApplicationType (line 139) | pub enum OAuth2ApplicationType {
type OAuth2Client (line 149) | pub struct OAuth2Client(pub mas_data_model::Client);
method id (line 154) | pub async fn id(&self) -> ID {
method client_id (line 159) | pub async fn client_id(&self) -> &str {
method client_name (line 164) | pub async fn client_name(&self) -> Option<&str> {
method client_uri (line 169) | pub async fn client_uri(&self) -> Option<&Url> {
method logo_uri (line 174) | pub async fn logo_uri(&self) -> Option<&Url> {
method tos_uri (line 179) | pub async fn tos_uri(&self) -> Option<&Url> {
method policy_uri (line 184) | pub async fn policy_uri(&self) -> Option<&Url> {
method redirect_uris (line 189) | pub async fn redirect_uris(&self) -> &[Url] {
method application_type (line 194) | pub async fn application_type(&self) -> Option<OAuth2ApplicationType> {
FILE: crates/handlers/src/graphql/model/site_config.rs
constant SITE_CONFIG_ID (line 12) | pub const SITE_CONFIG_ID: &str = "site_config";
constant CAPTCHA_CONFIG_ID (line 13) | pub const CAPTCHA_CONFIG_ID: &str = "captcha_config";
type SiteConfig (line 18) | pub struct SiteConfig {
method id (line 108) | pub async fn id(&self) -> ID {
method new (line 116) | pub fn new(data_model: &mas_data_model::SiteConfig) -> Self {
type CaptchaConfig (line 69) | pub struct CaptchaConfig {
method id (line 142) | pub async fn id(&self) -> ID {
method new (line 150) | pub fn new(data_model: &mas_data_model::CaptchaConfig) -> Self {
type CaptchaService (line 79) | pub enum CaptchaService {
type SessionLimitConfig (line 87) | pub struct SessionLimitConfig {
method new (line 96) | pub fn new(data_model: &mas_data_model::SessionLimitConfig) -> Self {
FILE: crates/handlers/src/graphql/model/upstream_oauth.rs
type UpstreamOAuth2Provider (line 17) | pub struct UpstreamOAuth2Provider {
method new (line 23) | pub const fn new(provider: mas_data_model::UpstreamOAuthProvider) -> S...
method id (line 31) | pub async fn id(&self) -> ID {
method created_at (line 36) | pub async fn created_at(&self) -> DateTime<Utc> {
method issuer (line 41) | pub async fn issuer(&self) -> Option<&str> {
method client_id (line 46) | pub async fn client_id(&self) -> &str {
method human_name (line 51) | pub async fn human_name(&self) -> Option<&str> {
method brand_name (line 58) | pub async fn brand_name(&self) -> Option<&str> {
method link_url (line 63) | pub async fn link_url(&self, context: &Context<'_>) -> Url {
type UpstreamOAuth2Link (line 83) | pub struct UpstreamOAuth2Link {
method new (line 73) | pub const fn new(link: mas_data_model::UpstreamOAuthLink) -> Self {
method id (line 92) | pub async fn id(&self) -> ID {
method created_at (line 97) | pub async fn created_at(&self) -> DateTime<Utc> {
method subject (line 102) | pub async fn subject(&self) -> &str {
method human_account_name (line 107) | pub async fn human_account_name(&self) -> Option<&str> {
method provider (line 112) | pub async fn provider(
method user (line 138) | pub async fn user(&self, ctx: &Context<'_>) -> Result<Option<User>, as...
FILE: crates/handlers/src/graphql/model/users.rs
type User (line 33) | pub struct User(pub mas_data_model::User);
method from (line 36) | fn from(v: mas_data_model::User) -> Self {
method from (line 42) | fn from(v: mas_data_model::BrowserSession) -> Self {
method id (line 50) | pub async fn id(&self) -> ID {
method username (line 55) | async fn username(&self) -> &str {
method created_at (line 60) | pub async fn created_at(&self) -> DateTime<Utc> {
method locked_at (line 65) | pub async fn locked_at(&self) -> Option<DateTime<Utc>> {
method can_request_admin (line 70) | pub async fn can_request_admin(&self) -> bool {
method matrix (line 75) | async fn matrix(&self, ctx: &Context<'_>) -> Result<MatrixUser, async_...
method compat_sso_logins (line 82) | async fn compat_sso_logins(
method compat_sessions (line 143) | async fn compat_sessions(
method browser_sessions (line 237) | async fn browser_sessions(
method emails (line 321) | async fn emails(
method oauth2_sessions (line 390) | async fn oauth2_sessions(
method upstream_oauth2_links (line 496) | async fn upstream_oauth2_links(
method app_sessions (line 565) | async fn app_sessions(
method has_password (line 709) | async fn has_password(&self, ctx: &Context<'_>) -> Result<bool, async_...
type AppSession (line 721) | pub enum AppSession {
type UserEmail (line 728) | pub struct UserEmail(pub mas_data_model::UserEmail);
method id (line 733) | pub async fn id(&self) -> ID {
method email (line 738) | async fn email(&self) -> &str {
method created_at (line 743) | pub async fn created_at(&self) -> DateTime<Utc> {
method confirmed_at (line 750) | async fn confirmed_at(&self) -> Option<DateTime<Utc>> {
type UserEmailState (line 757) | pub enum UserEmailState {
type UserRecoveryTicket (line 767) | pub struct UserRecoveryTicket(pub mas_data_model::UserRecoveryTicket);
method id (line 785) | pub async fn id(&self) -> ID {
method created_at (line 790) | pub async fn created_at(&self) -> DateTime<Utc> {
method status (line 795) | pub async fn status(
method username (line 823) | pub async fn username(&self, ctx: &Context<'_>) -> Result<String, asyn...
method email (line 846) | pub async fn email(&self, ctx: &Context<'_>) -> Result<String, async_g...
type UserRecoveryTicketStatus (line 771) | pub enum UserRecoveryTicketStatus {
type UserEmailAuthentication (line 865) | pub struct UserEmailAuthentication(pub mas_data_model::UserEmailAuthenti...
method id (line 870) | pub async fn id(&self) -> ID {
method created_at (line 875) | pub async fn created_at(&self) -> DateTime<Utc> {
method completed_at (line 880) | pub async fn completed_at(&self) -> Option<DateTime<Utc>> {
method email (line 885) | pub async fn email(&self) -> &str {
FILE: crates/handlers/src/graphql/model/viewer/anonymous.rs
type Anonymous (line 11) | pub struct Anonymous;
method id (line 15) | pub async fn id(&self) -> ID {
FILE: crates/handlers/src/graphql/model/viewer/mod.rs
type Viewer (line 16) | pub enum Viewer {
method user (line 22) | pub fn user(user: mas_data_model::User) -> Self {
method anonymous (line 26) | pub fn anonymous() -> Self {
type ViewerSession (line 33) | pub enum ViewerSession {
method browser_session (line 40) | pub fn browser_session(session: mas_data_model::BrowserSession) -> Self {
method oauth2_session (line 44) | pub fn oauth2_session(session: mas_data_model::Session) -> Self {
method anonymous (line 48) | pub fn anonymous() -> Self {
FILE: crates/handlers/src/graphql/mutations/browser_session.rs
type BrowserSessionMutations (line 16) | pub struct BrowserSessionMutations {
method end_browser_session (line 64) | async fn end_browser_session(
type EndBrowserSessionInput (line 22) | pub struct EndBrowserSessionInput {
type EndBrowserSessionPayload (line 28) | pub enum EndBrowserSessionPayload {
method status (line 46) | async fn status(&self) -> EndBrowserSessionStatus {
method browser_session (line 54) | async fn browser_session(&self) -> Option<BrowserSession> {
type EndBrowserSessionStatus (line 35) | enum EndBrowserSessionStatus {
FILE: crates/handlers/src/graphql/mutations/compat_session.rs
type CompatSessionMutations (line 21) | pub struct CompatSessionMutations {
method end_compat_session (line 117) | async fn end_compat_session(
method set_compat_session_name (line 157) | async fn set_compat_session_name(
type EndCompatSessionInput (line 27) | pub struct EndCompatSessionInput {
type EndCompatSessionPayload (line 33) | pub enum EndCompatSessionPayload {
method status (line 51) | async fn status(&self) -> EndCompatSessionStatus {
method compat_session (line 59) | async fn compat_session(&self) -> Option<CompatSession> {
type EndCompatSessionStatus (line 40) | enum EndCompatSessionStatus {
type SetCompatSessionNameInput (line 69) | pub struct SetCompatSessionNameInput {
type SetCompatSessionNamePayload (line 78) | pub enum SetCompatSessionNamePayload {
method status (line 99) | async fn status(&self) -> SetCompatSessionNameStatus {
method oauth2_session (line 107) | async fn oauth2_session(&self) -> Option<CompatSession> {
type SetCompatSessionNameStatus (line 88) | enum SetCompatSessionNameStatus {
FILE: crates/handlers/src/graphql/mutations/matrix.rs
type MatrixMutations (line 17) | pub struct MatrixMutations {
method set_display_name (line 69) | async fn set_display_name(
type SetDisplayNameInput (line 23) | struct SetDisplayNameInput {
type SetDisplayNameStatus (line 33) | pub enum SetDisplayNameStatus {
type SetDisplayNamePayload (line 42) | enum SetDisplayNamePayload {
method status (line 50) | async fn status(&self) -> SetDisplayNameStatus {
method user (line 58) | async fn user(&self) -> Option<&User> {
FILE: crates/handlers/src/graphql/mutations/mod.rs
type Mutation (line 25) | pub struct Mutation(
method new (line 36) | pub fn new() -> Self {
function verify_password_if_needed (line 45) | async fn verify_password_if_needed(
FILE: crates/handlers/src/graphql/mutations/oauth2_session.rs
type OAuth2SessionMutations (line 28) | pub struct OAuth2SessionMutations {
method create_oauth2_session (line 166) | async fn create_oauth2_session(
method end_oauth2_session (line 256) | async fn end_oauth2_session(
method set_oauth2_session_name (line 298) | async fn set_oauth2_session_name(
type CreateOAuth2SessionInput (line 34) | pub struct CreateOAuth2SessionInput {
type CreateOAuth2SessionPayload (line 47) | pub struct CreateOAuth2SessionPayload {
method access_token (line 56) | pub async fn access_token(&self) -> &str {
method refresh_token (line 61) | pub async fn refresh_token(&self) -> Option<&str> {
method oauth2_session (line 66) | pub async fn oauth2_session(&self) -> OAuth2Session {
type EndOAuth2SessionInput (line 73) | pub struct EndOAuth2SessionInput {
type EndOAuth2SessionPayload (line 79) | pub enum EndOAuth2SessionPayload {
method status (line 97) | async fn status(&self) -> EndOAuth2SessionStatus {
method oauth2_session (line 105) | async fn oauth2_session(&self) -> Option<OAuth2Session> {
type EndOAuth2SessionStatus (line 86) | enum EndOAuth2SessionStatus {
type SetOAuth2SessionNameInput (line 115) | pub struct SetOAuth2SessionNameInput {
type SetOAuth2SessionNamePayload (line 124) | pub enum SetOAuth2SessionNamePayload {
method status (line 145) | async fn status(&self) -> SetOAuth2SessionNameStatus {
method oauth2_session (line 153) | async fn oauth2_session(&self) -> Option<OAuth2Session> {
type SetOAuth2SessionNameStatus (line 134) | enum SetOAuth2SessionNameStatus {
FILE: crates/handlers/src/graphql/mutations/user.rs
type UserMutations (line 29) | pub struct UserMutations {
method add_user (line 475) | async fn add_user(
method lock_user (line 527) | async fn lock_user(
method unlock_user (line 573) | async fn unlock_user(
method set_can_request_admin (line 616) | async fn set_can_request_admin(
method allow_user_cross_signing_reset (line 648) | async fn allow_user_cross_signing_reset(
method set_password (line 683) | async fn set_password(
method set_password_by_recovery (line 792) | async fn set_password_by_recovery(
method resend_recovery_email (line 896) | pub async fn resend_recovery_email(
method deactivate_user (line 945) | async fn deactivate_user(
type AddUserInput (line 35) | struct AddUserInput {
type AddUserStatus (line 49) | enum AddUserStatus {
type AddUserPayload (line 65) | enum AddUserPayload {
method status (line 75) | async fn status(&self) -> AddUserStatus {
method user (line 85) | async fn user(&self) -> Option<User> {
type LockUserInput (line 95) | struct LockUserInput {
type LockUserStatus (line 105) | enum LockUserStatus {
type LockUserPayload (line 115) | enum LockUserPayload {
method status (line 126) | async fn status(&self) -> LockUserStatus {
method user (line 134) | async fn user(&self) -> Option<User> {
type UnlockUserInput (line 144) | struct UnlockUserInput {
type UnlockUserStatus (line 151) | enum UnlockUserStatus {
type UnlockUserPayload (line 161) | enum UnlockUserPayload {
method status (line 172) | async fn status(&self) -> UnlockUserStatus {
method user (line 180) | async fn user(&self) -> Option<User> {
type SetCanRequestAdminInput (line 190) | struct SetCanRequestAdminInput {
type SetCanRequestAdminPayload (line 200) | enum SetCanRequestAdminPayload {
method user (line 211) | async fn user(&self) -> Option<User> {
type AllowUserCrossSigningResetInput (line 221) | struct AllowUserCrossSigningResetInput {
type AllowUserCrossSigningResetPayload (line 228) | enum AllowUserCrossSigningResetPayload {
method user (line 239) | async fn user(&self) -> Option<User> {
type SetPasswordInput (line 249) | struct SetPasswordInput {
type SetPasswordByRecoveryInput (line 265) | struct SetPasswordByRecoveryInput {
type SetPasswordPayload (line 277) | struct SetPasswordPayload {
method status (line 327) | async fn status(&self) -> SetPasswordStatus {
type SetPasswordStatus (line 283) | enum SetPasswordStatus {
type ResendRecoveryEmailInput (line 334) | pub struct ResendRecoveryEmailInput {
type ResendRecoveryEmailPayload (line 341) | pub enum ResendRecoveryEmailPayload {
method status (line 363) | async fn status(&self) -> ResendRecoveryEmailStatus {
method progress_url (line 372) | async fn progress_url(&self, context: &Context<'_>) -> Option<Url> {
type ResendRecoveryEmailStatus (line 349) | pub enum ResendRecoveryEmailStatus {
type DeactivateUserInput (line 389) | pub struct DeactivateUserInput {
type DeactivateUserPayload (line 406) | pub enum DeactivateUserPayload {
method status (line 427) | async fn status(&self) -> DeactivateUserStatus {
method user (line 434) | async fn user(&self) -> Option<User> {
type DeactivateUserStatus (line 416) | pub enum DeactivateUserStatus {
function valid_username_character (line 442) | fn valid_username_character(c: char) -> bool {
function username_valid (line 454) | fn username_valid(username: &str) -> bool {
FILE: crates/handlers/src/graphql/mutations/user_email.rs
type UserEmailMutations (line 23) | pub struct UserEmailMutations {
method add_email (line 416) | async fn add_email(
method remove_email (line 487) | async fn remove_email(
method set_primary_email (line 552) | async fn set_primary_email(
method start_email_authentication (line 591) | async fn start_email_authentication(
method resend_email_authentication_code (line 703) | async fn resend_email_authentication_code(
method complete_email_authentication (line 766) | async fn complete_email_authentication(
type AddEmailInput (line 29) | struct AddEmailInput {
type AddEmailStatus (line 45) | pub enum AddEmailStatus {
type AddEmailPayload (line 58) | enum AddEmailPayload {
method status (line 70) | async fn status(&self) -> AddEmailStatus {
method email (line 80) | async fn email(&self) -> Option<UserEmail> {
method user (line 90) | async fn user(&self, ctx: &Context<'_>) -> Result<Option<User>, async_...
method violations (line 109) | async fn violations(&self) -> Option<Vec<String>> {
type RemoveEmailInput (line 121) | struct RemoveEmailInput {
type RemoveEmailStatus (line 132) | enum RemoveEmailStatus {
type RemoveEmailPayload (line 145) | enum RemoveEmailPayload {
method status (line 154) | async fn status(&self) -> RemoveEmailStatus {
method email (line 163) | async fn email(&self) -> Option<UserEmail> {
method user (line 171) | async fn user(&self, ctx: &Context<'_>) -> Result<Option<User>, async_...
type SetPrimaryEmailInput (line 195) | struct SetPrimaryEmailInput {
type SetPrimaryEmailStatus (line 202) | enum SetPrimaryEmailStatus {
type SetPrimaryEmailPayload (line 213) | enum SetPrimaryEmailPayload {
method status (line 220) | async fn status(&self) -> SetPrimaryEmailStatus {
method user (line 228) | async fn user(&self) -> Option<User> {
type StartEmailAuthenticationInput (line 238) | struct StartEmailAuthenticationInput {
type StartEmailAuthenticationStatus (line 253) | enum StartEmailAuthenticationStatus {
type StartEmailAuthenticationPayload (line 270) | enum StartEmailAuthenticationPayload {
method status (line 284) | async fn status(&self) -> StartEmailAuthenticationStatus {
method authentication (line 296) | async fn authentication(&self) -> Option<&UserEmailAuthentication> {
method violations (line 308) | async fn violations(&self) -> Option<Vec<String>> {
type CompleteEmailAuthenticationInput (line 320) | struct CompleteEmailAuthenticationInput {
type CompleteEmailAuthenticationPayload (line 330) | enum CompleteEmailAuthenticationPayload {
method status (line 356) | async fn status(&self) -> CompleteEmailAuthenticationStatus {
type CompleteEmailAuthenticationStatus (line 340) | enum CompleteEmailAuthenticationStatus {
type ResendEmailAuthenticationCodeInput (line 369) | struct ResendEmailAuthenticationCodeInput {
type ResendEmailAuthenticationCodePayload (line 380) | enum ResendEmailAuthenticationCodePayload {
method status (line 403) | async fn status(&self) -> ResendEmailAuthenticationCodeStatus {
type ResendEmailAuthenticationCodeStatus (line 391) | enum ResendEmailAuthenticationCodeStatus {
FILE: crates/handlers/src/graphql/query/mod.rs
type Query (line 29) | pub struct Query(
method new (line 39) | pub fn new() -> Self {
type BaseQuery (line 45) | struct BaseQuery;
method current_browser_session (line 52) | async fn current_browser_session(
method current_user (line 65) | async fn current_user(&self, ctx: &Context<'_>) -> Result<Option<User>...
method oauth2_client (line 71) | async fn oauth2_client(
method browser_session (line 87) | async fn browser_session(
method compat_session (line 112) | async fn compat_session(
method oauth2_session (line 137) | async fn oauth2_session(
method user_email (line 162) | async fn user_email(
method user_recovery_ticket (line 187) | async fn user_recovery_ticket(
method user_email_authentication (line 201) | async fn user_email_authentication(
method node (line 227) | async fn node(&self, ctx: &Context<'_>, id: ID) -> Result<Option<Node>...
method site_config (line 297) | async fn site_config(&self, ctx: &Context<'_>) -> SiteConfig {
FILE: crates/handlers/src/graphql/query/session.rs
type SessionQuery (line 22) | pub struct SessionQuery;
method session (line 34) | async fn session(
type Session (line 26) | enum Session {
FILE: crates/handlers/src/graphql/query/upstream_oauth.rs
type UpstreamOAuthQuery (line 22) | pub struct UpstreamOAuthQuery;
method upstream_oauth2_link (line 27) | pub async fn upstream_oauth2_link(
method upstream_oauth2_provider (line 52) | pub async fn upstream_oauth2_provider(
method upstream_oauth2_providers (line 77) | async fn upstream_oauth2_providers(
FILE: crates/handlers/src/graphql/query/user.rs
type UserQuery (line 20) | pub struct UserQuery;
method user (line 25) | pub async fn user(
method user_by_username (line 49) | async fn user_by_username(
method users (line 75) | async fn users(
type UserState (line 162) | enum UserState {
FILE: crates/handlers/src/graphql/query/viewer.rs
type ViewerQuery (line 15) | pub struct ViewerQuery;
method viewer (line 20) | async fn viewer(&self, ctx: &Context<'_>) -> Viewer {
method viewer_session (line 31) | async fn viewer_session(&self, ctx: &Context<'_>) -> ViewerSession {
FILE: crates/handlers/src/graphql/state.rs
constant CLEAR_SESSION_SENTINEL (line 16) | const CLEAR_SESSION_SENTINEL: &str = "__CLEAR_SESSION__";
type State (line 19) | pub trait State {
method repository (line 20) | async fn repository(&self) -> Result<BoxRepository, RepositoryError>;
method policy (line 21) | async fn policy(&self) -> Result<Policy, mas_policy::InstantiateError>;
method password_manager (line 22) | fn password_manager(&self) -> PasswordManager;
method homeserver_connection (line 23) | fn homeserver_connection(&self) -> &dyn HomeserverConnection;
method clock (line 24) | fn clock(&self) -> BoxClock;
method rng (line 25) | fn rng(&self) -> BoxRng;
method site_config (line 26) | fn site_config(&self) -> &SiteConfig;
method url_builder (line 27) | fn url_builder(&self) -> &UrlBuilder;
method limiter (line 28) | fn limiter(&self) -> &Limiter;
type BoxState (line 31) | pub type BoxState = Box<dyn State + Send + Sync + 'static>;
type ContextExt (line 33) | pub trait ContextExt {
method state (line 34) | fn state(&self) -> &BoxState;
method mark_session_ended (line 36) | fn mark_session_ended(&self);
method requester (line 38) | fn requester(&self) -> &Requester;
method state (line 42) | fn state(&self) -> &BoxState {
method mark_session_ended (line 46) | fn mark_session_ended(&self) {
method requester (line 54) | fn requester(&self) -> &Requester {
function has_session_ended (line 63) | pub fn has_session_ended(response: &mut Response) -> bool {
FILE: crates/handlers/src/graphql/tests.rs
function create_test_client (line 27) | async fn create_test_client(state: &TestState) -> Client {
function create_test_user (line 62) | async fn create_test_user<U: Into<String> + Send>(state: &TestState, use...
function start_oauth_session (line 78) | async fn start_oauth_session(
constant GRAPHQL (line 112) | const GRAPHQL: ScopeToken = ScopeToken::from_static("urn:mas:graphql:*");
constant ADMIN (line 113) | const ADMIN: ScopeToken = ScopeToken::from_static("urn:mas:admin");
type GraphQLResponse (line 116) | struct GraphQLResponse {
function test_get (line 125) | async fn test_get(pool: PgPool) {
function test_anonymous_viewer (line 149) | async fn test_anonymous_viewer(pool: PgPool) {
function test_oauth2_viewer (line 180) | async fn test_oauth2_viewer(pool: PgPool) {
function test_oauth2_no_scope (line 227) | async fn test_oauth2_no_scope(pool: PgPool) {
function test_oauth2_admin (line 265) | async fn test_oauth2_admin(pool: PgPool) {
function test_oauth2_client_credentials (line 352) | async fn test_oauth2_client_credentials(pool: PgPool) {
function test_add_user (line 580) | async fn test_add_user(pool: PgPool) {
function test_set_password_rejected_wrong_password (line 793) | async fn test_set_password_rejected_wrong_password(pool: PgPool) {
function test_start_email_authentication_rejected_wrong_password (line 867) | async fn test_start_email_authentication_rejected_wrong_password(pool: P...
function test_remove_email_rejected_wrong_password (line 938) | async fn test_remove_email_rejected_wrong_password(pool: PgPool) {
function test_deactivate_user_rejected_wrong_password (line 1020) | async fn test_deactivate_user_rejected_wrong_password(pool: PgPool) {
FILE: crates/handlers/src/health.rs
function get (line 12) | pub async fn get(State(pool): State<PgPool>) -> Result<impl IntoResponse...
function test_get_health (line 32) | async fn test_get_health(pool: PgPool) {
FILE: crates/handlers/src/lib.rs
function healthcheck_router (line 112) | pub fn healthcheck_router<S>() -> Router<S>
function graphql_router (line 120) | pub fn graphql_router<S>(playground: bool, undocumented_oauth2_access: b...
function discovery_router (line 165) | pub fn discovery_router<S>() -> Router<S>
function api_router (line 198) | pub fn api_router<S>() -> Router<S>
function compat_router (line 263) | pub fn compat_router<S>(templates: Templates) -> Router<S>
function human_router (line 335) | pub fn human_router<S>(templates: Templates) -> Router<S>
function recover_error (line 476) | fn recover_error(
function fallback (line 499) | pub async fn fallback(
FILE: crates/handlers/src/oauth2/authorization/callback.rs
type CallbackDestinationMode (line 21) | enum CallbackDestinationMode {
type CallbackDestination (line 30) | pub struct CallbackDestination {
type Error (line 58) | type Error = IntoCallbackDestinationError;
method try_from (line 60) | fn try_from(value: &AuthorizationGrant) -> Result<Self, Self::Error> {
method try_new (line 70) | pub fn try_new(
method go (line 104) | pub fn go<T: Serialize + Send + Sync>(
type IntoCallbackDestinationError (line 37) | pub enum IntoCallbackDestinationError {
type CallbackDestinationError (line 49) | pub enum CallbackDestinationError {
function test_query_mode_location_header (line 204) | async fn test_query_mode_location_header(pool: PgPool) {
FILE: crates/handlers/src/oauth2/authorization/consent.rs
type RouteError (line 42) | pub enum RouteError {
method into_response (line 69) | fn into_response(self) -> axum::response::Response {
function get (line 87) | pub(crate) async fn get(
function post (line 217) | pub(crate) async fn post(
FILE: crates/handlers/src/oauth2/authorization/mod.rs
type RouteError (line 37) | pub enum RouteError {
method into_response (line 55) | fn into_response(self) -> axum::response::Response {
type Params (line 75) | pub(crate) struct Params {
function resolve_response_mode (line 86) | fn resolve_response_mode(
function get (line 112) | pub(crate) async fn get(
FILE: crates/handlers/src/oauth2/device/authorize.rs
type RouteError (line 32) | pub(crate) enum RouteError {
method into_response (line 63) | fn into_response(self) -> axum::response::Response {
function post (line 98) | pub(crate) async fn post(
function test_device_code_request (line 213) | async fn test_device_code_request(pool: PgPool) {
function test_device_code_request_disabled (line 248) | async fn test_device_code_request_disabled(pool: PgPool) {
FILE: crates/handlers/src/oauth2/device/consent.rs
type Action (line 38) | enum Action {
type ConsentForm (line 44) | pub(crate) struct ConsentForm {
function get (line 49) | pub(crate) async fn get(
function post (line 193) | pub(crate) async fn post(
FILE: crates/handlers/src/oauth2/device/link.rs
type Params (line 24) | pub struct Params {
function get (line 30) | pub(crate) async fn get(
FILE: crates/handlers/src/oauth2/discovery.rs
type DiscoveryResponse (line 25) | struct DiscoveryResponse {
function get (line 38) | pub(crate) async fn get(
function test_valid_discovery_metadata (line 218) | async fn test_valid_discovery_metadata(pool: PgPool) {
function test_discovery_device_code_grant_disabled (line 233) | async fn test_discovery_device_code_grant_disabled(pool: PgPool) {
FILE: crates/handlers/src/oauth2/introspection.rs
constant KIND (line 49) | const KIND: Key = Key::from_static_str("kind");
constant ACTIVE (line 50) | const ACTIVE: Key = Key::from_static_str("active");
type RouteError (line 53) | pub enum RouteError {
method into_response (line 133) | fn into_response(self) -> axum::response::Response {
constant INACTIVE (line 212) | const INACTIVE: IntrospectionResponse = IntrospectionResponse {
constant UNSTABLE_API_SCOPE (line 229) | const UNSTABLE_API_SCOPE: ScopeToken =
constant STABLE_API_SCOPE (line 231) | const STABLE_API_SCOPE: ScopeToken = ScopeToken::from_static("urn:matrix...
constant SYNAPSE_ADMIN_SCOPE (line 232) | const SYNAPSE_ADMIN_SCOPE: ScopeToken = ScopeToken::from_static("urn:syn...
function normalize_scope (line 236) | fn normalize_scope(mut scope: Scope) -> Scope {
function post (line 261) | pub(crate) async fn post(
function test_introspect_oauth_tokens (line 768) | async fn test_introspect_oauth_tokens(pool: PgPool) {
function test_introspect_compat_tokens (line 981) | async fn test_introspect_compat_tokens(pool: PgPool) {
function test_introspect_with_bearer_token (line 1158) | async fn test_introspect_with_bearer_token(pool: PgPool) {
function test_introspect_personal_access_tokens (line 1183) | async fn test_introspect_personal_access_tokens(pool: PgPool) {
FILE: crates/handlers/src/oauth2/keys.rs
function get (line 11) | pub(crate) async fn get(State(key_store): State<Keystore>) -> impl IntoR...
FILE: crates/handlers/src/oauth2/mod.rs
type IdTokenSignatureError (line 38) | pub(crate) enum IdTokenSignatureError {
function generate_id_token (line 47) | pub(crate) fn generate_id_token(
function generate_token_pair (line 98) | pub(crate) async fn generate_token_pair<R: RepositoryAccess>(
FILE: crates/handlers/src/oauth2/registration.rs
constant RESULT (line 44) | const RESULT: Key = Key::from_static_str("result");
type RouteError (line 47) | pub(crate) enum RouteError {
method into_response (line 71) | fn into_response(self) -> axum::response::Response {
type RouteResponse (line 180) | struct RouteResponse {
function host_is_public_suffix (line 188) | fn host_is_public_suffix(url: &Url) -> bool {
function localised_url_has_public_suffix (line 213) | fn localised_url_has_public_suffix(url: &Localized<Url>) -> bool {
function post (line 218) | pub(crate) async fn post(
function test_public_suffix_list (line 415) | fn test_public_suffix_list() {
function test_registration_error (line 435) | async fn test_registration_error(pool: PgPool) {
function test_registration (line 528) | async fn test_registration(pool: PgPool) {
function test_registration_dedupe (line 565) | async fn test_registration_dedupe(pool: PgPool) {
function test_registration_device_code_grant_disabled (line 636) | async fn test_registration_device_code_grant_disabled(pool: PgPool) {
FILE: crates/handlers/src/oauth2/revoke.rs
type RouteError (line 30) | pub(crate) enum RouteError {
method from (line 112) | fn from(_e: mas_data_model::TokenFormatError) -> Self {
method into_response (line 68) | fn into_response(self) -> axum::response::Response {
function post (line 122) | pub(crate) async fn post(
function test_revoke_access_token (line 271) | async fn test_revoke_access_token(pool: PgPool) {
FILE: crates/handlers/src/oauth2/token.rs
constant GRANT_TYPE (line 62) | const GRANT_TYPE: Key = Key::from_static_str("grant_type");
constant RESULT (line 63) | const RESULT: Key = Key::from_static_str("successful");
type RouteError (line 66) | pub(crate) enum RouteError {
method into_response (line 162) | fn into_response(self) -> axum::response::Response {
function post (line 274) | pub(crate) async fn post(
function authorization_code_grant (line 409) | async fn authorization_code_grant(
function refresh_token_grant (line 610) | async fn refresh_token_grant(
function client_credentials_grant (line 782) | async fn client_credentials_grant(
function device_code_grant (line 860) | async fn device_code_grant(
function test_auth_code_grant (line 1036) | async fn test_auth_code_grant(pool: PgPool) {
function test_refresh_token_grant (line 1245) | async fn test_refresh_token_grant(pool: PgPool) {
function test_double_refresh (line 1367) | async fn test_double_refresh(pool: PgPool) {
function test_client_credentials (line 1579) | async fn test_client_credentials(pool: PgPool) {
function test_device_code_grant (line 1711) | async fn test_device_code_grant(pool: PgPool) {
function test_unsupported_grant (line 1909) | async fn test_unsupported_grant(pool: PgPool) {
function test_device_code_grant_disabled (line 1947) | async fn test_device_code_grant_disabled(pool: PgPool) {
FILE: crates/handlers/src/oauth2/userinfo.rs
type UserInfo (line 35) | struct UserInfo {
type SignedUserInfo (line 41) | struct SignedUserInfo {
type RouteError (line 49) | pub enum RouteError {
method into_response (line 76) | fn into_response(self) -> axum::response::Response {
function get (line 101) | pub async fn get(
FILE: crates/handlers/src/oauth2/webfinger.rs
type Params (line 15) | pub(crate) struct Params {
function jrd (line 23) | fn jrd() -> mime::Mime {
function get (line 28) | pub(crate) async fn get(
FILE: crates/handlers/src/passwords.rs
type SchemeVersion (line 18) | pub type SchemeVersion = u16;
type PasswordVerificationResult (line 27) | pub enum PasswordVerificationResult<T = ()> {
function success (line 35) | fn success() -> Self {
function failure (line 39) | fn failure() -> Self {
function with_data (line 46) | fn with_data<N>(self, data: N) -> PasswordVerificationResult<N> {
function is_success (line 54) | pub fn is_success(&self) -> bool {
function from (line 60) | fn from(value: bool) -> Self {
type PasswordManagerDisabledError (line 71) | pub struct PasswordManagerDisabledError;
type PasswordManager (line 74) | pub struct PasswordManager {
method new (line 97) | pub fn new<I: IntoIterator<Item = (SchemeVersion, Hasher)>>(
method disabled (line 123) | pub const fn disabled() -> Self {
method is_enabled (line 129) | pub const fn is_enabled(&self) -> bool {
method get_inner (line 138) | fn get_inner(&self) -> Result<Arc<InnerPasswordManager>, PasswordManag...
method is_password_complex_enough (line 148) | pub fn is_password_complex_enough(
method hash (line 165) | pub async fn hash<R: CryptoRng + RngCore + Send>(
method verify (line 196) | pub async fn verify(
method verify_and_upgrade (line 232) | pub async fn verify_and_upgrade<R: CryptoRng + RngCore + Send>(
type InnerPasswordManager (line 78) | struct InnerPasswordManager {
type Hasher (line 259) | pub struct Hasher {
method bcrypt (line 268) | pub const fn bcrypt(
method argon2id (line 283) | pub const fn argon2id(pepper: Option<Vec<u8>>, unicode_normalization: ...
method pbkdf2 (line 294) | pub const fn pbkdf2(pepper: Option<Vec<u8>>, unicode_normalization: bo...
method normalize_password (line 303) | fn normalize_password(&self, password: Zeroizing<String>) -> Zeroizing...
method hash_blocking (line 313) | fn hash_blocking<R: CryptoRng + RngCore>(
method verify_blocking (line 324) | fn verify_blocking(
type Algorithm (line 337) | enum Algorithm {
method hash_blocking (line 344) | fn hash_blocking<R: CryptoRng + RngCore>(
method verify_blocking (line 392) | fn verify_blocking(
function hashing_bcrypt (line 456) | fn hashing_bcrypt() {
function hashing_argon2id (line 515) | fn hashing_argon2id() {
function hashing_pbkdf2 (line 575) | fn hashing_pbkdf2() {
function hash_verify_and_upgrade (line 634) | async fn hash_verify_and_upgrade() {
FILE: crates/handlers/src/preferred_language.rs
type PreferredLanguage (line 17) | pub struct PreferredLanguage(pub DataLocale);
type Rejection (line 24) | type Rejection = Infallible;
method from_request_parts (line 26) | async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Se...
FILE: crates/handlers/src/rate_limit.rs
type AccountRecoveryLimitedError (line 15) | pub enum AccountRecoveryLimitedError {
type PasswordCheckLimitedError (line 24) | pub enum PasswordCheckLimitedError {
type RegistrationLimitedError (line 33) | pub enum RegistrationLimitedError {
type EmailAuthenticationLimitedError (line 39) | pub enum EmailAuthenticationLimitedError {
type RequesterFingerprint (line 52) | pub struct RequesterFingerprint {
method fmt (line 57) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
constant EMPTY (line 69) | pub const EMPTY: Self = Self { ip: None };
method new (line 73) | pub const fn new(ip: IpAddr) -> Self {
type Limiter (line 80) | pub struct Limiter {
method new (line 133) | pub fn new(config: &RateLimitingConfig) -> Option<Self> {
method start (line 143) | pub fn start(&self) {
method check_account_recovery (line 179) | pub fn check_account_recovery(
method check_password (line 206) | pub fn check_password(
method check_registration (line 229) | pub fn check_registration(
method check_email_authentication_email (line 247) | pub fn check_email_authentication_email(
method check_email_authentication_attempt (line 273) | pub fn check_email_authentication_attempt(
method check_email_authentication_send_code (line 289) | pub fn check_email_authentication_send_code(
type KeyedRateLimiter (line 84) | type KeyedRateLimiter<K> = RateLimiter<K, DashMapStateStore<K>, QuantaCl...
type LimiterInner (line 87) | struct LimiterInner {
method new (line 100) | fn new(config: &RateLimitingConfig) -> Option<Self> {
function test_password_check_limiter (line 310) | fn test_password_check_limiter() {
FILE: crates/handlers/src/session.rs
type SessionLoadError (line 24) | pub enum SessionLoadError {
type SessionOrFallback (line 30) | pub enum SessionOrFallback {
function load_session_or_fallback (line 42) | pub async fn load_session_or_fallback(
function count_user_sessions_for_limiting (line 136) | pub(crate) async fn count_user_sessions_for_limiting(
FILE: crates/handlers/src/test_utils.rs
function setup (line 62) | pub(crate) fn setup() {
function policy_factory (line 71) | pub(crate) async fn policy_factory(
type TestState (line 97) | pub(crate) struct TestState {
method from_pool (line 159) | pub async fn from_pool(pool: PgPool) -> Result<Self, anyhow::Error> {
method from_pool_with_site_config (line 164) | pub async fn from_pool_with_site_config(
method run_jobs_in_queue (line 298) | pub async fn run_jobs_in_queue(&self) {
method restart (line 307) | pub async fn restart(self) -> Self {
method request (line 330) | pub async fn request<B>(&self, request: Request<B>) -> Response<String>
method token_with_scope (line 366) | pub async fn token_with_scope(&mut self, scope: &str) -> String {
method repository (line 413) | pub async fn repository(&self) -> Result<BoxRepository, RepositoryErro...
method rng (line 422) | pub fn rng(&self) -> ChaChaRng {
method is_access_token_valid (line 433) | pub async fn is_access_token_valid(&self, token: &str) -> bool {
method cookie_jar (line 448) | pub fn cookie_jar(&self) -> CookieJar {
function workspace_root (line 122) | fn workspace_root() -> camino::Utf8PathBuf {
function test_site_config (line 130) | pub fn test_site_config() -> SiteConfig {
type TestGraphQLState (line 453) | struct TestGraphQLState {
method repository (line 467) | async fn repository(&self) -> Result<BoxRepository, mas_storage::Repos...
method policy (line 471) | async fn policy(&self) -> Result<Policy, InstantiateError> {
method password_manager (line 475) | fn password_manager(&self) -> PasswordManager {
method homeserver_connection (line 479) | fn homeserver_connection(&self) -> &dyn HomeserverConnection {
method url_builder (line 483) | fn url_builder(&self) -> &UrlBuilder {
method clock (line 487) | fn clock(&self) -> BoxClock {
method site_config (line 491) | fn site_config(&self) -> &SiteConfig {
method limiter (line 495) | fn limiter(&self) -> &Limiter {
method rng (line 499) | fn rng(&self) -> BoxRng {
method from_ref (line 507) | fn from_ref(input: &TestState) -> Self {
method from_ref (line 513) | fn from_ref(input: &TestState) -> Self {
function from_ref (line 519) | fn from_ref(input: &TestState) -> Self {
method from_ref (line 525) | fn from_ref(input: &TestState) -> Self {
function from_ref (line 531) | fn from_ref(input: &TestState) -> Self {
method from_ref (line 537) | fn from_ref(input: &TestState) -> Self {
method from_ref (line 543) | fn from_ref(input: &TestState) -> Self {
method from_ref (line 549) | fn from_ref(input: &TestState) -> Self {
method from_ref (line 555) | fn from_ref(input: &TestState) -> Self {
method from_ref (line 561) | fn from_ref(input: &TestState) -> Self {
method from_ref (line 567) | fn from_ref(input: &TestState) -> Self {
method from_ref (line 573) | fn from_ref(input: &TestState) -> Self {
function from_ref (line 579) | fn from_ref(input: &TestState) -> Self {
function from_ref (line 585) | fn from_ref(input: &TestState) -> Self {
method from_ref (line 591) | fn from_ref(input: &TestState) -> Self {
function from_ref (line 597) | fn from_ref(input: &TestState) -> Self {
method from_ref (line 603) | fn from_ref(_input: &TestState) -> Self {
type Rejection (line 609) | type Rejection = Infallible;
method from_request_parts (line 611) | async fn from_request_parts(
type Rejection (line 620) | type Rejection = Infallible;
method from_request_parts (line 622) | async fn from_request_parts(
type Rejection (line 632) | type Rejection = Infallible;
method from_request_parts (line 634) | async fn from_request_parts(
type Rejection (line 643) | type Rejection = Infallible;
method from_request_parts (line 645) | async fn from_request_parts(
type Rejection (line 654) | type Rejection = Infallible;
method from_request_parts (line 656) | async fn from_request_parts(
type Rejection (line 667) | type Rejection = ErrorWrapper<RepositoryError>;
method from_request_parts (line 669) | async fn from_request_parts(
type Rejection (line 679) | type Rejection = ErrorWrapper<mas_policy::InstantiateError>;
method from_request_parts (line 681) | async fn from_request_parts(
type RequestBuilderExt (line 690) | pub(crate) trait RequestBuilderExt {
method json (line 692) | fn json<T: Serialize>(self, body: T) -> hyper::Request<String>;
method form (line 695) | fn form<T: Serialize>(self, body: T) -> hyper::Request<String>;
method bearer (line 698) | fn bearer(self, token: &str) -> Self;
method basic_auth (line 702) | fn basic_auth(self, username: &str, password: &str) -> Self;
method empty (line 705) | fn empty(self) -> hyper::Request<String>;
method json (line 709) | fn json<T: Serialize>(mut self, body: T) -> hyper::Request<String> {
method form (line 717) | fn form<T: Serialize>(mut self, body: T) -> hyper::Request<String> {
method bearer (line 726) | fn bearer(mut self, token: &str) -> Self {
method basic_auth (line 733) | fn basic_auth(mut self, username: &str, password: &str) -> Self {
method empty (line 740) | fn empty(self) -> hyper::Request<String> {
type ResponseExt (line 745) | pub(crate) trait ResponseExt {
method assert_status (line 751) | fn assert_status(&self, status: StatusCode);
method assert_header_value (line 759) | fn assert_header_value(&self, header: HeaderName, value: &str);
method json (line 767) | fn json<T: DeserializeOwned>(&self) -> T;
method assert_status (line 772) | fn assert_status(&self, status: StatusCode) {
method assert_header_value (line 784) | fn assert_header_value(&self, header: HeaderName, value: &str) {
method json (line 800) | fn json<T: DeserializeOwned>(&self) -> T {
type CookieHelper (line 808) | pub struct CookieHelper {
method new (line 813) | pub fn new() -> Self {
method with_cookies (line 818) | pub fn with_cookies<B>(&self, mut request: Request<B>) -> Request<B> {
method save_cookies (line 839) | pub fn save_cookies<B>(&self, response: &Response<B>) {
method import (line 860) | pub fn import(&self, res: impl IntoResponseParts) {
type Service (line 867) | type Service = CookieStoreService<S>;
method layer (line 869) | fn layer(&self, inner: S) -> Self::Service {
type CookieStoreService (line 878) | pub struct CookieStoreService<S> {
type Response (line 888) | type Response = S::Response;
type Error (line 889) | type Error = S::Error;
type Future (line 890) | type Future = BoxFuture<'static, Result<S::Response, S::Error>>;
function poll_ready (line 892) | fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::...
function call (line 896) | fn call(&mut self, request: Request<ReqBody>) -> Self::Future {
FILE: crates/handlers/src/upstream_oauth2/authorize.rs
type RouteError (line 31) | pub(crate) enum RouteError {
method into_response (line 44) | fn into_response(self) -> axum::response::Response {
function get (line 59) | pub(crate) async fn get(
FILE: crates/handlers/src/upstream_oauth2/backchannel_logout.rs
type RouteError (line 44) | pub enum RouteError {
method into_response (line 75) | fn into_response(self) -> axum::response::Response {
type BackchannelLogoutRequest (line 121) | pub(crate) struct BackchannelLogoutRequest {
type LogoutTokenEvents (line 126) | struct LogoutTokenEvents {
constant EVENTS (line 132) | const EVENTS: Claim<LogoutTokenEvents> = Claim::new("events");
function post (line 139) | pub(crate) async fn post(
FILE: crates/handlers/src/upstream_oauth2/cache.rs
type LazyProviderInfos (line 22) | pub struct LazyProviderInfos<'a> {
function new (line 30) | pub fn new(
function maybe_discover (line 45) | pub async fn maybe_discover(
function load (line 55) | async fn load(&mut self) -> Result<&VerifiedProviderMetadata, DiscoveryE...
function jwks_uri (line 81) | pub async fn jwks_uri(&mut self) -> Result<&Url, DiscoveryError> {
function authorization_endpoint (line 93) | pub async fn authorization_endpoint(&mut self) -> Result<&Url, Discovery...
function token_endpoint (line 105) | pub async fn token_endpoint(&mut self) -> Result<&Url, DiscoveryError> {
function userinfo_endpoint (line 117) | pub async fn userinfo_endpoint(&mut self) -> Result<&Url, DiscoveryError> {
function pkce_methods (line 129) | pub async fn pkce_methods(
type MetadataCache (line 152) | pub struct MetadataCache {
method new (line 159) | pub fn new() -> Self {
method warm_up_and_run (line 173) | pub async fn warm_up_and_run<R: RepositoryAccess>(
method fetch (line 213) | async fn fetch(
method get (line 249) | pub async fn get(
method refresh_all (line 272) | async fn refresh_all(&self, client: &reqwest::Client) {
function test_metadata_cache (line 320) | async fn test_metadata_cache() {
function test_lazy_provider_infos (line 383) | async fn test_lazy_provider_infos() {
FILE: crates/handlers/src/upstream_oauth2/callback.rs
constant PROVIDER (line 55) | const PROVIDER: Key = Key::from_static_str("provider");
constant RESULT (line 56) | const RESULT: Key = Key::from_static_str("result");
type Params (line 59) | pub struct Params {
method is_empty (line 84) | pub fn is_empty(&self) -> bool {
type RouteError (line 94) | pub(crate) enum RouteError {
method into_response (line 157) | fn into_response(self) -> axum::response::Response {
function handler (line 174) | pub(crate) async fn handler(
FILE: crates/handlers/src/upstream_oauth2/cookie.rs
type Payload (line 24) | pub struct Payload {
method expired (line 33) | fn expired(&self, now: DateTime<Utc>) -> bool {
type UpstreamSessions (line 45) | pub struct UpstreamSessions(Vec<Payload>);
method load (line 53) | pub fn load(cookie_jar: &CookieJar) -> Self {
method is_empty (line 65) | pub fn is_empty(&self) -> bool {
method save (line 70) | pub fn save<C>(self, cookie_jar: CookieJar, clock: &C) -> CookieJar
method expire (line 78) | fn expire(mut self, now: DateTime<Utc>) -> Self {
method add (line 84) | pub fn add(
method find_session (line 102) | pub fn find_session(
method add_link_to_session (line 115) | pub fn add_link_to_session(
method lookup_link (line 131) | pub fn lookup_link(
method consume_link (line 145) | pub fn consume_link(mut self, link_id: Ulid) -> Result<Self, UpstreamS...
type UpstreamSessionNotFound (line 49) | pub struct UpstreamSessionNotFound;
function test_session_cookie (line 167) | fn test_session_cookie() {
FILE: crates/handlers/src/upstream_oauth2/link.rs
constant PROVIDER (line 73) | const PROVIDER: Key = Key::from_static_str("provider");
constant DEFAULT_LOCALPART_TEMPLATE (line 75) | const DEFAULT_LOCALPART_TEMPLATE: &str = "{{ user.preferred_username }}";
constant DEFAULT_DISPLAYNAME_TEMPLATE (line 76) | const DEFAULT_DISPLAYNAME_TEMPLATE: &str = "{{ user.name }}";
constant DEFAULT_EMAIL_TEMPLATE (line 77) | const DEFAULT_EMAIL_TEMPLATE: &str = "{{ user.email }}";
type RouteError (line 80) | pub(crate) enum RouteError {
method into_response (line 137) | fn into_response(self) -> axum::response::Response {
function render_attribute_template (line 171) | fn render_attribute_template(
type FormData (line 206) | pub(crate) enum FormData {
type Field (line 221) | type Field = mas_templates::UpstreamRegisterFormField;
function get (line 229) | pub(crate) async fn get(
function post (line 847) | pub(crate) async fn post(
function prepare_user_registration (line 1195) | async fn prepare_user_registration(
function test_register (line 1275) | async fn test_register(pool: PgPool) {
function test_register_skip_confirmation (line 1469) | async fn test_register_skip_confirmation(pool: PgPool) {
function test_link_existing_account (line 1641) | async fn test_link_existing_account(pool: PgPool) {
function test_link_existing_account_when_not_allowed_by_default (line 1760) | async fn test_link_existing_account_when_not_allowed_by_default(pool: Pg...
function sign_token (line 1865) | fn sign_token(
function add_linked_upstream_session (line 1884) | async fn add_linked_upstream_session(
function test_link_existing_account_replace_conflict (line 1927) | async fn test_link_existing_account_replace_conflict(pool: PgPool) {
function test_link_existing_account_set_conflict_success (line 2076) | async fn test_link_existing_account_set_conflict_success(pool: PgPool) {
function test_link_existing_account_set_conflict_failure (line 2194) | async fn test_link_existing_account_set_conflict_failure(pool: PgPool) {
FILE: crates/handlers/src/upstream_oauth2/mod.rs
type ProviderCredentialsError (line 30) | enum ProviderCredentialsError {
type SignInWithApple (line 60) | pub struct SignInWithApple {
function client_credentials_for_provider (line 66) | fn client_credentials_for_provider(
FILE: crates/handlers/src/upstream_oauth2/template.rs
type AttributeMappingContext (line 24) | pub(crate) struct AttributeMappingContext {
method new (line 31) | pub fn new() -> Self {
method with_id_token_claims (line 35) | pub fn with_id_token_claims(
method with_extra_callback_parameters (line 43) | pub fn with_extra_callback_parameters(
method with_userinfo_claims (line 51) | pub fn with_userinfo_claims(mut self, userinfo_claims: serde_json::Val...
method build (line 56) | pub fn build(self) -> Value {
method get_value (line 62) | fn get_value(self: &Arc<Self>, name: &Value) -> Option<Value> {
method enumerate (line 91) | fn enumerate(self: &Arc<Self>) -> Enumerator {
function b64decode (line 109) | fn b64decode(value: &str) -> Result<Value, Error> {
function b64encode (line 129) | fn b64encode(bytes: &[u8]) -> String {
function tlvdecode (line 134) | fn tlvdecode(bytes: &[u8]) -> Result<HashMap<Value, Value>, Error> {
function string (line 164) | fn string(value: &Value) -> String {
function from_json (line 168) | fn from_json(value: &str) -> Result<Value, minijinja::Error> {
function environment (line 180) | pub fn environment() -> Environment<'static> {
function test_split (line 201) | fn test_split() {
function test_ilvdecode (line 210) | fn test_ilvdecode() {
function test_base64_decode (line 228) | fn test_base64_decode() {
FILE: crates/handlers/src/views/app.rs
type Params (line 25) | pub struct Params {
function get (line 34) | pub async fn get(
function get_anonymous (line 86) | pub async fn get_anonymous(
FILE: crates/handlers/src/views/index.rs
function get (line 24) | pub async fn get(
FILE: crates/handlers/src/views/login.rs
constant RESULT (line 52) | const RESULT: Key = Key::from_static_str("result");
type LoginForm (line 55) | pub(crate) struct LoginForm {
type Field (line 61) | type Field = LoginFormField;
function get (line 65) | pub(crate) async fn get(
function post (line 134) | pub(crate) async fn post(
function get_user_by_email_or_by_username (line 361) | async fn get_user_by_email_or_by_username<R: RepositoryAccess>(
function handle_login_hint (line 383) | fn handle_login_hint(
function render (line 406) | async fn render(
function test_password_disabled (line 472) | async fn test_password_disabled(pool: PgPool) {
function user_with_password (line 596) | async fn user_with_password(
function test_password_login (line 622) | async fn test_password_login(pool: PgPool) {
function test_password_login_with_mxid (line 669) | async fn test_password_login_with_mxid(pool: PgPool) {
function test_password_login_with_mxid_wrong_server (line 716) | async fn test_password_login_with_mxid_wrong_server(pool: PgPool) {
function test_password_login_rate_limit (line 756) | async fn test_password_login_rate_limit(pool: PgPool) {
function test_password_login_locked_account (line 825) | async fn test_password_login_locked_account(pool: PgPool) {
function test_password_login_deactivated_account (line 884) | async fn test_password_login_deactivated_account(pool: PgPool) {
FILE: crates/handlers/src/views/logout.rs
function post (line 23) | pub(crate) async fn post(
FILE: crates/handlers/src/views/recovery/progress.rs
function get (line 29) | pub(crate) async fn get(
function post (line 81) | pub(crate) async fn post(
FILE: crates/handlers/src/views/recovery/start.rs
type StartRecoveryForm (line 36) | pub(crate) struct StartRecoveryForm {
function get (line 40) | pub(crate) async fn get(
function post (line 76) | pub(crate) async fn post(
FILE: crates/handlers/src/views/register/cookie.rs
type UserRegistrationSessions (line 25) | pub struct UserRegistrationSessions(BTreeSet<Ulid>);
method load (line 33) | pub fn load(cookie_jar: &CookieJar) -> Self {
method is_empty (line 48) | pub fn is_empty(&self) -> bool {
method save (line 53) | pub fn save<C>(self, cookie_jar: CookieJar, clock: &C) -> CookieJar
method expire (line 66) | fn expire(mut self, now: DateTime<Utc>) -> Self {
method add (line 81) | pub fn add(mut self, user_registration: &UserRegistration) -> Self {
method contains (line 87) | pub fn contains(&self, user_registration: &UserRegistration) -> bool {
method consume_session (line 92) | pub fn consume_session(
type UserRegistrationSessionNotFound (line 29) | pub struct UserRegistrationSessionNotFound;
FILE: crates/handlers/src/views/register/mod.rs
function get (line 27) | pub(crate) async fn get(
FILE: crates/handlers/src/views/register/password.rs
type RegisterForm (line 46) | pub(crate) struct RegisterForm {
type Field (line 60) | type Field = RegisterFormField;
type QueryParams (line 64) | pub struct QueryParams {
function get (line 71) | pub(crate) async fn get(
function post (line 124) | pub(crate) async fn post(
function render (line 407) | async fn render(
function test_password_disabled (line 451) | async fn test_password_disabled(pool: PgPool) {
function test_register (line 484) | async fn test_register(pool: PgPool) {
function test_register_password_mismatch (line 550) | async fn test_register_password_mismatch(pool: PgPool) {
function test_register_username_too_long (line 591) | async fn test_register_username_too_long(pool: PgPool) {
function test_register_user_exists (line 637) | async fn test_register_user_exists(pool: PgPool) {
function test_register_user_reserved (line 689) | async fn test_register_user_reserved(pool: PgPool) {
function test_register_without_email_when_not_required (line 734) | async fn test_register_without_email_when_not_required(pool: PgPool) {
function test_register_with_email_when_not_required (line 803) | async fn test_register_with_email_when_not_required(pool: PgPool) {
function test_register_fails_without_email_when_required (line 873) | async fn test_register_fails_without_email_when_required(pool: PgPool) {
function test_register_fails_with_empty_email_when_required (line 931) | async fn test_register_fails_with_empty_email_when_required(pool: PgPool) {
function test_register_fails_with_invalid_email_when_required (line 990) | async fn test_register_fails_with_invalid_email_when_required(pool: PgPo...
FILE: crates/handlers/src/views/register/steps/display_name.rs
type FormAction (line 31) | enum FormAction {
type DisplayNameForm (line 38) | pub(crate) struct DisplayNameForm {
type Field (line 46) | type Field = mas_templates::RegisterStepsDisplayNameFormField;
function get (line 54) | pub(crate) async fn get(
function post (line 105) | pub(crate) async fn post(
FILE: crates/handlers/src/views/register/steps/finish.rs
function get (line 46) | pub(crate) async fn get(
FILE: crates/handlers/src/views/register/steps/registration_token.rs
type RegistrationTokenForm (line 30) | pub(crate) struct RegistrationTokenForm {
type Field (line 36) | type Field = mas_templates::RegisterStepsRegistrationTokenFormField;
function get (line 44) | pub(crate) async fn get(
function post (line 99) | pub(crate) async fn post(
FILE: crates/handlers/src/views/register/steps/verify_email.rs
type CodeForm (line 29) | pub struct CodeForm {
type Field (line 34) | type Field = mas_templates::RegisterStepsVerifyEmailFormField;
function get (line 42) | pub(crate) async fn get(
function post (line 111) | pub(crate) async fn post(
FILE: crates/handlers/src/views/shared.rs
type OptionalPostAuthAction (line 23) | pub(crate) struct OptionalPostAuthAction {
method from (line 29) | fn from(post_auth_action: Option<PostAuthAction>) -> Self {
method go_next_or_default (line 35) | pub fn go_next_or_default<T: Route>(
method go_next (line 46) | pub fn go_next(&self, url_builder: &UrlBuilder) -> axum::response::Red...
method load_context (line 50) | pub async fn load_context<'a>(
type LoginHint (line 114) | pub enum LoginHint<'a> {
type QueryLoginHint (line 121) | pub(crate) struct QueryLoginHint {
method parse_login_hint (line 133) | pub fn parse_login_hint(&self, homeserver: &str) -> LoginHint<'_> {
function no_login_hint (line 156) | fn no_login_hint() {
function valid_login_hint (line 165) | fn valid_login_hint() {
function valid_login_hint_with_email (line 176) | fn valid_login_hint_with_email() {
function invalid_login_hint (line 187) | fn invalid_login_hint() {
function valid_login_hint_for_wrong_homeserver (line 198) | fn valid_login_hint_for_wrong_homeserver() {
function unknown_login_hint_type (line 209) | fn unknown_login_hint_type() {
FILE: crates/http/src/ext.rs
function set_propagator (line 20) | pub fn set_propagator(propagator: &dyn opentelemetry::propagation::TextM...
type CorsLayerExt (line 35) | pub trait CorsLayerExt {
method allow_otel_headers (line 37) | fn allow_otel_headers<H>(self, headers: H) -> Self
method allow_otel_headers (line 43) | fn allow_otel_headers<H>(self, headers: H) -> Self
FILE: crates/http/src/reqwest.rs
type TracingResolver (line 58) | struct TracingResolver {
method new (line 63) | fn new() -> Self {
method resolve (line 70) | fn resolve(&self, name: reqwest::dns::Name) -> reqwest::dns::Resolving {
function client (line 92) | pub fn client() -> reqwest::Client {
function send_traced (line 111) | async fn send_traced(
type RequestBuilderExt (line 230) | pub trait RequestBuilderExt {
method send_traced (line 232) | fn send_traced(self) -> impl Future<Output = Result<reqwest::Response,...
method send_traced (line 236) | fn send_traced(self) -> impl Future<Output = Result<reqwest::Response,...
FILE: crates/i18n-scan/src/key.rs
type Context (line 10) | pub struct Context {
method new (line 17) | pub fn new(func: String) -> Self {
method set_current_file (line 25) | pub fn set_current_file(&mut self, file: &str) {
method record (line 29) | pub fn record(&mut self, key: Key) {
method func (line 33) | pub fn func(&self) -> &str {
method add_missing (line 37) | pub fn add_missing(&self, translation_tree: &mut TranslationTree) -> u...
method set_key_location (line 79) | pub fn set_key_location(&self, mut key: Key, span: Span) -> Key {
type Kind (line 92) | pub enum Kind {
type Location (line 98) | pub struct Location {
type Key (line 104) | pub struct Key {
method new (line 111) | pub fn new(kind: Kind, name: String) -> Self {
FILE: crates/i18n-scan/src/main.rs
type Options (line 25) | struct Options {
function main (line 46) | fn main() {
FILE: crates/i18n-scan/src/minijinja.rs
function find_in_stmt (line 15) | pub fn find_in_stmt<'a>(context: &mut Context, stmt: &'a Stmt<'a>) -> Re...
function as_const (line 93) | fn as_const<'a>(call_arg: &'a CallArg<'a>) -> Option<&'a Const> {
function find_in_macro (line 100) | fn find_in_macro<'a>(context: &mut Context, macro_: &'a Macro<'a>) -> Re...
function find_in_call (line 108) | fn find_in_call<'a>(
function find_in_call_args (line 151) | fn find_in_call_args<'a>(
function find_in_call_arg (line 162) | fn find_in_call_arg<'a>(
function find_in_stmts (line 174) | fn find_in_stmts<'a>(context: &mut Context, stmts: &'a [Stmt<'a>]) -> Re...
function find_in_expr (line 182) | fn find_in_expr<'a>(context: &mut Context, expr: &'a Expr<'a>) -> Result...
function find_in_exprs (line 234) | fn find_in_exprs<'a>(context: &mut Context, exprs: &'a [Expr<'a>]) -> Re...
function find_in_optional_expr (line 242) | fn find_in_optional_expr<'a>(
function test_find_keys (line 260) | fn test_find_keys() {
function test_invalid_key_not_string (line 336) | fn test_invalid_key_not_string() {
function test_invalid_key_filtered (line 352) | fn test_invalid_key_filtered() {
function test_invalid_key_missing (line 368) | fn test_invalid_key_missing() {
function test_invalid_key_negated (line 384) | fn test_invalid_key_negated() {
FILE: crates/i18n/src/sprintf/argument.rs
type List (line 13) | pub struct List {
method get_by_index (line 21) | pub fn get_by_index(&self, index: usize) -> Option<&Value> {
method get_by_name (line 27) | pub fn get_by_name(&self, name: &str) -> Option<&Value> {
method from_iter (line 35) | fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
type Argument (line 56) | pub struct Argument {
method from (line 62) | fn from(value: Value) -> Self {
method from (line 68) | fn from((name, value): (&str, Value)) -> Self {
method from (line 77) | fn from((name, value): (String, Value)) -> Self {
method named (line 88) | pub fn named(name: String, value: Value) -> Self {
method with_name (line 97) | pub fn with_name(mut self, name: String) -> Self {
function test_argument_list (line 110) | fn test_argument_list() {
FILE: crates/i18n/src/sprintf/formatter.rs
type ValueType (line 125) | pub enum ValueType {
method of_value (line 136) | fn of_value(value: &Value) -> Self {
type FormatError (line 149) | pub enum FormatError {
function find_value (line 178) | fn find_value<'a>(
function to_precision (line 199) | fn to_precision(number: f64, mut placeholder: Placeholder) -> String {
function format_value (line 230) | fn format_value(value: &Value, placeholder: &Placeholder) -> Result<Stri...
type FormattedMessagePart (line 479) | pub enum FormattedMessagePart<'a> {
function len (line 487) | fn len(&self) -> usize {
function fmt (line 496) | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
type FormattedMessage (line 504) | pub struct FormattedMessage<'a> {
function len (line 512) | pub fn len(&self) -> usize {
function is_empty (line 518) | pub fn is_empty(&self) -> bool {
function parts (line 524) | pub fn parts(&self) -> &[FormattedMessagePart<'_>] {
function fmt (line 530) | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
method format (line 545) | pub fn format(&self, arguments: &ArgumentList) -> Result<String, FormatE...
method format_ (line 550) | pub fn format_(&self, arguments: &ArgumentList) -> Result<FormattedMessa...
FILE: crates/i18n/src/sprintf/message.rs
type TypeSpecifier (line 11) | pub enum TypeSpecifier {
method is_numeric (line 64) | const fn is_numeric(self) -> bool {
method fmt (line 82) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type ArgumentReference (line 106) | pub enum ArgumentReference {
method fmt (line 112) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type PaddingSpecifier (line 121) | pub enum PaddingSpecifier {
method fmt (line 127) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method char (line 136) | pub fn char(self) -> char {
method is_zero (line 143) | pub const fn is_zero(self) -> bool {
type Placeholder (line 152) | pub struct Placeholder {
method padding_specifier_is_zero (line 163) | pub fn padding_specifier_is_zero(&self) -> bool {
method numeric_width (line 169) | pub fn numeric_width(&self) -> Option<usize> {
method fmt (line 176) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Message (line 207) | pub struct Message {
method fmt (line 212) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method from_iter (line 221) | fn from_iter<T: IntoIterator<Item = Part>>(iter: T) -> Self {
method parts (line 229) | pub(crate) fn parts(&self) -> std::slice::Iter<'_, Part> {
method from_literal (line 235) | pub fn from_literal(literal: String) -> Message {
method deserialize (line 250) | fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result...
method serialize (line 243) | fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok...
type Part (line 257) | pub(crate) enum Part {
method from (line 264) | fn from(placeholder: Placeholder) -> Self {
method from (line 270) | fn from(text: String) -> Self {
method fmt (line 276) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
FILE: crates/i18n/src/sprintf/mod.rs
type Error (line 76) | enum Error {
method from (line 82) | fn from(err: self::parser::Error) -> Self {
function test_sprintf (line 92) | fn test_sprintf() {
function test_complex (line 150) | fn test_complex() {
FILE: crates/i18n/src/sprintf/parser.rs
type SprintfParser (line 19) | struct SprintfParser;
type Error (line 21) | pub type Error = pest::error::Error<Rule>;
type Result (line 22) | type Result<T, E = Error> = std::result::Result<T, E>;
function unexpected_rule_error (line 24) | fn unexpected_rule_error(pair: &Pair<Rule>) -> Error {
function ensure_end_of_pairs (line 33) | fn ensure_end_of_pairs(pairs: &mut pest::iterators::Pairs<Rule>, span: S...
function next_pair (line 46) | fn next_pair<'i>(
function ensure_rule_type (line 60) | fn ensure_rule_type(pair: &Pair<Rule>, rule: Rule) -> Result<()> {
function interpret_ident (line 68) | fn interpret_ident(pair: &Pair<Rule>) -> Result<String> {
function interpret_number (line 73) | fn interpret_number(pair: &Pair<Rule>) -> Result<usize> {
function interpret_arg_named (line 85) | fn interpret_arg_named(pair: Pair<Rule>) -> Result<ArgumentReference> {
function interpret_arg_indexed (line 97) | fn interpret_arg_indexed(pair: Pair<Rule>) -> Result<ArgumentReference> {
function interpret_padding_specifier (line 109) | fn interpret_padding_specifier(pair: &Pair<Rule>) -> Result<PaddingSpeci...
func
Condensed preview — 1410 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (9,331K chars).
[
{
"path": ".cargo/config.toml",
"chars": 455,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".codecov.yml",
"chars": 251,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".config/nextest.toml",
"chars": 208,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".dockerignore",
"chars": 342,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".editorconfig",
"chars": 373,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".github/CODEOWNERS",
"chars": 32,
"preview": "* @element-hq/mas-maintainers\n"
},
{
"path": ".github/actions/build-frontend/action.yml",
"chars": 699,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".github/actions/build-policies/action.yml",
"chars": 635,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".github/dependabot.yml",
"chars": 2501,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".github/release.yml",
"chars": 856,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".github/scripts/.gitignore",
"chars": 210,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".github/scripts/cleanup-pr.cjs",
"chars": 1486,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": ".github/scripts/commit-and-tag.cjs",
"chars": 1756,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": ".github/scripts/create-release-branch.cjs",
"chars": 664,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": ".github/scripts/create-version-tag.cjs",
"chars": 755,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": ".github/scripts/merge-back.cjs",
"chars": 1644,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": ".github/scripts/package.json",
"chars": 139,
"preview": "{\n \"private\": true,\n \"devDependencies\": {\n \"@actions/github-script\": \"github:actions/github-script\",\n \"typescrip"
},
{
"path": ".github/scripts/update-release-branch.cjs",
"chars": 657,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": ".github/scripts/update-unstable-tag.cjs",
"chars": 588,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": ".github/workflows/build.yaml",
"chars": 29220,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".github/workflows/ci.yaml",
"chars": 9653,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".github/workflows/coverage.yaml",
"chars": 4256,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".github/workflows/docs.yaml",
"chars": 2232,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".github/workflows/merge-back.yaml",
"chars": 1109,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".github/workflows/release-branch.yaml",
"chars": 4153,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".github/workflows/release-bump.yaml",
"chars": 2959,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".github/workflows/tag.yaml",
"chars": 2572,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".github/workflows/translations-download.yaml",
"chars": 2256,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".github/workflows/translations-upload.yaml",
"chars": 1240,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".gitignore",
"chars": 239,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": ".rustfmt.toml",
"chars": 332,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": "CONTRIBUTING.md",
"chars": 237,
"preview": "# Contributing to MAS\n\nThank you for taking the time to contribute to Matrix!\n\nPlease see the [contributors' guide](http"
},
{
"path": "Cargo.toml",
"chars": 18753,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": "Dockerfile",
"chars": 5608,
"preview": "# syntax = docker/dockerfile:1.21.0\n# Copyright 2025, 2026 Element Creations Ltd.\n# Copyright 2025 New Vector Ltd.\n#\n# S"
},
{
"path": "LICENSE",
"chars": 34523,
"preview": " GNU AFFERO GENERAL PUBLIC LICENSE\n Version 3, 19 November 2007\n\n Copyright (C)"
},
{
"path": "LICENSE-COMMERCIAL",
"chars": 255,
"preview": "Licensees holding a valid commercial license with Element may use this\nsoftware in accordance with the terms contained i"
},
{
"path": "README.md",
"chars": 3875,
"preview": "# Matrix Authentication Service\n\nMAS (Matrix Authentication Service) is a user management and authentication service for"
},
{
"path": "biome.json",
"chars": 1568,
"preview": "{\n \"$schema\": \"https://biomejs.dev/schemas/2.2.4/schema.json\",\n \"assist\": { \"actions\": { \"source\": { \"organizeImports\""
},
{
"path": "book.toml",
"chars": 828,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": "clippy.toml",
"chars": 1278,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": "crates/axum-utils/Cargo.toml",
"chars": 1152,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": "crates/axum-utils/src/client_authorization.rs",
"chars": 25192,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/axum-utils/src/cookies.rs",
"chars": 4528,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/axum-utils/src/csrf.rs",
"chars": 4905,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/axum-utils/src/error_wrapper.rs",
"chars": 677,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/axum-utils/src/fancy_error.rs",
"chars": 2678,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/axum-utils/src/jwt.rs",
"chars": 749,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/axum-utils/src/language_detection.rs",
"chars": 8742,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/axum-utils/src/lib.rs",
"chars": 689,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/axum-utils/src/sentry.rs",
"chars": 1994,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/axum-utils/src/session.rs",
"chars": 2707,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/axum-utils/src/user_authorization.rs",
"chars": 10739,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/cli/Cargo.toml",
"chars": 2718,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": "crates/cli/build.rs",
"chars": 1190,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n//"
},
{
"path": "crates/cli/src/app_state.rs",
"chars": 11909,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/cli/src/commands/config.rs",
"chars": 5333,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/cli/src/commands/database.rs",
"chars": 1201,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/cli/src/commands/debug.rs",
"chars": 2473,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/cli/src/commands/doctor.rs",
"chars": 15443,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/cli/src/commands/manage.rs",
"chars": 47424,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/cli/src/commands/mod.rs",
"chars": 3339,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/cli/src/commands/server.rs",
"chars": 11944,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/cli/src/commands/syn2mas.rs",
"chars": 12695,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n//"
},
{
"path": "crates/cli/src/commands/templates.rs",
"chars": 6219,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/cli/src/commands/worker.rs",
"chars": 2844,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/cli/src/lifecycle.rs",
"chars": 8391,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n//"
},
{
"path": "crates/cli/src/main.rs",
"chars": 6053,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/cli/src/server.rs",
"chars": 15813,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/cli/src/sync.rs",
"chars": 17527,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/cli/src/telemetry.rs",
"chars": 10606,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/cli/src/util.rs",
"chars": 21707,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/config/Cargo.toml",
"chars": 1141,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": "crates/config/src/bin/schema.rs",
"chars": 540,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/config/src/lib.rs",
"chars": 752,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/config/src/schema.rs",
"chars": 702,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/config/src/sections/account.rs",
"chars": 4831,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/config/src/sections/branding.rs",
"chars": 1976,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/config/src/sections/captcha.rs",
"chars": 2675,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/config/src/sections/clients.rs",
"chars": 13805,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/config/src/sections/database.rs",
"chars": 10574,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/config/src/sections/email.rs",
"chars": 8660,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/config/src/sections/experimental.rs",
"chars": 9525,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/config/src/sections/http.rs",
"chars": 15881,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/config/src/sections/matrix.rs",
"chars": 6923,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/config/src/sections/mod.rs",
"chars": 12952,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/config/src/sections/oauth.rs",
"chars": 1575,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/config/src/sections/passwords.rs",
"chars": 7034,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/config/src/sections/policy.rs",
"chars": 5527,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/config/src/sections/rate_limiting.rs",
"chars": 10829,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/config/src/sections/secrets.rs",
"chars": 28273,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/config/src/sections/telemetry.rs",
"chars": 7082,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/config/src/sections/templates.rs",
"chars": 3193,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/config/src/sections/upstream_oauth2.rs",
"chars": 29199,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/config/src/util.rs",
"chars": 2479,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/context/Cargo.toml",
"chars": 727,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": "crates/context/src/fmt.rs",
"chars": 4892,
"preview": "// Copyright 2025, 2026 Element Creations Ltd.\n// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0"
},
{
"path": "crates/context/src/future.rs",
"chars": 1718,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/context/src/layer.rs",
"chars": 906,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/context/src/lib.rs",
"chars": 4415,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/context/src/service.rs",
"chars": 1343,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/data-model/Cargo.toml",
"chars": 861,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": "crates/data-model/examples/ua-parser.rs",
"chars": 609,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/data-model/src/clock.rs",
"chars": 3315,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/data-model/src/compat/device.rs",
"chars": 3590,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/data-model/src/compat/mod.rs",
"chars": 2851,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/data-model/src/compat/session.rs",
"chars": 2908,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/data-model/src/compat/sso_login.rs",
"chars": 6157,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/data-model/src/lib.rs",
"chars": 2480,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/data-model/src/oauth2/authorization_grant.rs",
"chars": 6762,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/data-model/src/oauth2/client.rs",
"chars": 10844,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/data-model/src/oauth2/device_code_grant.rs",
"chars": 7858,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/data-model/src/oauth2/mod.rs",
"chars": 598,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/data-model/src/oauth2/session.rs",
"chars": 2980,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/data-model/src/personal/mod.rs",
"chars": 794,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/data-model/src/personal/session.rs",
"chars": 3951,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/data-model/src/policy_data.rs",
"chars": 412,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/data-model/src/site_config.rs",
"chars": 3577,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/data-model/src/tokens.rs",
"chars": 15156,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/data-model/src/upstream_oauth2/link.rs",
"chars": 576,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/data-model/src/upstream_oauth2/mod.rs",
"chars": 1151,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/data-model/src/upstream_oauth2/provider.rs",
"chars": 11825,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/data-model/src/upstream_oauth2/session.rs",
"chars": 11157,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/data-model/src/user_agent.rs",
"chars": 8205,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/data-model/src/users.rs",
"chars": 8508,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2021-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/data-model/src/utils.rs",
"chars": 402,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/data-model/src/version.rs",
"chars": 330,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/email/Cargo.toml",
"chars": 565,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": "crates/email/src/lib.rs",
"chars": 623,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/email/src/mailer.rs",
"chars": 4546,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/email/src/transport.rs",
"chars": 4035,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/handlers/Cargo.toml",
"chars": 2554,
"preview": "# Copyright 2025 New Vector Ltd.\n#\n# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n# Please se"
},
{
"path": "crates/handlers/src/activity_tracker/bound.rs",
"chars": 1895,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/handlers/src/activity_tracker/mod.rs",
"chars": 6749,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/handlers/src/activity_tracker/worker.rs",
"chars": 9293,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Iden"
},
{
"path": "crates/handlers/src/admin/call_context.rs",
"chars": 10779,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/mod.rs",
"chars": 8644,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/model.rs",
"chars": 30509,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/params.rs",
"chars": 5298,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/response.rs",
"chars": 8082,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/schema.rs",
"chars": 1732,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/v1/compat_sessions/finish.rs",
"chars": 8405,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/compat_sessions/get.rs",
"chars": 5487,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/compat_sessions/list.rs",
"chars": 22322,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/compat_sessions/mod.rs",
"chars": 374,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/mod.rs",
"chars": 8440,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/v1/oauth2_sessions/finish.rs",
"chars": 8216,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/oauth2_sessions/get.rs",
"chars": 5235,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/v1/oauth2_sessions/list.rs",
"chars": 14696,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/v1/oauth2_sessions/mod.rs",
"chars": 431,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/v1/personal_sessions/add.rs",
"chars": 10245,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/personal_sessions/get.rs",
"chars": 6176,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/personal_sessions/list.rs",
"chars": 19310,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/personal_sessions/mod.rs",
"chars": 1276,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/personal_sessions/regenerate.rs",
"chars": 7949,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/personal_sessions/revoke.rs",
"chars": 8272,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/policy_data/get.rs",
"chars": 4987,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/policy_data/get_latest.rs",
"chars": 4799,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/policy_data/mod.rs",
"chars": 386,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/policy_data/set.rs",
"chars": 4918,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/site_config.rs",
"chars": 3723,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/upstream_oauth_links/add.rs",
"chars": 14830,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/upstream_oauth_links/delete.rs",
"chars": 5786,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/upstream_oauth_links/get.rs",
"chars": 5717,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/upstream_oauth_links/list.rs",
"chars": 25165,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/upstream_oauth_links/mod.rs",
"chars": 2249,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/upstream_oauth_providers/get.rs",
"chars": 7098,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/upstream_oauth_providers/list.rs",
"chars": 29804,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/upstream_oauth_providers/mod.rs",
"chars": 310,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/user_emails/add.rs",
"chars": 10402,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/user_emails/delete.rs",
"chars": 4641,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/user_emails/get.rs",
"chars": 4986,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/user_emails/list.rs",
"chars": 16261,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n//"
},
{
"path": "crates/handlers/src/admin/v1/user_emails/mod.rs",
"chars": 426,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/user_registration_tokens/add.rs",
"chars": 8759,
"preview": "// Copyright 2025 New Vector Ltd.\n// Copyright 2025 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier: AGPL"
},
{
"path": "crates/handlers/src/admin/v1/user_registration_tokens/get.rs",
"chars": 5733,
"preview": "// Copyright 2025 New Vector Ltd.\n// Copyright 2025 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier: AGPL"
},
{
"path": "crates/handlers/src/admin/v1/user_registration_tokens/list.rs",
"chars": 55479,
"preview": "// Copyright 2025 New Vector Ltd.\n// Copyright 2025 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier: AGPL"
},
{
"path": "crates/handlers/src/admin/v1/user_registration_tokens/mod.rs",
"chars": 613,
"preview": "// Copyright 2025 New Vector Ltd.\n// Copyright 2025 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier: AGPL"
},
{
"path": "crates/handlers/src/admin/v1/user_registration_tokens/revoke.rs",
"chars": 7518,
"preview": "// Copyright 2025 New Vector Ltd.\n// Copyright 2025 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier: AGPL"
},
{
"path": "crates/handlers/src/admin/v1/user_registration_tokens/unrevoke.rs",
"chars": 8155,
"preview": "// Copyright 2025 New Vector Ltd.\n// Copyright 2025 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier: AGPL"
},
{
"path": "crates/handlers/src/admin/v1/user_registration_tokens/update.rs",
"chars": 16625,
"preview": "// Copyright 2025 New Vector Ltd.\n// Copyright 2025 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier: AGPL"
},
{
"path": "crates/handlers/src/admin/v1/user_sessions/finish.rs",
"chars": 7374,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/user_sessions/get.rs",
"chars": 4495,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/user_sessions/list.rs",
"chars": 19751,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/user_sessions/mod.rs",
"chars": 374,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/users/add.rs",
"chars": 11714,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/v1/users/by_username.rs",
"chars": 2858,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/v1/users/deactivate.rs",
"chars": 11129,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/v1/users/get.rs",
"chars": 2430,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/v1/users/list.rs",
"chars": 14089,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/v1/users/lock.rs",
"chars": 7521,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/v1/users/mod.rs",
"chars": 947,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/v1/users/reactivate.rs",
"chars": 7705,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/admin/v1/users/set_admin.rs",
"chars": 5509,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/v1/users/set_password.rs",
"chars": 10396,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/v1/users/unlock.rs",
"chars": 8372,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/admin/v1/version.rs",
"chars": 1839,
"preview": "// Copyright 2025 New Vector Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n// Pleas"
},
{
"path": "crates/handlers/src/bin/api-schema.rs",
"chars": 2713,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/bin/graphql-schema.rs",
"chars": 531,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2022-2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Ident"
},
{
"path": "crates/handlers/src/captcha.rs",
"chars": 7717,
"preview": "// Copyright 2024, 2025 New Vector Ltd.\n// Copyright 2024 The Matrix.org Foundation C.I.C.\n//\n// SPDX-License-Identifier"
},
{
"path": "crates/handlers/src/cleanup_tests.rs",
"chars": 24293,
"preview": "// Copyright 2026 Element Creations Ltd.\n//\n// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n/"
}
]
// ... and 1210 more files (download for full content)
About this extraction
This page contains the full source code of the element-hq/matrix-authentication-service GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 1410 files (8.1 MB), approximately 2.2M tokens, and a symbol index with 6550 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.