aboutsummaryrefslogtreecommitdiff
path: root/crates/secd/src/util
diff options
context:
space:
mode:
authorbenj <benj@rse8.com>2022-12-24 00:43:38 -0800
committerbenj <benj@rse8.com>2022-12-24 00:43:38 -0800
commitc2268c285648ef02ece04de0d9df0813c6d70ff8 (patch)
treef84ec7ee42f97d78245f26d0c5a0c559cd35e89d /crates/secd/src/util
parentde6339da72af1d61ca5908b780977e2b037ce014 (diff)
downloadsecdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar
secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar.gz
secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar.bz2
secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar.lz
secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar.xz
secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar.zst
secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.zip
refactor everything with more abstraction and a nicer interface
Diffstat (limited to '')
-rw-r--r--crates/secd/src/util/from.rs66
-rw-r--r--crates/secd/src/util/mod.rs189
2 files changed, 99 insertions, 156 deletions
diff --git a/crates/secd/src/util/from.rs b/crates/secd/src/util/from.rs
new file mode 100644
index 0000000..bab8a25
--- /dev/null
+++ b/crates/secd/src/util/from.rs
@@ -0,0 +1,66 @@
+use std::str::FromStr;
+
+use crate::AuthStore;
+
+impl From<Option<String>> for AuthStore {
+ fn from(s: Option<String>) -> Self {
+ let conn = s.clone().unwrap_or("sqlite::memory:".into());
+ match conn.split(":").next() {
+ Some("postgresql") | Some("postgres") => AuthStore::Postgres { conn },
+ Some("sqlite") => AuthStore::Sqlite { conn },
+ _ => panic!(
+ "AuthStore: Invalid database connection string provided. Found: {:?}",
+ s
+ ),
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn auth_store_from_string() {
+ let in_conn = None;
+ let a = AuthStore::from(in_conn.clone());
+ match AuthStore::from(in_conn.clone()) {
+ AuthStore::Sqlite { conn } => assert_eq!(conn, "sqlite::memory:"),
+ r @ _ => assert!(
+ false,
+ "should have parsed None as in-memory sqlite. Found: {:?}",
+ r
+ ),
+ }
+
+ let postgresql_conn = Some("postgresql://testuser:p4ssw0rd@1.2.3.4:5432/test-db".into());
+ match AuthStore::from(postgresql_conn.clone()) {
+ AuthStore::Postgres { conn } => assert_eq!(conn, postgresql_conn.unwrap()),
+ r @ _ => assert!(false, "should have parsed as postgres. Found: {:?}", r),
+ }
+
+ let postgres_conn = Some("postgres://testuser:p4ssw0rd@1.2.3.4:5432/test-db".into());
+ match AuthStore::from(postgres_conn.clone()) {
+ AuthStore::Postgres { conn } => assert_eq!(conn, postgres_conn.unwrap()),
+ r @ _ => assert!(false, "should have parsed as postgres. Found: {:?}", r),
+ }
+
+ let sqlite_conn = Some("sqlite:///path/to/db.sql".into());
+ let a = AuthStore::from(sqlite_conn.clone());
+ match AuthStore::from(sqlite_conn.clone()) {
+ AuthStore::Sqlite { conn } => assert_eq!(conn, sqlite_conn.unwrap()),
+ r @ _ => assert!(false, "should have parsed as sqlite. Found: {:?}", r),
+ }
+
+ let sqlite_mem_conn = Some("sqlite:memory:".into());
+ let a = AuthStore::from(sqlite_mem_conn.clone());
+ match AuthStore::from(sqlite_mem_conn.clone()) {
+ AuthStore::Sqlite { conn } => assert_eq!(conn, sqlite_mem_conn.unwrap()),
+ r @ _ => assert!(
+ false,
+ "should have parsed as in-memoy sqlite. Found: {:?}",
+ r
+ ),
+ }
+ }
+}
diff --git a/crates/secd/src/util/mod.rs b/crates/secd/src/util/mod.rs
index bb177cb..6677c2f 100644
--- a/crates/secd/src/util/mod.rs
+++ b/crates/secd/src/util/mod.rs
@@ -1,38 +1,12 @@
-use std::str::FromStr;
+pub(crate) mod from;
-use anyhow::{bail, Context};
-use log::error;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
-use reqwest::header;
-use serde::{Deserialize, Serialize};
+use sha2::{Digest, Sha256};
+use time::OffsetDateTime;
use url::Url;
-use crate::{
- OauthProvider, OauthProviderName, OauthValidation, SecdError, ValidationRequestId,
- INTERNAL_ERR_MSG,
-};
-
-pub(crate) fn log_err(e: Box<dyn std::error::Error>, new_e: SecdError) -> SecdError {
- error!("{:?}", e);
- new_e
-}
-pub(crate) fn to_secd_err(e: anyhow::Error, new_e: SecdError) -> SecdError {
- error!("{:?}", e);
- new_e
-}
-
-pub(crate) fn log_err_sqlx(e: sqlx::Error) -> sqlx::Error {
- error!("{:?}", e);
- e
-}
-pub(crate) fn generate_random_url_safe(n: usize) -> String {
- thread_rng()
- .sample_iter(&Alphanumeric)
- .take(n)
- .map(char::from)
- .collect()
-}
+use crate::{AddressType, IdentityId, SecdError, Session, SESSION_DURATION, SESSION_SIZE_BYTES};
pub(crate) fn remove_trailing_slash(url: &mut Url) -> String {
let mut u = url.to_string();
@@ -44,134 +18,37 @@ pub(crate) fn remove_trailing_slash(url: &mut Url) -> String {
u
}
-pub(crate) fn build_oauth_auth_url(
- p: &OauthProvider,
- validation_id: ValidationRequestId,
-) -> Result<Url, SecdError> {
- let redirect_url = remove_trailing_slash(&mut p.redirect_url.clone());
-
- Ok(Url::from_str(&format!(
- "{}?client_id={}&response_type={}&redirect_uri={}&scope={}&state={}",
- p.base_url,
- p.client_id,
- p.response.to_string().to_lowercase(),
- redirect_url,
- p.default_scope,
- validation_id.to_string()
- ))
- .map_err(|_| SecdError::InternalError(INTERNAL_ERR_MSG.into()))?)
-}
-
-pub(crate) async fn get_oauth_identity_data(
- validation: &OauthValidation,
-) -> anyhow::Result<OauthAccessIdentity> {
- let provider = validation.oauth_provider.name;
- let token = validation
- .access_token
- .clone()
- .ok_or(SecdError::InternalError(
- "no access token provided with which to build oauth data url".into(),
- ))?;
-
- let url = Url::from_str(&format!(
- "{}{}",
- match provider {
- OauthProviderName::Google =>
- "https://www.googleapis.com/oauth2/v2/userinfo?access_token=",
- _ => unimplemented!(),
- },
- token
- ))?;
-
- let resp = reqwest::get(url).await?.json::<serde_json::Value>().await?;
- let identity = match provider {
- OauthProviderName::Google => OauthAccessIdentity {
- email: resp
- .get("email")
- .and_then(|v| v.as_str().map(|s| s.to_string())),
- email_is_verified: resp.get("verified_email").and_then(|v| v.as_bool()),
- picture_url: resp
- .get("picture")
- .and_then(|v| Url::from_str(&v.to_string()).ok()),
- },
- _ => unimplemented!(),
- };
-
- Ok(identity)
-}
-
-#[derive(Debug, Serialize)]
-pub(crate) struct OauthAccessTokenGoogleRequest {
- grant_type: String,
- code: String,
- client_id: String,
- client_secret: String,
- redirect_uri: String,
-}
-
-#[derive(Debug, Deserialize)]
-pub(crate) struct OauthAccessTokenGoogleResponse {
- access_token: String,
- expires_in: i32,
- token_type: String,
- scope: String,
- id_token: String,
+pub(crate) fn hash(i: &[u8]) -> Vec<u8> {
+ let mut hasher = Sha256::new();
+ hasher.update(i);
+ hasher.finalize().to_vec()
}
-#[derive(Debug)]
-pub(crate) struct OauthAccessIdentity {
- pub(crate) email: Option<String>,
- pub(crate) email_is_verified: Option<bool>,
- pub(crate) picture_url: Option<Url>,
-}
-
-type AccessTokenRequestData = String;
-
-pub(crate) async fn get_oauth_access_token(
- validation: &OauthValidation,
- secret_code: &String,
-) -> anyhow::Result<String> {
- let provider = validation.oauth_provider.name;
-
- let url = Url::from_str(match provider {
- OauthProviderName::Google => "https://accounts.google.com/o/oauth2/token",
- _ => unimplemented!(),
- })?;
-
- let request_data = serde_json::to_string(&match provider {
- OauthProviderName::Google => OauthAccessTokenGoogleRequest {
- grant_type: "authorization_code".to_string(),
- code: secret_code.to_string(),
- client_id: validation.oauth_provider.client_id.clone(),
- client_secret: validation.oauth_provider.client_secret.clone(),
- redirect_uri: remove_trailing_slash(
- &mut validation.oauth_provider.redirect_url.clone(),
- ),
- },
- _ => unimplemented!(),
- })?;
-
- let r = reqwest::Client::new()
- .post(url)
- .body(request_data)
- .header(header::CONTENT_TYPE, "application/json")
- .send()
- .await
- .context(format!(
- "Failed to successfully POST a new access token for: {}",
- provider
- ))?;
-
- let access_token = match provider {
- OauthProviderName::Google => {
- let resp: OauthAccessTokenGoogleResponse = r.json().await.context(format!(
- "Failed to parse access token response for: {}",
- provider
- ))?;
- resp.access_token
+impl AddressType {
+ pub fn get_value(&self) -> Option<String> {
+ match &self {
+ AddressType::Email { email_address } => {
+ email_address.as_ref().map(|a| a.to_string().clone())
+ }
+ AddressType::Sms { phone_number } => phone_number.as_ref().cloned(),
}
- _ => unimplemented!(),
- };
+ }
+}
- Ok(access_token)
+impl Session {
+ pub(crate) fn new(identity_id: IdentityId) -> Result<Self, SecdError> {
+ let token = (0..SESSION_SIZE_BYTES)
+ .map(|_| rand::random::<u8>())
+ .collect::<Vec<u8>>();
+ let now = OffsetDateTime::now_utc();
+ Ok(Session {
+ identity_id,
+ token,
+ created_at: now,
+ expired_at: now
+ .checked_add(time::Duration::new(SESSION_DURATION, 0))
+ .ok_or(SecdError::Todo)?,
+ revoked_at: None,
+ })
+ }
}