This commit is contained in:
nora 2021-07-19 14:26:51 +02:00
parent dfc6771135
commit e8419c3a14
7 changed files with 205 additions and 205 deletions

View file

@ -1,2 +1,2 @@
# nein # nein
zu windows zu windows

View file

@ -1,3 +1,3 @@
-- This file should undo anything in `up.sql` -- This file should undo anything in `up.sql`
DROP TABLE users; DROP TABLE users;

View file

@ -1,10 +1,10 @@
-- Your SQL goes here -- Your SQL goes here
CREATE TABLE users CREATE TABLE users
( (
id SERIAL NOT NULL PRIMARY KEY, id SERIAL NOT NULL PRIMARY KEY,
first_name TEXT NOT NULL, first_name TEXT NOT NULL,
last_name TEXT NOT NULL, last_name TEXT NOT NULL,
email TEXT NOT NULL, email TEXT NOT NULL,
created_at TIMESTAMP NOT NULL created_at TIMESTAMP NOT NULL
); );

View file

@ -1,39 +1,39 @@
use super::models::{NewUser, User}; use super::models::{NewUser, User};
use crate::diesel::QueryDsl; use crate::diesel::QueryDsl;
use crate::diesel::RunQueryDsl; use crate::diesel::RunQueryDsl;
use crate::handlers::InputUser; use crate::handlers::InputUser;
use crate::Pool; use crate::Pool;
use diesel::{delete, insert_into}; use diesel::{delete, insert_into};
type DbResult<T> = Result<T, diesel::result::Error>; type DbResult<T> = Result<T, diesel::result::Error>;
pub fn get_all_users(db: &Pool) -> DbResult<Vec<User>> { pub fn get_all_users(db: &Pool) -> DbResult<Vec<User>> {
use super::schema::users::dsl::*; use super::schema::users::dsl::*;
let conn = db.get().unwrap(); let conn = db.get().unwrap();
users.load::<User>(&conn) users.load::<User>(&conn)
} }
pub fn get_user_by_id(db: &Pool, user_id: i32) -> DbResult<User> { pub fn get_user_by_id(db: &Pool, user_id: i32) -> DbResult<User> {
use super::schema::users::dsl::*; use super::schema::users::dsl::*;
let conn = db.get().unwrap(); let conn = db.get().unwrap();
users.find(user_id).get_result::<User>(&conn) users.find(user_id).get_result::<User>(&conn)
} }
pub fn add_user(db: &Pool, user: InputUser) -> DbResult<User> { pub fn add_user(db: &Pool, user: InputUser) -> DbResult<User> {
use super::schema::users::dsl::*; use super::schema::users::dsl::*;
let conn = db.get().unwrap(); let conn = db.get().unwrap();
let new_user = NewUser { let new_user = NewUser {
first_name: &user.first_name, first_name: &user.first_name,
last_name: &user.last_name, last_name: &user.last_name,
email: &user.email, email: &user.email,
created_at: chrono::Local::now().naive_local(), created_at: chrono::Local::now().naive_local(),
}; };
insert_into(users).values(&new_user).get_result(&conn) insert_into(users).values(&new_user).get_result(&conn)
} }
pub fn delete_user(db: &Pool, user_id: i32) -> DbResult<usize> { pub fn delete_user(db: &Pool, user_id: i32) -> DbResult<usize> {
use super::schema::users::dsl::*; use super::schema::users::dsl::*;
let conn = db.get().unwrap(); let conn = db.get().unwrap();
delete(users.find(user_id)).execute(&conn) delete(users.find(user_id)).execute(&conn)
} }

View file

@ -1,86 +1,86 @@
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::error::ErrorUnauthorized; use actix_web::error::ErrorUnauthorized;
use actix_web::http::header::Header; use actix_web::http::header::Header;
use actix_web::{FromRequest, HttpMessage, HttpRequest}; use actix_web::{FromRequest, HttpMessage, HttpRequest};
use actix_web_httpauth::extractors::bearer::BearerAuth; use actix_web_httpauth::extractors::bearer::BearerAuth;
use actix_web_httpauth::headers::authorization; use actix_web_httpauth::headers::authorization;
use actix_web_httpauth::headers::authorization::Bearer; use actix_web_httpauth::headers::authorization::Bearer;
use chrono::Utc; use chrono::Utc;
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Validation}; use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Validation};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::future; use std::future;
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub enum Role { pub enum Role {
None = 0, None = 0,
ReadAll = 1, ReadAll = 1,
WriteAll = 2, WriteAll = 2,
Admin = 3, Admin = 3,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Claims { pub struct Claims {
pub exp: usize, pub exp: usize,
pub uid: i32, pub uid: i32,
pub role: Role, pub role: Role,
} }
impl FromRequest for Claims { impl FromRequest for Claims {
type Error = actix_web::Error; type Error = actix_web::Error;
type Future = std::future::Ready<Result<Self, Self::Error>>; type Future = std::future::Ready<Result<Self, Self::Error>>;
type Config = (); type Config = ();
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
future::ready(match authorization::Authorization::<Bearer>::parse(req) { future::ready(match authorization::Authorization::<Bearer>::parse(req) {
Ok(auth) => validate_token(auth.into_scheme().token()), Ok(auth) => validate_token(auth.into_scheme().token()),
Err(_) => Err(ErrorUnauthorized("No Bearer token present")), Err(_) => Err(ErrorUnauthorized("No Bearer token present")),
}) })
} }
} }
fn validate_token(token: &str) -> Result<Claims, actix_web::Error> { fn validate_token(token: &str) -> Result<Claims, actix_web::Error> {
let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET env var"); let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET env var");
let decoded = jsonwebtoken::decode::<Claims>( let decoded = jsonwebtoken::decode::<Claims>(
&token, &token,
&DecodingKey::from_secret(secret.as_bytes()), &DecodingKey::from_secret(secret.as_bytes()),
&Validation::new(Algorithm::HS512), &Validation::new(Algorithm::HS512),
) )
.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.into()) Err(ServiceError::TokenExpiredError.into())
} else { } else {
Ok(decoded) Ok(decoded)
} }
} }
pub fn create_jwt(user: &User) -> Result<String, ServiceError> { pub fn create_jwt(user: &User) -> Result<String, ServiceError> {
create_jwt_role(user, Role::ReadAll) create_jwt_role(user, Role::ReadAll)
} }
pub fn create_jwt_role(user: &User, role: Role) -> Result<String, ServiceError> { 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")
.timestamp(); .timestamp();
let claims = Claims { let claims = Claims {
exp: expiration as usize, exp: expiration as usize,
uid: user.id, uid: user.id,
role, 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");
let header = jsonwebtoken::Header::new(Algorithm::HS512); let header = jsonwebtoken::Header::new(Algorithm::HS512);
jsonwebtoken::encode( jsonwebtoken::encode(
&header, &header,
&claims, &claims,
&EncodingKey::from_secret(secret.as_bytes()), &EncodingKey::from_secret(secret.as_bytes()),
) )
.map_err(|_| ServiceError::JWTCreationError) .map_err(|_| ServiceError::JWTCreationError)
} }

View file

@ -1,41 +1,41 @@
use actix_web::{error::ResponseError, HttpResponse}; use actix_web::{error::ResponseError, HttpResponse};
use derive_more::Display; use derive_more::Display;
#[derive(Debug, Display)] #[derive(Debug, Display)]
pub enum ServiceError { pub enum ServiceError {
#[display(fmt = "Internal Server Error")] #[display(fmt = "Internal Server Error")]
InternalServerError, InternalServerError,
#[display(fmt = "BadRequest: {}", _0)] #[display(fmt = "BadRequest: {}", _0)]
BadRequest(String), BadRequest(String),
#[display(fmt = "JWT Creation Error")] #[display(fmt = "JWT Creation Error")]
JWTCreationError, JWTCreationError,
#[display(fmt = "JWT Error")] #[display(fmt = "JWT Error")]
JWTokenError, JWTokenError,
#[display(fmt = "No Permission Error")] #[display(fmt = "No Permission Error")]
NoPermissionError, NoPermissionError,
#[display(fmt = "Token Expired Error")] #[display(fmt = "Token Expired Error")]
TokenExpiredError, TokenExpiredError,
} }
// impl ResponseError trait allows to convert our errors into http responses with appropriate data // impl ResponseError trait allows to convert our errors into http responses with appropriate data
impl ResponseError for ServiceError { impl ResponseError for ServiceError {
fn error_response(&self) -> HttpResponse { fn error_response(&self) -> HttpResponse {
match self { match self {
ServiceError::InternalServerError => { ServiceError::InternalServerError => {
HttpResponse::InternalServerError().json("Internal Server Error, Please try later") HttpResponse::InternalServerError().json("Internal Server Error, Please try later")
} }
ServiceError::BadRequest(ref message) => HttpResponse::BadRequest().json(message), ServiceError::BadRequest(ref message) => HttpResponse::BadRequest().json(message),
ServiceError::JWTCreationError => { ServiceError::JWTCreationError => {
HttpResponse::InternalServerError().json("Could not fetch JWKS") HttpResponse::InternalServerError().json("Could not fetch JWKS")
} }
ServiceError::JWTokenError => HttpResponse::BadRequest().json("Invalid JWT"), ServiceError::JWTokenError => HttpResponse::BadRequest().json("Invalid JWT"),
ServiceError::NoPermissionError => HttpResponse::Unauthorized().json("No permissions"), ServiceError::NoPermissionError => HttpResponse::Unauthorized().json("No permissions"),
ServiceError::TokenExpiredError => HttpResponse::Unauthorized().json("Token expired"), ServiceError::TokenExpiredError => HttpResponse::Unauthorized().json("Token expired"),
} }
} }
} }

View file

@ -1,27 +1,27 @@
use crate::schema::*; use crate::schema::*;
use actix_web::HttpResponse; use actix_web::HttpResponse;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, Queryable)] #[derive(Debug, Clone, Serialize, Deserialize, Queryable)]
pub struct User { pub struct User {
pub id: i32, pub id: i32,
pub first_name: String, pub first_name: String,
pub last_name: String, pub last_name: String,
pub email: String, pub email: String,
pub created_at: chrono::NaiveDateTime, pub created_at: chrono::NaiveDateTime,
} }
#[derive(Insertable, Debug)] #[derive(Insertable, Debug)]
#[table_name = "users"] #[table_name = "users"]
pub struct NewUser<'a> { pub struct NewUser<'a> {
pub first_name: &'a str, pub first_name: &'a str,
pub last_name: &'a str, pub last_name: &'a str,
pub email: &'a str, pub email: &'a str,
pub created_at: chrono::NaiveDateTime, pub created_at: chrono::NaiveDateTime,
} }
impl From<User> for HttpResponse { impl From<User> for HttpResponse {
fn from(user: User) -> Self { fn from(user: User) -> Self {
HttpResponse::Ok().json(user) HttpResponse::Ok().json(user)
} }
} }