switch to scope-based tokens
This commit is contained in:
parent
b188b399bb
commit
3450130f17
|
@ -24,8 +24,12 @@ pub static CONFIG: Lazy<HotelConfig> = Lazy::new(|| {
|
|||
#[derive(Serialize, Debug, Deserialize)]
|
||||
pub struct HotelConfig {
|
||||
pub db_uri: String,
|
||||
pub authorized_3fa_tokens: Vec<String>,
|
||||
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>
|
||||
}
|
|
@ -15,6 +15,7 @@ pub mod models;
|
|||
pub mod schema;
|
||||
pub mod routes;
|
||||
pub mod util;
|
||||
pub mod tokens;
|
||||
|
||||
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ use rand::Rng;
|
|||
use serde::{Serialize, Deserialize};
|
||||
use crate::models::{Code3FA, NewCode3FA, User};
|
||||
use crate::PgPool;
|
||||
use crate::tokens::{Scope, token_has_scope};
|
||||
use crate::util::current_unix_time;
|
||||
|
||||
#[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::codes_3fa;
|
||||
|
||||
if !CONFIG.authorized_3fa_tokens.contains(&req.token) {
|
||||
if !token_has_scope(&req.token, &Scope::Code3FAAdd) {
|
||||
return HttpResponse::Unauthorized().json(APIErrorResponse {
|
||||
errors: vec![
|
||||
APIError {
|
||||
code: "ERR_INVALID_CODEREQ_TOKEN".to_string(),
|
||||
message: "Invalid codereq token".to_string(),
|
||||
code: "ERR_MISSING_SCOPE".to_string(),
|
||||
message: "This endpoint requires the 3fa:add scope".to_string(),
|
||||
}
|
||||
],
|
||||
})
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::error::{APIError, APIErrorResponse};
|
|||
use crate::models::{NewUser, User};
|
||||
use crate::PgPool;
|
||||
use diesel::prelude::*;
|
||||
use crate::tokens::{Scope, token_has_scope};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct UserAddRequest {
|
||||
|
@ -29,12 +30,12 @@ pub struct UserResponse {
|
|||
pub async fn add_user_request(pool: Data<PgPool>, req: Json<UserAddRequest>) -> HttpResponse {
|
||||
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 {
|
||||
errors: vec![
|
||||
APIError {
|
||||
code: "ERR_INVALID_NEW_USER_TOKEN".to_string(),
|
||||
message: "Invalid newuser token".to_string(),
|
||||
code: "ERR_MISSING_SCOPE".to_string(),
|
||||
message: "This endpoint requires the user:add scope".to_string(),
|
||||
}
|
||||
],
|
||||
})
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue