-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
identity: implement config file and http redirect
- Loading branch information
Showing
8 changed files
with
660 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,8 +4,13 @@ | |
|
||
*.swp | ||
.DS_Store | ||
|
||
# For local development | ||
.direnv | ||
.envrc | ||
my_config.toml | ||
|
||
# SQLite | ||
*.db | ||
|
||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Default settings. Alternative options are commented out. | ||
|
||
[cache] | ||
# By default, we use the cache directory on your machine (from | ||
# `$XDG_CACHE_HOME/vr_identity` or `~/.config/cache/vr_identity` | ||
# dir = "path/to/my/cache/dir" | ||
|
||
# Note: If both HTTP and HTTPS are enabled, the HTTP server will always redirect to the | ||
# HTTPS endpoints. | ||
[http] | ||
port = 80 # also supports 0 to mean random | ||
|
||
# Note: Enabling HTTPS always will force HSTS on. | ||
[https] | ||
port = 443 # also supports 0 to mean random | ||
|
||
[https.tls] | ||
type = "acme" # requires publicly visible port to be 443, otherwise the challenge fails | ||
domains = [] # The public domain name(s). Should not include the url scheme. | ||
# domains = ["socialvr.net", "socialvr.net:1337", "10.11.12.13"] | ||
|
||
# [https.tls] | ||
# type = "self_signed" | ||
# domains = ["socialvr.net"] | ||
|
||
# [https.tls] | ||
# type = "file" | ||
# cert_path = "path/to/cert.pem" | ||
# private_key_path = "another/path/key.pem" | ||
|
||
[third_party.google] | ||
# To get the client id, follow the instructions at: | ||
# https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid#get_your_google_api_client_id | ||
oauth2_client_id = "" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
//! Structs representing deserialized config file | ||
//! | ||
//! See [`Config`]. | ||
use std::path::PathBuf; | ||
|
||
use serde::{Deserialize, Serialize}; | ||
|
||
pub const DEFAULT_CONFIG_CONTENTS: &str = include_str!("../default_config.toml"); | ||
|
||
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)] | ||
#[serde(deny_unknown_fields)] | ||
pub enum DatabaseConfig { | ||
Sqlite { db_file: PathBuf }, | ||
} | ||
|
||
impl Default for DatabaseConfig { | ||
fn default() -> Self { | ||
Self::Sqlite { | ||
db_file: PathBuf::from(".").join("identities.db"), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone, Default)] | ||
#[serde(deny_unknown_fields)] | ||
pub struct CacheSettings { | ||
/// If `None`, relies on `XDG_CACHE_HOME` instead. | ||
pub dir: Option<PathBuf>, | ||
} | ||
|
||
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)] | ||
#[serde(deny_unknown_fields)] | ||
pub struct HttpConfig { | ||
/// If `0`, uses a random available port. | ||
pub port: u16, | ||
} | ||
|
||
impl Default for HttpConfig { | ||
fn default() -> Self { | ||
Self { port: 80 } | ||
} | ||
} | ||
|
||
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)] | ||
#[serde(deny_unknown_fields)] | ||
pub struct HttpsConfig { | ||
/// If `0`, uses a random available port. | ||
pub port: u16, | ||
#[serde(default)] | ||
pub tls: TlsConfig, | ||
} | ||
|
||
impl Default for HttpsConfig { | ||
fn default() -> Self { | ||
Self { | ||
port: 443, | ||
tls: TlsConfig::default(), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)] | ||
#[serde(deny_unknown_fields)] | ||
pub struct ThirdPartySettings { | ||
#[serde(default = "default_some")] | ||
pub google: Option<GoogleSettings>, | ||
} | ||
|
||
impl Default for ThirdPartySettings { | ||
fn default() -> Self { | ||
Self { | ||
google: Some(GoogleSettings::default()), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone, Default)] | ||
#[serde(deny_unknown_fields)] | ||
pub struct GoogleSettings { | ||
/// The Google API OAuth2 Client ID. | ||
/// See https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid | ||
#[serde(default)] | ||
pub oauth2_client_id: String, | ||
} | ||
|
||
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)] | ||
#[serde(deny_unknown_fields, tag = "type", rename_all = "snake_case")] | ||
pub enum TlsConfig { | ||
/// LetsEncrypt's certificate authoriy and the TLS-ALPN-01 challenge type to get a | ||
/// valid signed certificate. | ||
/// Read more at https://letsencrypt.org/docs/challenge-types/#tls-alpn-01 | ||
Acme { | ||
domains: Vec<String>, | ||
}, | ||
/// Creates a self-signed certificate | ||
SelfSigned { | ||
domains: Vec<String>, | ||
}, | ||
File { | ||
path: PathBuf, | ||
}, | ||
} | ||
|
||
impl Default for TlsConfig { | ||
fn default() -> Self { | ||
Self::Acme { | ||
domains: Vec::new(), | ||
} | ||
} | ||
} | ||
|
||
/// Helper function to construct `Some(T::default())`. | ||
fn default_some<T: Default>() -> Option<T> { | ||
Some(T::default()) | ||
} | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum ConfigError { | ||
#[error("error in toml file: {0}")] | ||
Toml(#[from] toml::de::Error), | ||
} | ||
|
||
/// The contents of the config file. Contains all settings customizeable during | ||
/// deployment. | ||
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)] | ||
#[serde(deny_unknown_fields)] | ||
pub struct Config { | ||
#[serde(default)] | ||
pub database: DatabaseConfig, | ||
#[serde(default = "default_some")] | ||
pub http: Option<HttpConfig>, | ||
#[serde(default = "default_some")] | ||
pub https: Option<HttpsConfig>, | ||
#[serde(default)] | ||
pub cache: CacheSettings, | ||
#[serde(default)] | ||
pub third_party: ThirdPartySettings, | ||
} | ||
|
||
impl Config { | ||
pub fn from_str(str: &str) -> Result<Self, ConfigError> { | ||
let config: Self = toml::from_str(str)?; | ||
|
||
Ok(config) | ||
} | ||
} | ||
|
||
impl Default for Config { | ||
fn default() -> Self { | ||
Self { | ||
database: DatabaseConfig::default(), | ||
http: Some(HttpConfig::default()), | ||
https: Some(HttpsConfig::default()), | ||
cache: CacheSettings::default(), | ||
third_party: ThirdPartySettings::default(), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
|
||
/// We could have used Config::default, but I wanted to explicitly write it all out | ||
/// in case something is messed up. | ||
fn default_config() -> Config { | ||
Config { | ||
database: DatabaseConfig::Sqlite { | ||
db_file: PathBuf::from("./identities.db"), | ||
}, | ||
http: Some(HttpConfig { port: 80 }), | ||
https: Some(HttpsConfig { | ||
port: 443, | ||
tls: TlsConfig::Acme { | ||
domains: Vec::new(), | ||
}, | ||
}), | ||
cache: CacheSettings { dir: None }, | ||
third_party: ThirdPartySettings { | ||
google: Some(GoogleSettings { | ||
oauth2_client_id: String::new(), | ||
}), | ||
}, | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_empty_config_file_deserializes_to_default() { | ||
let config = Config::from_str("").unwrap(); | ||
assert_eq!(config, default_config()); | ||
assert_eq!(config, Config::default()); | ||
} | ||
|
||
#[test] | ||
fn test_default_config_deserializes_correctly() { | ||
let deserialized: Config = toml::from_str(DEFAULT_CONFIG_CONTENTS) | ||
.expect("default config file should always deserialize"); | ||
assert_eq!(deserialized, Config::default()); | ||
} | ||
} |
Oops, something went wrong.