switch to scope-based tokens

This commit is contained in:
c0repwn3r 2023-03-26 15:01:39 -04:00
parent b188b399bb
commit 3450130f17
Signed by: core
GPG Key ID: FDBF740DADDCEECF
5 changed files with 66 additions and 8 deletions

View File

@ -24,8 +24,12 @@ pub static CONFIG: Lazy<HotelConfig> = Lazy::new(|| {
#[derive(Serialize, Debug, Deserialize)] #[derive(Serialize, Debug, Deserialize)]
pub struct HotelConfig { pub struct HotelConfig {
pub db_uri: String, pub db_uri: String,
pub authorized_3fa_tokens: Vec<String>,
pub mfa_codes_expire_in: i64, pub mfa_codes_expire_in: i64,
pub authorized_new_user_tokens: Vec<String> pub tokens: Vec<HotelConfigToken>
} }
#[derive(Serialize, Debug, Deserialize)]
pub struct HotelConfigToken {
pub token: String,
pub scopes: Vec<String>
}

View File

@ -15,6 +15,7 @@ pub mod models;
pub mod schema; pub mod schema;
pub mod routes; pub mod routes;
pub mod util; pub mod util;
pub mod tokens;
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!();

View File

@ -9,6 +9,7 @@ use rand::Rng;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::models::{Code3FA, NewCode3FA, User}; use crate::models::{Code3FA, NewCode3FA, User};
use crate::PgPool; use crate::PgPool;
use crate::tokens::{Scope, token_has_scope};
use crate::util::current_unix_time; use crate::util::current_unix_time;
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone)]
@ -30,12 +31,12 @@ pub async fn get_3fa_code(pool: Data<PgPool>, req: Json<CodeRequest3FA>) -> Http
use crate::schema::users; use crate::schema::users;
use crate::schema::codes_3fa; use crate::schema::codes_3fa;
if !CONFIG.authorized_3fa_tokens.contains(&req.token) { if !token_has_scope(&req.token, &Scope::Code3FAAdd) {
return HttpResponse::Unauthorized().json(APIErrorResponse { return HttpResponse::Unauthorized().json(APIErrorResponse {
errors: vec![ errors: vec![
APIError { APIError {
code: "ERR_INVALID_CODEREQ_TOKEN".to_string(), code: "ERR_MISSING_SCOPE".to_string(),
message: "Invalid codereq token".to_string(), message: "This endpoint requires the 3fa:add scope".to_string(),
} }
], ],
}) })

View File

@ -8,6 +8,7 @@ use crate::error::{APIError, APIErrorResponse};
use crate::models::{NewUser, User}; use crate::models::{NewUser, User};
use crate::PgPool; use crate::PgPool;
use diesel::prelude::*; use diesel::prelude::*;
use crate::tokens::{Scope, token_has_scope};
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone)]
pub struct UserAddRequest { pub struct UserAddRequest {
@ -29,12 +30,12 @@ pub struct UserResponse {
pub async fn add_user_request(pool: Data<PgPool>, req: Json<UserAddRequest>) -> HttpResponse { pub async fn add_user_request(pool: Data<PgPool>, req: Json<UserAddRequest>) -> HttpResponse {
use crate::schema::users; use crate::schema::users;
if !CONFIG.authorized_new_user_tokens.contains(&req.token) { if !token_has_scope(&req.token, &Scope::UserAdd) {
return HttpResponse::Unauthorized().json(APIErrorResponse { return HttpResponse::Unauthorized().json(APIErrorResponse {
errors: vec![ errors: vec![
APIError { APIError {
code: "ERR_INVALID_NEW_USER_TOKEN".to_string(), code: "ERR_MISSING_SCOPE".to_string(),
message: "Invalid newuser token".to_string(), message: "This endpoint requires the user:add scope".to_string(),
} }
], ],
}) })

51
src/tokens.rs Normal file
View File

@ -0,0 +1,51 @@
use crate::config::CONFIG;
pub fn token_valid(token: &str) -> bool {
CONFIG.tokens.iter().any(|u| u.token == token)
}
pub fn token_has_scope(token: &str, scope: &Scope) -> bool {
if !token_valid(token) { return false; }
let token = CONFIG.tokens.iter().find(|u| u.token == token).unwrap();
let scopes: Vec<Scope> = token.scopes.iter().map(|f| f.as_str().into()).collect();
scopes.contains(scope)
}
#[derive(Eq, PartialEq, Debug)]
pub enum Scope<'a> {
Code3FAAdd,
UserAdd,
UserRead,
UserRemove,
Unknown { scope: &'a str }
}
impl<'a> From<&str> for Scope<'a> {
fn from(value: &str) -> Self {
match value {
"3fa:add" => Self::Code3FAAdd,
"user:add" => Self::UserAdd,
"user:read" => Self::UserRead,
"user:remove" => Self::UserRemove,
_ => Self::Unknown { scope: value }
}
}
}
impl<'a> From<Scope<'a>> for &str {
fn from(value: Scope<'a>) -> Self {
match value {
Scope::Code3FAAdd => "3fa:add",
Scope::UserAdd => "user:add",
Scope::UserRead => "user:read",
Scope::UserRemove => "user:remove",
Scope::Unknown { scope } => scope
}
}
}
impl<'a> From<Scope<'a>> for String {
fn from(value: Scope<'a>) -> Self {
let str_val: &str = value.into();
str_val.to_string()
}
}