userget
This commit is contained in:
parent
3450130f17
commit
2b7213021a
|
@ -72,6 +72,7 @@ async fn main() -> std::io::Result<()> {
|
|||
}))
|
||||
.service(routes::v1::code_3fa::get_3fa_code)
|
||||
.service(routes::v1::user_add::add_user_request)
|
||||
.service(routes::v1::user_get::get_user_request)
|
||||
})
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.run()
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod code_3fa;
|
||||
pub mod user_add;
|
||||
pub mod user_get;
|
|
@ -41,6 +41,17 @@ pub async fn add_user_request(pool: Data<PgPool>, req: Json<UserAddRequest>) ->
|
|||
})
|
||||
}
|
||||
|
||||
if !token_has_scope(&req.token, &Scope::UserRead) {
|
||||
return HttpResponse::Unauthorized().json(APIErrorResponse {
|
||||
errors: vec![
|
||||
APIError {
|
||||
code: "ERR_MISSING_SCOPE".to_string(),
|
||||
message: "This endpoint requires the user:read scope".to_string(),
|
||||
}
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
let req_clone = req.clone();
|
||||
let pool_clone = pool.clone();
|
||||
let results = match web::block(move || {
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
use actix_web::{HttpResponse, web};
|
||||
use actix_web::post;
|
||||
use actix_web::web::{Data, Json};
|
||||
use log::error;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
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 UserGetRequest {
|
||||
pub token: String,
|
||||
pub name: Option<String>,
|
||||
pub discord_id: Option<i64>,
|
||||
pub id: Option<i32>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct UserResponse {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub discord_id: i64,
|
||||
pub password_hash: String
|
||||
}
|
||||
|
||||
#[post("/v1/user/get")]
|
||||
pub async fn get_user_request(pool: Data<PgPool>, req: Json<UserGetRequest>) -> HttpResponse {
|
||||
use crate::schema::users;
|
||||
|
||||
if !token_has_scope(&req.token, &Scope::UserRead) {
|
||||
return HttpResponse::Unauthorized().json(APIErrorResponse {
|
||||
errors: vec![
|
||||
APIError {
|
||||
code: "ERR_MISSING_SCOPE".to_string(),
|
||||
message: "This endpoint requires the user:read scope".to_string(),
|
||||
}
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
let mut amt = 0;
|
||||
if req.id.is_some() { amt += 1; }
|
||||
if req.name.is_some() { amt += 1; }
|
||||
if req.discord_id.is_some() { amt += 1; }
|
||||
|
||||
if amt == 0 {
|
||||
return HttpResponse::BadRequest().json(APIErrorResponse {
|
||||
errors: vec![
|
||||
APIError {
|
||||
code: "ERR_NOT_SPECIFIC_ENOUGH".to_string(),
|
||||
message: "One filter must be provided".to_string(),
|
||||
}
|
||||
],
|
||||
})
|
||||
} else if amt != 1 {
|
||||
return HttpResponse::BadRequest().json(APIErrorResponse {
|
||||
errors: vec![
|
||||
APIError {
|
||||
code: "ERR_TOO_SPECIFIC".to_string(),
|
||||
message: "Only one filter may be provided".to_string(),
|
||||
}
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
let req_clone = req.clone();
|
||||
let pool_clone = pool.clone();
|
||||
let results = match web::block(move || {
|
||||
let mut conn = pool_clone.get().expect("Unable to get db pool");
|
||||
|
||||
if let Some(id) = req_clone.id {
|
||||
users::table.filter(users::id.eq(id)).load::<User>(&mut conn)
|
||||
} else if let Some(name) = req_clone.name {
|
||||
users::table.filter(users::name.eq(name)).load::<User>(&mut conn)
|
||||
} else if let Some(discord_id) = req_clone.discord_id {
|
||||
users::table.filter(users::discord_id.eq(discord_id)).load::<User>(&mut conn)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}).await {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
error!("Database error: {}", e);
|
||||
return HttpResponse::InternalServerError().json(APIErrorResponse {
|
||||
errors: vec![
|
||||
APIError {
|
||||
code: "ERR_BLOCKING_ERROR".to_string(),
|
||||
message: "There was an error running the database request. Please try again later.".to_string()
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
};
|
||||
let user_list = match results {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
error!("Database error: {}", e);
|
||||
return HttpResponse::InternalServerError().json(APIErrorResponse {
|
||||
errors: vec![
|
||||
APIError {
|
||||
code: "ERR_DB_ERROR".to_string(),
|
||||
message: "There was an error fetching the user. Please try again later.".to_string()
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
};
|
||||
if user_list.is_empty() {
|
||||
return HttpResponse::Unauthorized().json(APIErrorResponse {
|
||||
errors: vec![
|
||||
APIError {
|
||||
code: "ERR_USER_DOES_NOT_EXIST".to_string(),
|
||||
message: "Unable to find a user by that search query".to_string()
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
let user = &user_list[0];
|
||||
|
||||
HttpResponse::Ok().json(UserResponse {
|
||||
id: user.id,
|
||||
name: user.name.clone(),
|
||||
discord_id: user.discord_id,
|
||||
password_hash: user.password_hash.clone(),
|
||||
})
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
use actix_web::web::to;
|
||||
use crate::config::CONFIG;
|
||||
|
||||
pub fn token_valid(token: &str) -> bool {
|
||||
|
@ -11,41 +12,48 @@ pub fn token_has_scope(token: &str, scope: &Scope) -> bool {
|
|||
scopes.contains(scope)
|
||||
}
|
||||
|
||||
pub fn token_has_scopes(token: &str, req_scopes: &Vec<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();
|
||||
|
||||
for scope in req_scopes {
|
||||
if !scopes.contains(scope) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub enum Scope<'a> {
|
||||
pub enum Scope {
|
||||
Code3FAAdd,
|
||||
UserAdd,
|
||||
UserRead,
|
||||
UserRemove,
|
||||
Unknown { scope: &'a str }
|
||||
Unknown { scope: String }
|
||||
}
|
||||
impl<'a> From<&str> for Scope<'a> {
|
||||
impl From<&str> for Scope {
|
||||
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 }
|
||||
_ => Scope::Unknown { scope: value.to_string() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Scope<'a>> for &str {
|
||||
fn from(value: Scope<'a>) -> Self {
|
||||
impl From<Scope> for String {
|
||||
fn from(value: Scope) -> Self {
|
||||
match value {
|
||||
Scope::Code3FAAdd => "3fa:add",
|
||||
Scope::UserAdd => "user:add",
|
||||
Scope::UserRead => "user:read",
|
||||
Scope::UserRemove => "user:remove",
|
||||
Scope::Code3FAAdd => "3fa:add".to_string(),
|
||||
Scope::UserAdd => "user:add".to_string(),
|
||||
Scope::UserRead => "user:read".to_string(),
|
||||
Scope::UserRemove => "user:remove".to_string(),
|
||||
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