mirror of
https://github.com/Noratrieb/karlauth.git
synced 2026-01-16 23:35:01 +01:00
works!
This commit is contained in:
parent
c2973477d1
commit
9e74f75148
3 changed files with 103 additions and 39 deletions
58
src/auth.rs
58
src/auth.rs
|
|
@ -1,42 +1,56 @@
|
||||||
use crate::errors::ServiceError;
|
use crate::errors::ServiceError;
|
||||||
use crate::models::User;
|
use crate::models::User;
|
||||||
use actix_web::dev::{Payload, ServiceRequest};
|
use actix_web::dev::{Payload, ServiceRequest};
|
||||||
use actix_web_httpauth::extractors::bearer::{BearerAuth, Config};
|
use actix_web::{FromRequest, HttpMessage, HttpRequest, HttpResponse};
|
||||||
use actix_web_httpauth::extractors::AuthenticationError;
|
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation};
|
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub enum Role {
|
pub enum Role {
|
||||||
None,
|
None = 0,
|
||||||
ReadAll,
|
ReadAll = 1,
|
||||||
WriteAll,
|
WriteAll = 2,
|
||||||
Admin,
|
Admin = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
struct Claims {
|
pub struct Claims {
|
||||||
exp: usize,
|
pub exp: usize,
|
||||||
uid: i32,
|
pub uid: i32,
|
||||||
role: Role,
|
pub role: Role,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRequest for Claims {
|
||||||
|
type Error = actix_web::Error;
|
||||||
|
type Future = std::future::Ready<Result<Self, Self::Error>>;
|
||||||
|
type Config = ();
|
||||||
|
|
||||||
|
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
||||||
|
std::future::ready(
|
||||||
|
req.extensions()
|
||||||
|
.get::<Claims>()
|
||||||
|
.map(|claims| claims.clone())
|
||||||
|
.ok_or(
|
||||||
|
HttpResponse::InternalServerError()
|
||||||
|
.json("Could not get claims")
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn validator(
|
pub async fn validator(
|
||||||
req: ServiceRequest,
|
req: ServiceRequest,
|
||||||
credentials: BearerAuth,
|
credentials: BearerAuth,
|
||||||
) -> Result<ServiceRequest, actix_web::Error> {
|
) -> Result<ServiceRequest, actix_web::Error> {
|
||||||
let config = req
|
|
||||||
.app_data::<Config>()
|
|
||||||
.map(|data| data.get_ref().clone())
|
|
||||||
.unwrap_or(Default::default());
|
|
||||||
|
|
||||||
match validate_token(credentials.token()) {
|
match validate_token(credentials.token()) {
|
||||||
Ok(claims) => {
|
Ok(claims) => {
|
||||||
//req.extensions_mut().insert(claims);
|
req.extensions_mut().insert(claims);
|
||||||
Ok(req)
|
Ok(req)
|
||||||
}
|
}
|
||||||
Err(err) => Err(AuthenticationError::from(config).into()),
|
Err(err) => Err(err.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,7 +65,7 @@ fn validate_token(token: &str) -> Result<Claims, ServiceError> {
|
||||||
.map_err(|_| ServiceError::JWTokenError)?
|
.map_err(|_| ServiceError::JWTokenError)?
|
||||||
.claims;
|
.claims;
|
||||||
|
|
||||||
if decoded.exp > Utc::now().timestamp() as usize {
|
if decoded.exp < Utc::now().timestamp() as usize {
|
||||||
Err(ServiceError::TokenExpiredError)
|
Err(ServiceError::TokenExpiredError)
|
||||||
} else {
|
} else {
|
||||||
Ok(decoded)
|
Ok(decoded)
|
||||||
|
|
@ -59,6 +73,10 @@ fn validate_token(token: &str) -> Result<Claims, ServiceError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_jwt(user: &User) -> Result<String, ServiceError> {
|
pub fn create_jwt(user: &User) -> Result<String, ServiceError> {
|
||||||
|
create_jwt_role(user, Role::ReadAll)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_jwt_role(user: &User, role: Role) -> Result<String, ServiceError> {
|
||||||
let expiration = Utc::now()
|
let expiration = Utc::now()
|
||||||
.checked_add_signed(chrono::Duration::weeks(10))
|
.checked_add_signed(chrono::Duration::weeks(10))
|
||||||
.expect("valid timestamp")
|
.expect("valid timestamp")
|
||||||
|
|
@ -67,7 +85,7 @@ pub fn create_jwt(user: &User) -> Result<String, ServiceError> {
|
||||||
let claims = Claims {
|
let claims = Claims {
|
||||||
exp: expiration as usize,
|
exp: expiration as usize,
|
||||||
uid: user.id,
|
uid: user.id,
|
||||||
role: Role::ReadAll,
|
role,
|
||||||
};
|
};
|
||||||
|
|
||||||
let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET env var");
|
let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET env var");
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
use super::actions;
|
use super::actions;
|
||||||
use super::Pool;
|
use super::Pool;
|
||||||
use crate::auth::create_jwt;
|
use crate::auth::{create_jwt, create_jwt_role, Claims, Role};
|
||||||
use crate::models::User;
|
use crate::models::User;
|
||||||
use actix_web::{web, HttpResponse};
|
use actix_web::error::ErrorUnauthorized;
|
||||||
|
use actix_web::web::Json;
|
||||||
|
use actix_web::{web, Error, HttpResponse};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
type HttpResult = Result<HttpResponse, actix_web::Error>;
|
type HttpResult = Result<HttpResponse, Error>;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct InputUser {
|
pub struct InputUser {
|
||||||
|
|
@ -21,19 +23,31 @@ struct UserWithToken {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// handler for `GET /users`
|
/// handler for `GET /users`
|
||||||
pub async fn get_users(db: web::Data<Pool>) -> HttpResult {
|
pub async fn get_users(db: web::Data<Pool>, claims: Claims) -> HttpResult {
|
||||||
Ok(web::block(move || actions::get_all_users(&db))
|
if claims.role >= Role::ReadAll {
|
||||||
.await
|
Ok(web::block(move || actions::get_all_users(&db))
|
||||||
.map(|users| HttpResponse::Ok().json(users))
|
.await
|
||||||
.map_err(|_| HttpResponse::InternalServerError())?)
|
.map(|users| HttpResponse::Ok().json(users))
|
||||||
|
.map_err(|_| HttpResponse::InternalServerError())?)
|
||||||
|
} else {
|
||||||
|
Err(ErrorUnauthorized("Cannot read other users"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// handler for `GET /users/{id}`
|
/// handler for `GET /users/{id}`
|
||||||
pub async fn get_user_by_id(db: web::Data<Pool>, user_id: web::Path<i32>) -> HttpResult {
|
pub async fn get_user_by_id(
|
||||||
Ok(web::block(move || actions::get_user_by_id(&db, *user_id))
|
db: web::Data<Pool>,
|
||||||
.await
|
user_id: web::Path<i32>,
|
||||||
.map(|user| user.into())
|
claims: Claims,
|
||||||
.map_err(|_| HttpResponse::InternalServerError())?)
|
) -> HttpResult {
|
||||||
|
if claims.uid == *user_id || claims.role >= Role::ReadAll {
|
||||||
|
Ok(web::block(move || actions::get_user_by_id(&db, *user_id))
|
||||||
|
.await
|
||||||
|
.map(|user| user.into())
|
||||||
|
.map_err(|_| HttpResponse::InternalServerError())?)
|
||||||
|
} else {
|
||||||
|
Err(ErrorUnauthorized("Cannot read other users"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// handler for `POST /users`
|
/// handler for `POST /users`
|
||||||
|
|
@ -52,9 +66,40 @@ pub async fn add_user(db: web::Data<Pool>, item: web::Json<InputUser>) -> HttpRe
|
||||||
}
|
}
|
||||||
|
|
||||||
/// handler for `DELETE /users/{id}`
|
/// handler for `DELETE /users/{id}`
|
||||||
pub async fn delete_user(db: web::Data<Pool>, user_id: web::Path<i32>) -> HttpResult {
|
pub async fn delete_user(
|
||||||
Ok(web::block(move || actions::delete_user(&db, *user_id))
|
db: web::Data<Pool>,
|
||||||
.await
|
user_id: web::Path<i32>,
|
||||||
.map(|count| HttpResponse::Ok().body(format!("Deleted {} user.", count)))
|
claims: Claims,
|
||||||
.map_err(|_| HttpResponse::InternalServerError())?)
|
) -> HttpResult {
|
||||||
|
if claims.uid == *user_id || claims.role >= Role::WriteAll {
|
||||||
|
Ok(web::block(move || actions::delete_user(&db, *user_id))
|
||||||
|
.await
|
||||||
|
.map(|count| HttpResponse::Ok().body(format!("Deleted {} user.", count)))
|
||||||
|
.map_err(|_| HttpResponse::InternalServerError())?)
|
||||||
|
} else {
|
||||||
|
Err(ErrorUnauthorized("Cannot delete other user"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
||||||
|
pub struct LoginData {
|
||||||
|
name: String,
|
||||||
|
password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn admin_login(credentials: Json<LoginData>) -> HttpResult {
|
||||||
|
if *credentials.password == *"hugo" && *credentials.name == *"hugo" {
|
||||||
|
Ok(HttpResponse::Ok().body(create_jwt_role(
|
||||||
|
&User {
|
||||||
|
id: 0,
|
||||||
|
first_name: "".to_string(),
|
||||||
|
last_name: "".to_string(),
|
||||||
|
email: "".to_string(),
|
||||||
|
created_at: chrono::Local::now().naive_local(),
|
||||||
|
},
|
||||||
|
Role::Admin,
|
||||||
|
)?))
|
||||||
|
} else {
|
||||||
|
Err(ErrorUnauthorized("Incorrect credentials"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
extern crate diesel;
|
extern crate diesel;
|
||||||
|
|
||||||
use crate::auth::validator;
|
use crate::auth::validator;
|
||||||
use actix_web::{web, App, Error, HttpServer};
|
use actix_web::{web, App, HttpServer};
|
||||||
use actix_web_httpauth::middleware::HttpAuthentication;
|
use actix_web_httpauth::middleware::HttpAuthentication;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel::r2d2::{self, ConnectionManager};
|
use diesel::r2d2::{self, ConnectionManager};
|
||||||
|
|
@ -32,6 +32,7 @@ async fn main() -> std::io::Result<()> {
|
||||||
App::new()
|
App::new()
|
||||||
.data(pool.clone())
|
.data(pool.clone())
|
||||||
.route("/users", web::post().to(handlers::add_user))
|
.route("/users", web::post().to(handlers::add_user))
|
||||||
|
.route("/admin", web::post().to(handlers::admin_login))
|
||||||
.service(
|
.service(
|
||||||
web::scope("/users")
|
web::scope("/users")
|
||||||
.wrap(auth_middleware)
|
.wrap(auth_middleware)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue