You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-31 09:24:31 +03:00
Loads of docs & enabling more clippy lints
This commit is contained in:
@ -16,7 +16,6 @@
|
||||
#![deny(clippy::all)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
#![allow(clippy::suspicious_else_formatting)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
@ -13,13 +13,14 @@
|
||||
// limitations under the License.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::all)]
|
||||
#![deny(missing_docs)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![deny(
|
||||
clippy::all,
|
||||
missing_docs,
|
||||
rustdoc::missing_crate_level_docs,
|
||||
rustdoc::broken_intra_doc_links
|
||||
)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
#![allow(clippy::missing_panics_doc)]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
|
||||
//! Application configuration logic
|
||||
|
||||
|
@ -51,6 +51,10 @@ impl Encrypter {
|
||||
}
|
||||
|
||||
/// Encrypt a payload
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `Err` when the payload failed to encrypt
|
||||
pub fn encrypt(&self, nonce: &[u8; 12], decrypted: &[u8]) -> anyhow::Result<Vec<u8>> {
|
||||
let nonce = GenericArray::from_slice(&nonce[..]);
|
||||
let encrypted = self.aead.encrypt(nonce, decrypted)?;
|
||||
@ -58,6 +62,10 @@ impl Encrypter {
|
||||
}
|
||||
|
||||
/// Decrypts a payload
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `Err` when the payload failed to decrypt
|
||||
pub fn decrypt(&self, nonce: &[u8; 12], encrypted: &[u8]) -> anyhow::Result<Vec<u8>> {
|
||||
let nonce = GenericArray::from_slice(&nonce[..]);
|
||||
let encrypted = self.aead.decrypt(nonce, encrypted)?;
|
||||
@ -110,6 +118,10 @@ pub struct SecretsConfig {
|
||||
|
||||
impl SecretsConfig {
|
||||
/// Derive a signing and verifying keystore out of the config
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error when a key could not be imported
|
||||
pub async fn key_store(&self) -> anyhow::Result<StaticKeystore> {
|
||||
let mut store = StaticKeystore::new();
|
||||
|
||||
|
@ -13,13 +13,14 @@
|
||||
// limitations under the License.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::all)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![deny(clippy::all, rustdoc::broken_intra_doc_links)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
#![allow(clippy::missing_panics_doc)]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
#![allow(clippy::trait_duplication_in_bounds)]
|
||||
#![allow(
|
||||
clippy::module_name_repetitions,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::missing_errors_doc,
|
||||
clippy::trait_duplication_in_bounds
|
||||
)]
|
||||
|
||||
pub mod errors;
|
||||
pub(crate) mod oauth2;
|
||||
|
@ -12,6 +12,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Helps sending emails to users, with different email backends
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::all, missing_docs, rustdoc::broken_intra_doc_links)]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
mod mailer;
|
||||
mod transport;
|
||||
|
||||
|
@ -12,6 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Send emails to users
|
||||
|
||||
use lettre::{
|
||||
message::{Mailbox, MessageBuilder, MultiPart},
|
||||
AsyncTransport, Message,
|
||||
@ -20,6 +22,7 @@ use mas_templates::{EmailVerificationContext, Templates};
|
||||
|
||||
use crate::MailTransport;
|
||||
|
||||
/// Helps sending mails to users
|
||||
#[derive(Clone)]
|
||||
pub struct Mailer {
|
||||
templates: Templates,
|
||||
@ -29,6 +32,8 @@ pub struct Mailer {
|
||||
}
|
||||
|
||||
impl Mailer {
|
||||
/// Constructs a new [`Mailer`]
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
templates: &Templates,
|
||||
transport: &MailTransport,
|
||||
@ -76,6 +81,11 @@ impl Mailer {
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
/// Send the verification email to a user
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `Err` if the email failed rendering or failed sending
|
||||
pub async fn send_verification_email(
|
||||
&self,
|
||||
to: Mailbox,
|
||||
|
@ -19,16 +19,21 @@ use aws_sdk_sesv2::{
|
||||
};
|
||||
use lettre::{address::Envelope, AsyncTransport};
|
||||
|
||||
/// An asynchronous email transport that sends email via the AWS Simple Email
|
||||
/// Service v2 API
|
||||
pub struct Transport {
|
||||
client: Client,
|
||||
}
|
||||
|
||||
impl Transport {
|
||||
/// Construct a [`Transport`] from the environment
|
||||
pub async fn from_env() -> Self {
|
||||
let config = aws_config::from_env().load().await;
|
||||
Self::new(&config)
|
||||
}
|
||||
|
||||
/// Constructs a [`Transport`] from a given AWS shared config
|
||||
#[must_use]
|
||||
pub fn new(config: &aws_config::Config) -> Self {
|
||||
let client = Client::new(config);
|
||||
Self { client }
|
||||
|
@ -12,6 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Email transport backends
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
@ -27,6 +29,7 @@ use mas_config::{EmailSmtpMode, EmailTransportConfig};
|
||||
|
||||
pub mod aws_ses;
|
||||
|
||||
/// A wrapper around many [`AsyncTransport`]s
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Transport {
|
||||
inner: Arc<TransportInner>,
|
||||
@ -40,6 +43,11 @@ enum TransportInner {
|
||||
}
|
||||
|
||||
impl Transport {
|
||||
/// Construct a transport from a user configration
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `Err` on invalid confiuration
|
||||
pub async fn from_config(config: &EmailTransportConfig) -> Result<Self, anyhow::Error> {
|
||||
let inner = match config {
|
||||
EmailTransportConfig::Blackhole => TransportInner::Blackhole,
|
||||
@ -85,14 +93,19 @@ impl Transport {
|
||||
}
|
||||
|
||||
impl Transport {
|
||||
/// Test the connection to the underlying transport. Only works with the
|
||||
/// SMTP backend for now
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `Err` if the connection test failed
|
||||
pub async fn test_connection(&self) -> anyhow::Result<()> {
|
||||
match self.inner.as_ref() {
|
||||
TransportInner::Blackhole => {}
|
||||
TransportInner::Smtp(t) => {
|
||||
t.test_connection().await?;
|
||||
}
|
||||
&TransportInner::Sendmail(_) => {}
|
||||
TransportInner::AwsSes(_) => {}
|
||||
TransportInner::Blackhole | TransportInner::Sendmail(_) | TransportInner::AwsSes(_) => {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -13,14 +13,11 @@
|
||||
// limitations under the License.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::all)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![deny(clippy::all, rustdoc::broken_intra_doc_links)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
#![allow(clippy::missing_panics_doc)]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
#![allow(clippy::implicit_hasher)]
|
||||
#![allow(clippy::unused_async)] // Some warp filters need that
|
||||
#![allow(
|
||||
clippy::unused_async // Some warp filters need that
|
||||
)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -40,7 +40,7 @@ use warp::{filters::BoxedFilter, reply::html, Filter, Rejection, Reply};
|
||||
use super::{LoginRequest, PostAuthAction};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RegisterRequest {
|
||||
pub(crate) struct RegisterRequest {
|
||||
#[serde(flatten)]
|
||||
post_auth_action: Option<PostAuthAction>,
|
||||
}
|
||||
|
@ -13,8 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::all)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![deny(clippy::all, rustdoc::broken_intra_doc_links)]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
use std::{collections::HashMap, fmt::Display, path::PathBuf, sync::Arc};
|
||||
@ -135,6 +134,8 @@ pub enum {} {{"#,
|
||||
writeln!(f)?;
|
||||
if let Some(description) = &member.description {
|
||||
writeln!(f, " /// {}", description)?;
|
||||
} else {
|
||||
writeln!(f, " /// `{}`", member.value)?;
|
||||
}
|
||||
writeln!(f, " #[serde(rename = \"{}\")]", member.value)?;
|
||||
writeln!(f, " #[display(\"{}\")]", member.value)?;
|
||||
|
@ -12,9 +12,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Values from IANA registries, generated by the `mas-iana-codegen` crate
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::all)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![deny(clippy::all, missing_docs, rustdoc::broken_intra_doc_links)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
|
@ -28,14 +28,17 @@ use serde::{Deserialize, Serialize};
|
||||
Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema,
|
||||
)]
|
||||
pub enum OAuthAccessTokenType {
|
||||
/// `Bearer`
|
||||
#[serde(rename = "Bearer")]
|
||||
#[display("Bearer")]
|
||||
Bearer,
|
||||
|
||||
/// `N_A`
|
||||
#[serde(rename = "N_A")]
|
||||
#[display("N_A")]
|
||||
Na,
|
||||
|
||||
/// `PoP`
|
||||
#[serde(rename = "PoP")]
|
||||
#[display("PoP")]
|
||||
PoP,
|
||||
@ -48,34 +51,42 @@ pub enum OAuthAccessTokenType {
|
||||
Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema,
|
||||
)]
|
||||
pub enum OAuthAuthorizationEndpointResponseType {
|
||||
/// `code`
|
||||
#[serde(rename = "code")]
|
||||
#[display("code")]
|
||||
Code,
|
||||
|
||||
/// `code id_token`
|
||||
#[serde(rename = "code id_token")]
|
||||
#[display("code id_token")]
|
||||
CodeIdToken,
|
||||
|
||||
/// `code id_token token`
|
||||
#[serde(rename = "code id_token token")]
|
||||
#[display("code id_token token")]
|
||||
CodeIdTokenToken,
|
||||
|
||||
/// `code token`
|
||||
#[serde(rename = "code token")]
|
||||
#[display("code token")]
|
||||
CodeToken,
|
||||
|
||||
/// `id_token`
|
||||
#[serde(rename = "id_token")]
|
||||
#[display("id_token")]
|
||||
IdToken,
|
||||
|
||||
/// `id_token token`
|
||||
#[serde(rename = "id_token token")]
|
||||
#[display("id_token token")]
|
||||
IdTokenToken,
|
||||
|
||||
/// `none`
|
||||
#[serde(rename = "none")]
|
||||
#[display("none")]
|
||||
None,
|
||||
|
||||
/// `token`
|
||||
#[serde(rename = "token")]
|
||||
#[display("token")]
|
||||
Token,
|
||||
@ -88,14 +99,17 @@ pub enum OAuthAuthorizationEndpointResponseType {
|
||||
Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema,
|
||||
)]
|
||||
pub enum OAuthTokenTypeHint {
|
||||
/// `access_token`
|
||||
#[serde(rename = "access_token")]
|
||||
#[display("access_token")]
|
||||
AccessToken,
|
||||
|
||||
/// `refresh_token`
|
||||
#[serde(rename = "refresh_token")]
|
||||
#[display("refresh_token")]
|
||||
RefreshToken,
|
||||
|
||||
/// `pct`
|
||||
#[serde(rename = "pct")]
|
||||
#[display("pct")]
|
||||
Pct,
|
||||
@ -108,30 +122,37 @@ pub enum OAuthTokenTypeHint {
|
||||
Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema,
|
||||
)]
|
||||
pub enum OAuthClientAuthenticationMethod {
|
||||
/// `none`
|
||||
#[serde(rename = "none")]
|
||||
#[display("none")]
|
||||
None,
|
||||
|
||||
/// `client_secret_post`
|
||||
#[serde(rename = "client_secret_post")]
|
||||
#[display("client_secret_post")]
|
||||
ClientSecretPost,
|
||||
|
||||
/// `client_secret_basic`
|
||||
#[serde(rename = "client_secret_basic")]
|
||||
#[display("client_secret_basic")]
|
||||
ClientSecretBasic,
|
||||
|
||||
/// `client_secret_jwt`
|
||||
#[serde(rename = "client_secret_jwt")]
|
||||
#[display("client_secret_jwt")]
|
||||
ClientSecretJwt,
|
||||
|
||||
/// `private_key_jwt`
|
||||
#[serde(rename = "private_key_jwt")]
|
||||
#[display("private_key_jwt")]
|
||||
PrivateKeyJwt,
|
||||
|
||||
/// `tls_client_auth`
|
||||
#[serde(rename = "tls_client_auth")]
|
||||
#[display("tls_client_auth")]
|
||||
TlsClientAuth,
|
||||
|
||||
/// `self_signed_tls_client_auth`
|
||||
#[serde(rename = "self_signed_tls_client_auth")]
|
||||
#[display("self_signed_tls_client_auth")]
|
||||
SelfSignedTlsClientAuth,
|
||||
@ -144,10 +165,12 @@ pub enum OAuthClientAuthenticationMethod {
|
||||
Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema,
|
||||
)]
|
||||
pub enum PkceCodeChallengeMethod {
|
||||
/// `plain`
|
||||
#[serde(rename = "plain")]
|
||||
#[display("plain")]
|
||||
Plain,
|
||||
|
||||
/// `S256`
|
||||
#[serde(rename = "S256")]
|
||||
#[display("S256")]
|
||||
S256,
|
||||
|
@ -13,11 +13,9 @@
|
||||
// limitations under the License.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::all)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![deny(clippy::all, rustdoc::broken_intra_doc_links)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
#![allow(clippy::missing_errors_doc, clippy::module_name_repetitions)]
|
||||
|
||||
pub mod claims;
|
||||
pub(crate) mod jwk;
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::all)]
|
||||
#![deny(clippy::all, rustdoc::broken_intra_doc_links)]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
use mas_iana::oauth::OAuthAuthorizationEndpointResponseType;
|
||||
|
@ -198,7 +198,7 @@ pub struct Metadata {
|
||||
|
||||
// TODO: type
|
||||
/// Languages and scripts supported for values in Claims being returned,
|
||||
/// represented as a JSON array of BCP 47 [RFC5646] language tag values.
|
||||
/// represented as a JSON array of BCP 47 language tag values.
|
||||
pub claims_locales_supported: Option<HashSet<String>>,
|
||||
|
||||
/// Boolean value specifying whether the OP supports use of the "claims"
|
||||
|
@ -12,14 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Serve static files used by the web frontend
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::all)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![deny(clippy::all, missing_docs, rustdoc::broken_intra_doc_links)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
#![allow(clippy::missing_panics_doc)]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
#![allow(clippy::unused_async)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
@ -40,6 +37,7 @@ mod builtin {
|
||||
#[folder = "public/"]
|
||||
struct Asset;
|
||||
|
||||
#[allow(clippy::unused_async)]
|
||||
async fn serve_embed(
|
||||
path: Tail,
|
||||
if_none_match: Option<String>,
|
||||
@ -101,6 +99,7 @@ fn filter_for_path(path: PathBuf) -> BoxedFilter<(impl Reply,)> {
|
||||
warp::fs::dir(path).boxed()
|
||||
}
|
||||
|
||||
/// [`warp`] filter that serves static files
|
||||
#[must_use]
|
||||
pub fn filter(path: Option<PathBuf>) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||
let f = self::builtin::filter();
|
||||
|
@ -14,7 +14,14 @@
|
||||
|
||||
//! Interactions with the database
|
||||
|
||||
#![allow(clippy::used_underscore_binding)] // This is needed by sqlx macros
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::all, rustdoc::broken_intra_doc_links)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(
|
||||
clippy::missing_errors_doc,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::module_name_repetitions
|
||||
)]
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use mas_data_model::{StorageBackend, StorageBackendMarker};
|
||||
|
@ -93,6 +93,7 @@ impl AccessTokenLookupError {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub async fn lookup_active_access_token(
|
||||
executor: impl PgExecutor<'_>,
|
||||
token: &str,
|
||||
|
@ -12,6 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Database-related tasks
|
||||
|
||||
use sqlx::{Pool, Postgres};
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
@ -44,6 +46,7 @@ impl Task for CleanupExpired {
|
||||
}
|
||||
}
|
||||
|
||||
/// Cleanup expired tokens
|
||||
#[must_use]
|
||||
pub fn cleanup_expired(pool: &Pool<Postgres>) -> impl Task + Clone {
|
||||
CleanupExpired(pool.clone())
|
||||
|
@ -18,6 +18,10 @@
|
||||
//! resources and avoid database conflicts. Tasks are not persisted, which is
|
||||
//! considered "good enough" for now.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::all, missing_docs, rustdoc::broken_intra_doc_links)]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
use std::{collections::VecDeque, sync::Arc, time::Duration};
|
||||
|
||||
use futures_util::StreamExt;
|
||||
@ -32,8 +36,10 @@ mod database;
|
||||
|
||||
pub use self::database::cleanup_expired;
|
||||
|
||||
/// A [`Task`] can be executed by a [`TaskQueue`]
|
||||
#[async_trait::async_trait]
|
||||
pub trait Task: std::fmt::Debug + Send + Sync + 'static {
|
||||
/// Execute the [`Task`]
|
||||
async fn run(&self);
|
||||
}
|
||||
|
||||
@ -81,12 +87,14 @@ impl TaskQueueInner {
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`TaskQueue`] executes tasks inserted in it in order
|
||||
#[derive(Default)]
|
||||
pub struct TaskQueue {
|
||||
inner: Arc<TaskQueueInner>,
|
||||
}
|
||||
|
||||
impl TaskQueue {
|
||||
/// Start the task queue to run forever
|
||||
pub fn start(&self) {
|
||||
let queue = self.inner.clone();
|
||||
tokio::task::spawn(async move {
|
||||
@ -100,6 +108,7 @@ impl TaskQueue {
|
||||
queue.schedule(task).await;
|
||||
}
|
||||
|
||||
/// Schedule a task in the queue at regular intervals
|
||||
pub fn recuring(&self, every: Duration, task: impl Task + Clone + std::fmt::Debug) {
|
||||
debug!(?task, period = every.as_secs(), "Scheduling recuring task");
|
||||
let queue = self.inner.clone();
|
||||
|
@ -197,6 +197,8 @@ pub struct IndexContext {
|
||||
}
|
||||
|
||||
impl IndexContext {
|
||||
/// Constructs the context for the index page from the OIDC discovery
|
||||
/// document URL
|
||||
#[must_use]
|
||||
pub fn new(discovery_url: Url) -> Self {
|
||||
Self { discovery_url }
|
||||
@ -216,10 +218,14 @@ impl TemplateContext for IndexContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// Fields of the login form
|
||||
#[derive(Serialize, Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum LoginFormField {
|
||||
/// The username field
|
||||
Username,
|
||||
|
||||
/// The password field
|
||||
Password,
|
||||
}
|
||||
|
||||
@ -227,7 +233,11 @@ pub enum LoginFormField {
|
||||
#[derive(Serialize)]
|
||||
#[serde(tag = "kind", rename_all = "snake_case")]
|
||||
pub enum PostAuthContext {
|
||||
ContinueAuthorizationGrant { grant: AuthorizationGrant<()> },
|
||||
/// Continue an authorization grant
|
||||
ContinueAuthorizationGrant {
|
||||
/// The authorization grant that will be continued after authentication
|
||||
grant: AuthorizationGrant<()>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Context used by the `login.html` template
|
||||
@ -253,11 +263,13 @@ impl TemplateContext for LoginContext {
|
||||
}
|
||||
|
||||
impl LoginContext {
|
||||
/// Add an error on the login form
|
||||
#[must_use]
|
||||
pub fn with_form_error(self, form: ErroredForm<LoginFormField>) -> Self {
|
||||
Self { form, ..self }
|
||||
}
|
||||
|
||||
/// Add a post authentication action to the context
|
||||
#[must_use]
|
||||
pub fn with_post_action(self, next: PostAuthContext) -> Self {
|
||||
Self {
|
||||
@ -266,6 +278,7 @@ impl LoginContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a registration link to the context
|
||||
#[must_use]
|
||||
pub fn with_register_link(self, register_link: String) -> Self {
|
||||
Self {
|
||||
@ -285,11 +298,17 @@ impl Default for LoginContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// Fields of the registration form
|
||||
#[derive(Serialize, Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum RegisterFormField {
|
||||
/// The username field
|
||||
Username,
|
||||
|
||||
/// The password field
|
||||
Password,
|
||||
|
||||
/// The password confirmation field
|
||||
PasswordConfirm,
|
||||
}
|
||||
|
||||
@ -316,11 +335,13 @@ impl TemplateContext for RegisterContext {
|
||||
}
|
||||
|
||||
impl RegisterContext {
|
||||
/// Add an error on the registration form
|
||||
#[must_use]
|
||||
pub fn with_form_error(self, form: ErroredForm<LoginFormField>) -> Self {
|
||||
Self { form, ..self }
|
||||
}
|
||||
|
||||
/// Add a post authentication action to the context
|
||||
#[must_use]
|
||||
pub fn with_post_action(self, next: PostAuthContext) -> Self {
|
||||
Self {
|
||||
@ -329,6 +350,7 @@ impl RegisterContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a login link to the context
|
||||
#[must_use]
|
||||
pub fn with_login_link(self, login_link: String) -> Self {
|
||||
Self { login_link, ..self }
|
||||
@ -345,9 +367,11 @@ impl Default for RegisterContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// Fields of the reauthentication form
|
||||
#[derive(Serialize, Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum ReauthFormField {
|
||||
/// The password field
|
||||
Password,
|
||||
}
|
||||
|
||||
@ -365,11 +389,13 @@ impl TemplateContext for ReauthContext {
|
||||
}
|
||||
|
||||
impl ReauthContext {
|
||||
/// Add an error on the reauthentication form
|
||||
#[must_use]
|
||||
pub fn with_form_error(self, form: ErroredForm<ReauthFormField>) -> Self {
|
||||
Self { form, ..self }
|
||||
}
|
||||
|
||||
/// Add a post authentication action to the context
|
||||
#[must_use]
|
||||
pub fn with_post_action(self, next: PostAuthContext) -> Self {
|
||||
Self {
|
||||
@ -403,6 +429,7 @@ pub struct AccountContext {
|
||||
}
|
||||
|
||||
impl AccountContext {
|
||||
/// Constructs a context for the "my account" page
|
||||
#[must_use]
|
||||
pub fn new<T>(active_sessions: usize, emails: Vec<T>) -> Self
|
||||
where
|
||||
@ -433,6 +460,7 @@ pub struct AccountEmailsContext<T: StorageBackend> {
|
||||
}
|
||||
|
||||
impl<T: StorageBackend> AccountEmailsContext<T> {
|
||||
/// Constructs a context for the email management page
|
||||
#[must_use]
|
||||
pub fn new(emails: Vec<UserEmail<T>>) -> Self {
|
||||
Self { emails }
|
||||
@ -457,6 +485,7 @@ pub struct EmailVerificationContext {
|
||||
}
|
||||
|
||||
impl EmailVerificationContext {
|
||||
/// Constructs a context for the verification email
|
||||
#[must_use]
|
||||
pub fn new(user: User<()>, verification_link: Url) -> Self {
|
||||
Self {
|
||||
@ -507,6 +536,7 @@ impl<T: TemplateContext> TemplateContext for FormPostContext<T> {
|
||||
}
|
||||
|
||||
impl<T> FormPostContext<T> {
|
||||
/// Constructs a context for the `form_post` response mode form
|
||||
pub fn new(redirect_uri: Url, params: T) -> Self {
|
||||
Self {
|
||||
redirect_uri,
|
||||
@ -540,23 +570,27 @@ impl TemplateContext for ErrorContext {
|
||||
}
|
||||
|
||||
impl ErrorContext {
|
||||
/// Constructs a context for the error page
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Add the error code to the context
|
||||
#[must_use]
|
||||
pub fn with_code(mut self, code: &'static str) -> Self {
|
||||
self.code = Some(code);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add the error description to the context
|
||||
#[must_use]
|
||||
pub fn with_description(mut self, description: String) -> Self {
|
||||
self.description = Some(description);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add the error details to the context
|
||||
#[allow(dead_code)]
|
||||
#[must_use]
|
||||
pub fn with_details(mut self, details: String) -> Self {
|
||||
|
@ -13,13 +13,9 @@
|
||||
// limitations under the License.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(missing_docs)]
|
||||
#![deny(clippy::all)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![deny(clippy::all, missing_docs, rustdoc::broken_intra_doc_links)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
#![allow(clippy::missing_panics_doc)]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
#![allow(clippy::module_name_repetitions, clippy::missing_errors_doc)]
|
||||
|
||||
//! Templates rendering
|
||||
|
||||
@ -40,7 +36,6 @@ use thiserror::Error;
|
||||
use tokio::{fs::OpenOptions, io::AsyncWriteExt, sync::RwLock, task::JoinError};
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
#[allow(missing_docs)] // TODO
|
||||
mod context;
|
||||
mod functions;
|
||||
|
||||
|
@ -12,6 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Helper to deal with various unstructured errors in application code
|
||||
|
||||
use warp::{reject::Reject, Rejection};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -19,11 +21,14 @@ pub(crate) struct WrappedError(anyhow::Error);
|
||||
|
||||
impl warp::reject::Reject for WrappedError {}
|
||||
|
||||
/// Wrap any error in a [`Rejection`]
|
||||
pub fn wrapped_error<T: Into<anyhow::Error>>(e: T) -> impl Reject {
|
||||
WrappedError(e.into())
|
||||
}
|
||||
|
||||
/// Extension trait that wraps errors in [`Rejection`]s
|
||||
pub trait WrapError<T> {
|
||||
/// Wrap transform the [`Result`] error type to a [`Rejection`]
|
||||
fn wrap_error(self) -> Result<T, Rejection>;
|
||||
}
|
||||
|
||||
|
@ -94,6 +94,7 @@ enum ClientAuthenticationError {
|
||||
|
||||
impl Reject for ClientAuthenticationError {}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
async fn authenticate_client<T>(
|
||||
clients_config: ClientsConfig,
|
||||
audience: String,
|
||||
|
@ -134,12 +134,12 @@ where
|
||||
warp::cookie::cookie(T::cookie_key()).and_then(move |value: String| {
|
||||
let encrypter = encrypter.clone();
|
||||
async move {
|
||||
let encrypted =
|
||||
let encrypted_payload =
|
||||
EncryptedCookie::from_cookie_value(&value).map_err(decryption_error::<T>)?;
|
||||
let decrypted = encrypted
|
||||
let decrypted_payload = encrypted_payload
|
||||
.decrypt(&encrypter)
|
||||
.map_err(decryption_error::<T>)?;
|
||||
Ok::<_, Rejection>(decrypted)
|
||||
Ok::<_, Rejection>(decrypted_payload)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -37,11 +37,13 @@ pub struct UrlBuilder {
|
||||
|
||||
impl UrlBuilder {
|
||||
/// OIDC issuer
|
||||
#[must_use]
|
||||
pub fn oidc_issuer(&self) -> Url {
|
||||
self.base.clone()
|
||||
}
|
||||
|
||||
/// OIDC dicovery document URL
|
||||
#[must_use]
|
||||
pub fn oidc_discovery(&self) -> Url {
|
||||
self.base
|
||||
.join(".well-known/openid-configuration")
|
||||
@ -49,31 +51,37 @@ impl UrlBuilder {
|
||||
}
|
||||
|
||||
/// OAuth 2.0 authorization endpoint
|
||||
#[must_use]
|
||||
pub fn oauth_authorization_endpoint(&self) -> Url {
|
||||
self.base.join("oauth2/authorize").expect("build URL")
|
||||
}
|
||||
|
||||
/// OAuth 2.0 token endpoint
|
||||
#[must_use]
|
||||
pub fn oauth_token_endpoint(&self) -> Url {
|
||||
self.base.join("oauth2/token").expect("build URL")
|
||||
}
|
||||
|
||||
/// OAuth 2.0 introspection endpoint
|
||||
#[must_use]
|
||||
pub fn oauth_introspection_endpoint(&self) -> Url {
|
||||
self.base.join("oauth2/introspect").expect("build URL")
|
||||
}
|
||||
|
||||
/// OAuth 2.0 introspection endpoint
|
||||
#[must_use]
|
||||
pub fn oidc_userinfo_endpoint(&self) -> Url {
|
||||
self.base.join("oauth2/userinfo").expect("build URL")
|
||||
}
|
||||
|
||||
/// JWKS URI
|
||||
#[must_use]
|
||||
pub fn jwks_uri(&self) -> Url {
|
||||
self.base.join("oauth2/keys.json").expect("build URL")
|
||||
}
|
||||
|
||||
/// Email verification URL
|
||||
#[must_use]
|
||||
pub fn email_verification(&self, code: &str) -> Url {
|
||||
self.base
|
||||
.join("verify")
|
||||
@ -84,6 +92,7 @@ impl UrlBuilder {
|
||||
}
|
||||
|
||||
/// Injects an [`UrlBuilder`] to help building absolute URLs
|
||||
#[must_use]
|
||||
pub fn url_builder(
|
||||
config: &HttpConfig,
|
||||
) -> impl Filter<Extract = (UrlBuilder,), Error = Infallible> + Clone + Send + Sync + 'static {
|
||||
|
@ -12,6 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Various warp filters and replies
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::all, missing_docs, rustdoc::broken_intra_doc_links)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(clippy::module_name_repetitions, clippy::missing_errors_doc)]
|
||||
|
||||
pub mod errors;
|
||||
pub mod filters;
|
||||
pub mod reply;
|
||||
|
Reference in New Issue
Block a user