switch to role based auth

This commit is contained in:
jb-alvarado 2022-06-13 18:29:37 +02:00
parent 019a163176
commit b15eb449dc
5 changed files with 45 additions and 14 deletions

View File

@ -13,16 +13,16 @@ const JWT_EXPIRATION_MINUTES: i64 = 60;
pub struct Claims {
pub id: i64,
pub username: String,
pub permissions: Vec<String>,
pub role: String,
exp: i64,
}
impl Claims {
pub fn new(id: i64, username: String, permissions: Vec<String>) -> Self {
pub fn new(id: i64, username: String, role: String) -> Self {
Self {
id,
username,
permissions,
role,
exp: (Utc::now() + Duration::minutes(JWT_EXPIRATION_MINUTES)).timestamp(),
}
}

View File

@ -128,8 +128,6 @@ pub async fn db_get_settings(id: &i64) -> Result<Settings, sqlx::Error> {
let result: Settings = sqlx::query_as(query).bind(id).fetch_one(&conn).await?;
conn.close().await;
println!("{:#?}", result);
Ok(result)
}

View File

@ -1,5 +1,5 @@
use actix_web::{get, http::StatusCode, patch, post, put, web, Responder};
use actix_web_grants::proc_macro::has_permissions;
use actix_web_grants::proc_macro::has_any_role;
use argon2::{
password_hash::{rand_core::OsRng, PasswordHash, SaltString},
Argon2, PasswordHasher, PasswordVerifier,
@ -14,6 +14,7 @@ use crate::api::{
db_add_user, db_get_settings, db_login, db_role, db_update_settings, db_update_user,
},
models::{LoginUser, Settings, User},
utils::Role,
};
#[derive(Serialize)]
@ -25,7 +26,7 @@ struct ResponseObj<T> {
/// curl -X GET http://127.0.0.1:8080/api/settings/1 -H "Authorization: Bearer <TOKEN>"
#[get("/settings/{id}")]
#[has_permissions("admin", "user")]
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
async fn get_settings(id: web::Path<i64>) -> Result<impl Responder, ServiceError> {
if let Ok(settings) = db_get_settings(&id).await {
return Ok(web::Json(ResponseObj {
@ -43,7 +44,7 @@ async fn get_settings(id: web::Path<i64>) -> Result<impl Responder, ServiceError
/// "config_path":"/etc/ffplayout/ffplayout.yml","extra_extensions":".jpg,.jpeg,.png"}' \
/// -H "Authorization: Bearer <TOKEN>"
#[patch("/settings/{id}")]
#[has_permissions("admin")]
#[has_any_role("Role::Admin", type = "Role")]
async fn patch_settings(
id: web::Path<i64>,
data: web::Json<Settings>,
@ -55,10 +56,22 @@ async fn patch_settings(
Err(ServiceError::InternalServerError)
}
#[get("/playout/config/{id}")]
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
async fn get_playout_config(id: web::Path<i64>) -> Result<impl Responder, ServiceError> {
if let Ok(settings) = db_get_settings(&id).await {
println!("{:?}", settings.config_path);
return Ok("settings");
};
Err(ServiceError::InternalServerError)
}
/// curl -X PUT http://localhost:8080/api/user/1 --header 'Content-Type: application/json' \
/// --data '{"email": "<EMAIL>", "password": "<PASS>"}' --header 'Authorization: <TOKEN>'
#[put("/user/{id}")]
#[has_permissions("admin", "user")]
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
async fn update_user(
id: web::Path<i64>,
user: web::ReqData<LoginUser>,
@ -98,7 +111,7 @@ async fn update_user(
/// -d '{"email": "<EMAIL>", "username": "<USER>", "password": "<PASS>", "role_id": 1}' \
/// --header 'Authorization: Bearer <TOKEN>'
#[post("/user/")]
#[has_permissions("admin")]
#[has_any_role("Role::Admin", type = "Role")]
async fn add_user(data: web::Json<User>) -> Result<impl Responder, ServiceError> {
match db_add_user(data.into_inner()).await {
Ok(_) => Ok("Add User Success"),
@ -127,7 +140,7 @@ pub async fn login(credentials: web::Json<User>) -> impl Responder {
let role = db_role(&user.role_id.unwrap_or_default())
.await
.unwrap_or_else(|_| "guest".to_string());
let claims = Claims::new(user.id, user.username.clone(), vec![role.clone()]);
let claims = Claims::new(user.id, user.username.clone(), role.clone());
if let Ok(token) = create_jwt(claims) {
user.token = Some(token);

View File

@ -7,6 +7,23 @@ use crate::api::{
models::User,
};
#[derive(PartialEq, Clone)]
pub enum Role {
Admin,
User,
Guest,
}
impl Role {
pub fn set_role(role: &str) -> Self {
match role {
"admin" => Role::Admin,
"user" => Role::User,
_ => Role::Guest,
}
}
}
#[derive(Debug, sqlx::FromRow)]
pub struct GlobalSettings {
pub secret: String,

View File

@ -13,8 +13,8 @@ use ffplayout_engine::{
args_parse::Args,
auth,
models::LoginUser,
routes::{add_user, get_settings, login, patch_settings, update_user},
utils::{init_config, run_args},
routes::{add_user, get_playout_config, get_settings, login, patch_settings, update_user},
utils::{init_config, run_args, Role},
},
utils::{init_logging, GlobalConfig},
};
@ -22,10 +22,12 @@ use ffplayout_engine::{
async fn validator(req: ServiceRequest, credentials: BearerAuth) -> Result<ServiceRequest, Error> {
// We just get permissions from JWT
let claims = auth::decode_jwt(credentials.token()).await?;
req.attach(claims.permissions);
req.attach(vec![Role::set_role(&claims.role)]);
req.extensions_mut()
.insert(LoginUser::new(claims.id, claims.username));
println!("{:#?}", req);
Ok(req)
}
@ -63,6 +65,7 @@ async fn main() -> std::io::Result<()> {
web::scope("/api")
.wrap(auth)
.service(add_user)
.service(get_playout_config)
.service(get_settings)
.service(patch_settings)
.service(update_user),