You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-11-20 12:02:22 +03:00
Move secrets and oauth2 clients config
This commit is contained in:
200
crates/config/src/sections/clients.rs
Normal file
200
crates/config/src/sections/clients.rs
Normal file
@@ -0,0 +1,200 @@
|
||||
// Copyright 2021, 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mas_jose::{JsonWebKeySet, StaticJwksStore};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use thiserror::Error;
|
||||
use url::Url;
|
||||
|
||||
use super::ConfigurationSection;
|
||||
|
||||
#[derive(JsonSchema, Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum JwksOrJwksUri {
|
||||
Jwks(JsonWebKeySet),
|
||||
JwksUri(Url),
|
||||
}
|
||||
|
||||
impl JwksOrJwksUri {
|
||||
pub fn key_store(&self) -> StaticJwksStore {
|
||||
let jwks = match self {
|
||||
Self::Jwks(jwks) => jwks.clone(),
|
||||
Self::JwksUri(_) => unimplemented!("jwks_uri are not implemented yet"),
|
||||
};
|
||||
|
||||
StaticJwksStore::new(jwks)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JsonWebKeySet> for JwksOrJwksUri {
|
||||
fn from(jwks: JsonWebKeySet) -> Self {
|
||||
Self::Jwks(jwks)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(JsonSchema, Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(tag = "client_auth_method", rename_all = "snake_case")]
|
||||
pub enum ClientAuthMethodConfig {
|
||||
None,
|
||||
ClientSecretBasic { client_secret: String },
|
||||
ClientSecretPost { client_secret: String },
|
||||
ClientSecretJwt { client_secret: String },
|
||||
PrivateKeyJwt(JwksOrJwksUri),
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ClientConfig {
|
||||
pub client_id: String,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub client_auth_method: ClientAuthMethodConfig,
|
||||
|
||||
#[serde(default)]
|
||||
pub redirect_uris: Vec<Url>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("Invalid redirect URI")]
|
||||
pub struct InvalidRedirectUriError;
|
||||
|
||||
impl ClientConfig {
|
||||
pub fn resolve_redirect_uri<'a>(
|
||||
&'a self,
|
||||
suggested_uri: &'a Option<Url>,
|
||||
) -> Result<&'a Url, InvalidRedirectUriError> {
|
||||
suggested_uri.as_ref().map_or_else(
|
||||
|| self.redirect_uris.get(0).ok_or(InvalidRedirectUriError),
|
||||
|suggested_uri| self.check_redirect_uri(suggested_uri),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn check_redirect_uri<'a>(
|
||||
&self,
|
||||
redirect_uri: &'a Url,
|
||||
) -> Result<&'a Url, InvalidRedirectUriError> {
|
||||
if self.redirect_uris.contains(redirect_uri) {
|
||||
Ok(redirect_uri)
|
||||
} else {
|
||||
Err(InvalidRedirectUriError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(transparent)]
|
||||
pub struct ClientsConfig(Vec<ClientConfig>);
|
||||
|
||||
impl Deref for ClientsConfig {
|
||||
type Target = Vec<ClientConfig>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ClientsConfig {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ConfigurationSection<'_> for ClientsConfig {
|
||||
fn path() -> &'static str {
|
||||
"clients"
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
async fn generate() -> anyhow::Result<Self> {
|
||||
Ok(Self::default())
|
||||
}
|
||||
|
||||
fn test() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use figment::Jail;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn load_config() {
|
||||
Jail::expect_with(|jail| {
|
||||
jail.create_file(
|
||||
"config.yaml",
|
||||
r#"
|
||||
clients:
|
||||
- client_id: public
|
||||
client_auth_method: none
|
||||
redirect_uris:
|
||||
- https://exemple.fr/callback
|
||||
|
||||
- client_id: secret-basic
|
||||
client_auth_method: client_secret_basic
|
||||
client_secret: hello
|
||||
|
||||
- client_id: secret-post
|
||||
client_auth_method: client_secret_post
|
||||
client_secret: hello
|
||||
|
||||
- client_id: secret-jwk
|
||||
client_auth_method: client_secret_jwt
|
||||
client_secret: hello
|
||||
|
||||
- client_id: jwks
|
||||
client_auth_method: private_key_jwt
|
||||
jwks:
|
||||
keys:
|
||||
- kid: "03e84aed4ef4431014e8617567864c4efaaaede9"
|
||||
kty: "RSA"
|
||||
alg: "RS256"
|
||||
use: "sig"
|
||||
e: "AQAB"
|
||||
n: "ma2uRyBeSEOatGuDpCiV9oIxlDWix_KypDYuhQfEzqi_BiF4fV266OWfyjcABbam59aJMNvOnKW3u_eZM-PhMCBij5MZ-vcBJ4GfxDJeKSn-GP_dJ09rpDcILh8HaWAnPmMoi4DC0nrfE241wPISvZaaZnGHkOrfN_EnA5DligLgVUbrA5rJhQ1aSEQO_gf1raEOW3DZ_ACU3qhtgO0ZBG3a5h7BPiRs2sXqb2UCmBBgwyvYLDebnpE7AotF6_xBIlR-Cykdap3GHVMXhrIpvU195HF30ZoBU4dMd-AeG6HgRt4Cqy1moGoDgMQfbmQ48Hlunv9_Vi2e2CLvYECcBw"
|
||||
|
||||
- kid: "d01c1abe249269f72ef7ca2613a86c9f05e59567"
|
||||
kty: "RSA"
|
||||
alg: "RS256"
|
||||
use: "sig"
|
||||
e: "AQAB"
|
||||
n: "0hukqytPwrj1RbMYhYoepCi3CN5k7DwYkTe_Cmb7cP9_qv4ok78KdvFXt5AnQxCRwBD7-qTNkkfMWO2RxUMBdQD0ED6tsSb1n5dp0XY8dSWiBDCX8f6Hr-KolOpvMLZKRy01HdAWcM6RoL9ikbjYHUEW1C8IJnw3MzVHkpKFDL354aptdNLaAdTCBvKzU9WpXo10g-5ctzSlWWjQuecLMQ4G1mNdsR1LHhUENEnOvgT8cDkX0fJzLbEbyBYkdMgKggyVPEB1bg6evG4fTKawgnf0IDSPxIU-wdS9wdSP9ZCJJPLi5CEp-6t6rE_sb2dGcnzjCGlembC57VwpkUvyMw"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let config = ClientsConfig::load_from_file("config.yaml")?;
|
||||
|
||||
assert_eq!(config.0.len(), 5);
|
||||
|
||||
assert_eq!(config.0[0].client_id, "public");
|
||||
assert_eq!(
|
||||
config.0[0].redirect_uris,
|
||||
vec!["https://exemple.fr/callback".parse().unwrap()]
|
||||
);
|
||||
|
||||
assert_eq!(config.0[1].client_id, "secret-basic");
|
||||
assert_eq!(config.0[1].redirect_uris, Vec::new());
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
// Copyright 2021, 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::serde_as;
|
||||
|
||||
use super::ConfigurationSection;
|
||||
|
||||
fn example_secret() -> &'static str {
|
||||
"0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff"
|
||||
}
|
||||
|
||||
/// Cookies-related configuration
|
||||
#[serde_as]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct CookiesConfig {
|
||||
/// Encryption key for secure cookies
|
||||
#[schemars(
|
||||
with = "String",
|
||||
regex(pattern = r"[0-9a-fA-F]{64}"),
|
||||
example = "example_secret"
|
||||
)]
|
||||
#[serde_as(as = "serde_with::hex::Hex")]
|
||||
pub secret: [u8; 32],
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ConfigurationSection<'_> for CookiesConfig {
|
||||
fn path() -> &'static str {
|
||||
"cookies"
|
||||
}
|
||||
|
||||
async fn generate() -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
secret: rand::random(),
|
||||
})
|
||||
}
|
||||
|
||||
fn test() -> Self {
|
||||
Self { secret: [0xEA; 32] }
|
||||
}
|
||||
}
|
||||
@@ -16,22 +16,22 @@ use async_trait::async_trait;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
mod cookies;
|
||||
mod clients;
|
||||
mod csrf;
|
||||
mod database;
|
||||
mod email;
|
||||
mod http;
|
||||
mod oauth2;
|
||||
mod secrets;
|
||||
mod telemetry;
|
||||
mod templates;
|
||||
|
||||
pub use self::{
|
||||
cookies::CookiesConfig,
|
||||
clients::{ClientAuthMethodConfig, ClientConfig, ClientsConfig},
|
||||
csrf::CsrfConfig,
|
||||
database::DatabaseConfig,
|
||||
email::{EmailConfig, EmailSmtpMode, EmailTransportConfig},
|
||||
http::HttpConfig,
|
||||
oauth2::{OAuth2ClientAuthMethodConfig, OAuth2ClientConfig, OAuth2Config},
|
||||
secrets::{Encrypter, SecretsConfig},
|
||||
telemetry::{
|
||||
MetricsConfig, MetricsExporterConfig, Propagator, TelemetryConfig, TracingConfig,
|
||||
TracingExporterConfig,
|
||||
@@ -43,8 +43,9 @@ use crate::util::ConfigurationSection;
|
||||
/// Application configuration root
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct RootConfig {
|
||||
/// Configuration related to OAuth 2.0/OIDC operations
|
||||
pub oauth2: OAuth2Config,
|
||||
/// List of OAuth 2.0/OIDC clients config
|
||||
#[serde(default)]
|
||||
pub clients: ClientsConfig,
|
||||
|
||||
/// Configuration of the HTTP server
|
||||
#[serde(default)]
|
||||
@@ -54,9 +55,6 @@ pub struct RootConfig {
|
||||
#[serde(default)]
|
||||
pub database: DatabaseConfig,
|
||||
|
||||
/// Configuration related to cookies
|
||||
pub cookies: CookiesConfig,
|
||||
|
||||
/// Configuration related to sending monitoring data
|
||||
#[serde(default)]
|
||||
pub telemetry: TelemetryConfig,
|
||||
@@ -72,6 +70,9 @@ pub struct RootConfig {
|
||||
/// Configuration related to sending emails
|
||||
#[serde(default)]
|
||||
pub email: EmailConfig,
|
||||
|
||||
/// Application secrets
|
||||
pub secrets: SecretsConfig,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -82,27 +83,27 @@ impl ConfigurationSection<'_> for RootConfig {
|
||||
|
||||
async fn generate() -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
oauth2: OAuth2Config::generate().await?,
|
||||
clients: ClientsConfig::generate().await?,
|
||||
http: HttpConfig::generate().await?,
|
||||
database: DatabaseConfig::generate().await?,
|
||||
cookies: CookiesConfig::generate().await?,
|
||||
telemetry: TelemetryConfig::generate().await?,
|
||||
templates: TemplatesConfig::generate().await?,
|
||||
csrf: CsrfConfig::generate().await?,
|
||||
email: EmailConfig::generate().await?,
|
||||
secrets: SecretsConfig::generate().await?,
|
||||
})
|
||||
}
|
||||
|
||||
fn test() -> Self {
|
||||
Self {
|
||||
oauth2: OAuth2Config::test(),
|
||||
clients: ClientsConfig::test(),
|
||||
http: HttpConfig::test(),
|
||||
database: DatabaseConfig::test(),
|
||||
cookies: CookiesConfig::test(),
|
||||
telemetry: TelemetryConfig::test(),
|
||||
templates: TemplatesConfig::test(),
|
||||
csrf: CsrfConfig::test(),
|
||||
email: EmailConfig::test(),
|
||||
secrets: SecretsConfig::test(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021, 2022 The Matrix.org Foundation C.I.C.
|
||||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -12,11 +12,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use anyhow::Context;
|
||||
use async_trait::async_trait;
|
||||
use mas_jose::{JsonWebKeySet, StaticJwksStore, StaticKeystore};
|
||||
use chacha20poly1305::{
|
||||
aead::{generic_array::GenericArray, Aead, NewAead},
|
||||
ChaCha20Poly1305,
|
||||
};
|
||||
use mas_jose::StaticKeystore;
|
||||
use pkcs8::DecodePrivateKey;
|
||||
use rsa::{
|
||||
pkcs1::{DecodeRsaPrivateKey, EncodeRsaPrivateKey},
|
||||
@@ -24,14 +28,43 @@ use rsa::{
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use thiserror::Error;
|
||||
use serde_with::serde_as;
|
||||
use tokio::{fs::File, io::AsyncReadExt, task};
|
||||
use tracing::info;
|
||||
use url::Url;
|
||||
|
||||
use super::ConfigurationSection;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Encrypter {
|
||||
aead: Arc<ChaCha20Poly1305>,
|
||||
}
|
||||
|
||||
impl Encrypter {
|
||||
#[must_use]
|
||||
pub fn new(key: &[u8; 32]) -> Self {
|
||||
let key = GenericArray::from_slice(key);
|
||||
let aead = ChaCha20Poly1305::new(key);
|
||||
let aead = Arc::new(aead);
|
||||
Self { aead }
|
||||
}
|
||||
|
||||
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)?;
|
||||
Ok(encrypted)
|
||||
}
|
||||
|
||||
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)?;
|
||||
Ok(encrypted)
|
||||
}
|
||||
}
|
||||
|
||||
fn example_secret() -> &'static str {
|
||||
"0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff"
|
||||
}
|
||||
|
||||
#[derive(JsonSchema, Serialize, Deserialize, Clone, Copy, Debug)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum KeyType {
|
||||
@@ -53,89 +86,24 @@ pub struct KeyConfig {
|
||||
key: KeyOrPath,
|
||||
}
|
||||
|
||||
#[derive(JsonSchema, Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum JwksOrJwksUri {
|
||||
Jwks(JsonWebKeySet),
|
||||
JwksUri(Url),
|
||||
}
|
||||
|
||||
impl JwksOrJwksUri {
|
||||
pub fn key_store(&self) -> StaticJwksStore {
|
||||
let jwks = match self {
|
||||
Self::Jwks(jwks) => jwks.clone(),
|
||||
Self::JwksUri(_) => unimplemented!("jwks_uri are not implemented yet"),
|
||||
};
|
||||
|
||||
StaticJwksStore::new(jwks)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JsonWebKeySet> for JwksOrJwksUri {
|
||||
fn from(jwks: JsonWebKeySet) -> Self {
|
||||
Self::Jwks(jwks)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(JsonSchema, Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(tag = "client_auth_method", rename_all = "snake_case")]
|
||||
pub enum OAuth2ClientAuthMethodConfig {
|
||||
None,
|
||||
ClientSecretBasic { client_secret: String },
|
||||
ClientSecretPost { client_secret: String },
|
||||
ClientSecretJwt { client_secret: String },
|
||||
PrivateKeyJwt(JwksOrJwksUri),
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[serde_as]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct OAuth2ClientConfig {
|
||||
pub client_id: String,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub client_auth_method: OAuth2ClientAuthMethodConfig,
|
||||
pub struct SecretsConfig {
|
||||
/// Encryption key for secure cookies
|
||||
#[schemars(
|
||||
with = "String",
|
||||
regex(pattern = r"[0-9a-fA-F]{64}"),
|
||||
example = "example_secret"
|
||||
)]
|
||||
#[serde_as(as = "serde_with::hex::Hex")]
|
||||
encryption: [u8; 32],
|
||||
|
||||
/// List of private keys to use for signing and encrypting payloads
|
||||
#[serde(default)]
|
||||
pub redirect_uris: Vec<Url>,
|
||||
keys: Vec<KeyConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("Invalid redirect URI")]
|
||||
pub struct InvalidRedirectUriError;
|
||||
|
||||
impl OAuth2ClientConfig {
|
||||
pub fn resolve_redirect_uri<'a>(
|
||||
&'a self,
|
||||
suggested_uri: &'a Option<Url>,
|
||||
) -> Result<&'a Url, InvalidRedirectUriError> {
|
||||
suggested_uri.as_ref().map_or_else(
|
||||
|| self.redirect_uris.get(0).ok_or(InvalidRedirectUriError),
|
||||
|suggested_uri| self.check_redirect_uri(suggested_uri),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn check_redirect_uri<'a>(
|
||||
&self,
|
||||
redirect_uri: &'a Url,
|
||||
) -> Result<&'a Url, InvalidRedirectUriError> {
|
||||
if self.redirect_uris.contains(redirect_uri) {
|
||||
Ok(redirect_uri)
|
||||
} else {
|
||||
Err(InvalidRedirectUriError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct OAuth2Config {
|
||||
#[serde(default)]
|
||||
pub clients: Vec<OAuth2ClientConfig>,
|
||||
|
||||
#[serde(default)]
|
||||
pub keys: Vec<KeyConfig>,
|
||||
}
|
||||
|
||||
impl OAuth2Config {
|
||||
impl SecretsConfig {
|
||||
pub async fn key_store(&self) -> anyhow::Result<StaticKeystore> {
|
||||
let mut store = StaticKeystore::new();
|
||||
|
||||
@@ -189,12 +157,17 @@ impl OAuth2Config {
|
||||
|
||||
Ok(store)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn encrypter(&self) -> Encrypter {
|
||||
Encrypter::new(&self.encryption)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ConfigurationSection<'_> for OAuth2Config {
|
||||
impl ConfigurationSection<'_> for SecretsConfig {
|
||||
fn path() -> &'static str {
|
||||
"oauth2"
|
||||
"secrets"
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
@@ -237,7 +210,7 @@ impl ConfigurationSection<'_> for OAuth2Config {
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
clients: Vec::new(),
|
||||
encryption: rand::random(),
|
||||
keys: vec![rsa_key, ecdsa_key],
|
||||
})
|
||||
}
|
||||
@@ -276,97 +249,8 @@ impl ConfigurationSection<'_> for OAuth2Config {
|
||||
};
|
||||
|
||||
Self {
|
||||
clients: Vec::new(),
|
||||
encryption: [0xEA; 32],
|
||||
keys: vec![rsa_key, ecdsa_key],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use figment::Jail;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn load_config() {
|
||||
Jail::expect_with(|jail| {
|
||||
jail.create_file(
|
||||
"config.yaml",
|
||||
r#"
|
||||
oauth2:
|
||||
keys:
|
||||
- type: rsa
|
||||
key: |
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAymS2RkeIZo7pUeEN
|
||||
QUGCG4GLJru5jzxomO9jiNr5D/oRcerhpQVc9aCpBfAAg4l4a1SmYdBzWqX0X5pU
|
||||
scgTtQIDAQABAkEArNIMlrxUK4bSklkCcXtXdtdKE9vuWfGyOw0GyAB69fkEUBxh
|
||||
3j65u+u3ZmW+bpMWHgp1FtdobE9nGwb2VBTWAQIhAOyU1jiUEkrwKK004+6b5QRE
|
||||
vC9UI2vDWy5vioMNx5Y1AiEA2wGAJ6ETF8FF2Vd+kZlkKK7J0em9cl0gbJDsWIEw
|
||||
N4ECIEyWYkMurD1WQdTQqnk0Po+DMOihdFYOiBYgRdbnPxWBAiEAmtd0xJAd7622
|
||||
tPQniMnrBtiN2NxqFXHCev/8Gpc8gAECIBcaPcF59qVeRmYrfqzKBxFm7LmTwlAl
|
||||
Gh7BNzCeN+D6
|
||||
-----END PRIVATE KEY-----
|
||||
- type: ecdsa
|
||||
key: |
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgqfn5mYO/5Qq/wOOiWgHA
|
||||
NaiDiepgUJ2GI5eq2V8D8nahRANCAARMK9aKUd/H28qaU+0qvS6bSJItzAge1VHn
|
||||
OhBAAUVci1RpmUA+KdCL5sw9nadAEiONeiGr+28RYHZmlB9qXnjC
|
||||
-----END PRIVATE KEY-----
|
||||
clients:
|
||||
- client_id: public
|
||||
client_auth_method: none
|
||||
redirect_uris:
|
||||
- https://exemple.fr/callback
|
||||
|
||||
- client_id: secret-basic
|
||||
client_auth_method: client_secret_basic
|
||||
client_secret: hello
|
||||
|
||||
- client_id: secret-post
|
||||
client_auth_method: client_secret_post
|
||||
client_secret: hello
|
||||
|
||||
- client_id: secret-jwk
|
||||
client_auth_method: client_secret_jwt
|
||||
client_secret: hello
|
||||
|
||||
- client_id: jwks
|
||||
client_auth_method: private_key_jwt
|
||||
jwks:
|
||||
keys:
|
||||
- kid: "03e84aed4ef4431014e8617567864c4efaaaede9"
|
||||
kty: "RSA"
|
||||
alg: "RS256"
|
||||
use: "sig"
|
||||
e: "AQAB"
|
||||
n: "ma2uRyBeSEOatGuDpCiV9oIxlDWix_KypDYuhQfEzqi_BiF4fV266OWfyjcABbam59aJMNvOnKW3u_eZM-PhMCBij5MZ-vcBJ4GfxDJeKSn-GP_dJ09rpDcILh8HaWAnPmMoi4DC0nrfE241wPISvZaaZnGHkOrfN_EnA5DligLgVUbrA5rJhQ1aSEQO_gf1raEOW3DZ_ACU3qhtgO0ZBG3a5h7BPiRs2sXqb2UCmBBgwyvYLDebnpE7AotF6_xBIlR-Cykdap3GHVMXhrIpvU195HF30ZoBU4dMd-AeG6HgRt4Cqy1moGoDgMQfbmQ48Hlunv9_Vi2e2CLvYECcBw"
|
||||
|
||||
- kid: "d01c1abe249269f72ef7ca2613a86c9f05e59567"
|
||||
kty: "RSA"
|
||||
alg: "RS256"
|
||||
use: "sig"
|
||||
e: "AQAB"
|
||||
n: "0hukqytPwrj1RbMYhYoepCi3CN5k7DwYkTe_Cmb7cP9_qv4ok78KdvFXt5AnQxCRwBD7-qTNkkfMWO2RxUMBdQD0ED6tsSb1n5dp0XY8dSWiBDCX8f6Hr-KolOpvMLZKRy01HdAWcM6RoL9ikbjYHUEW1C8IJnw3MzVHkpKFDL354aptdNLaAdTCBvKzU9WpXo10g-5ctzSlWWjQuecLMQ4G1mNdsR1LHhUENEnOvgT8cDkX0fJzLbEbyBYkdMgKggyVPEB1bg6evG4fTKawgnf0IDSPxIU-wdS9wdSP9ZCJJPLi5CEp-6t6rE_sb2dGcnzjCGlembC57VwpkUvyMw"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let config = OAuth2Config::load_from_file("config.yaml")?;
|
||||
|
||||
assert_eq!(config.clients.len(), 5);
|
||||
|
||||
assert_eq!(config.clients[0].client_id, "public");
|
||||
assert_eq!(
|
||||
config.clients[0].redirect_uris,
|
||||
vec!["https://exemple.fr/callback".parse().unwrap()]
|
||||
);
|
||||
|
||||
assert_eq!(config.clients[1].client_id, "secret-basic");
|
||||
assert_eq!(config.clients[1].redirect_uris, Vec::new());
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user