Adds user authorization to the actix backend.
This commit is contained in:
parent
1c8110cdb0
commit
534e6867d8
26
backend-actix/Cargo.lock
generated
26
backend-actix/Cargo.lock
generated
@ -393,6 +393,7 @@ dependencies = [
|
|||||||
"js-sys",
|
"js-sys",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"serde",
|
||||||
"time 0.1.45",
|
"time 0.1.45",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"winapi",
|
"winapi",
|
||||||
@ -433,9 +434,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.5"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
|
checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@ -483,7 +484,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"scratch",
|
"scratch",
|
||||||
"syn 2.0.9",
|
"syn 2.0.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -500,7 +501,7 @@ checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.9",
|
"syn 2.0.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -524,6 +525,7 @@ checksum = "4391a22b19c916e50bec4d6140f29bdda3e3bb187223fe6e3ea0b6e4d1021c04"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
"chrono",
|
||||||
"diesel_derives",
|
"diesel_derives",
|
||||||
"itoa",
|
"itoa",
|
||||||
"pq-sys",
|
"pq-sys",
|
||||||
@ -767,9 +769,9 @@ checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.2"
|
version = "1.9.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
@ -1144,9 +1146,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.7.2"
|
version = "1.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c"
|
checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -1233,7 +1235,7 @@ checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.9",
|
"syn 2.0.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1341,9 +1343,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.9"
|
version = "2.0.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0da4a3c17e109f700685ec577c0f85efd9b19bcf15c913985f14dc1ac01775aa"
|
checksum = "5aad1363ed6d37b84299588d62d3a7d95b5a5c2d9aad5c85609fda12afaa1f40"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1376,7 +1378,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.9",
|
"syn 2.0.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -10,9 +10,9 @@ actix-web = "4"
|
|||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
uuid = { version = "1.3.0", features = ["serde", "v4"] }
|
uuid = { version = "1.3.0", features = ["serde", "v4"] }
|
||||||
diesel = { version = "2.0", features = ["postgres", "r2d2", "uuid"] }
|
diesel = { version = "2.0", features = ["postgres", "r2d2", "uuid", "chrono"] }
|
||||||
diesel-derive-enum = { version = "2.0", features = ["postgres"] }
|
diesel-derive-enum = { version = "2.0", features = ["postgres"] }
|
||||||
argon2 = "0.5"
|
argon2 = "0.5"
|
||||||
chrono = "0.4"
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
jsonwebtoken = "8.1"
|
jsonwebtoken = "8.1"
|
||||||
validator = { version = "0.16", features = ["derive"] }
|
validator = { version = "0.16", features = ["derive"] }
|
@ -5,7 +5,7 @@ use actix_web::HttpServer;
|
|||||||
use actix_web::App;
|
use actix_web::App;
|
||||||
use actix_web::web;
|
use actix_web::web;
|
||||||
use diesel::PgConnection;
|
use diesel::PgConnection;
|
||||||
use request::{login, register};
|
use request::{login, register, gamenights};
|
||||||
use diesel::r2d2::ConnectionManager;
|
use diesel::r2d2::ConnectionManager;
|
||||||
use diesel::r2d2::Pool;
|
use diesel::r2d2::Pool;
|
||||||
|
|
||||||
@ -28,6 +28,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.app_data(web::Data::new(pool.clone()))
|
.app_data(web::Data::new(pool.clone()))
|
||||||
.service(login)
|
.service(login)
|
||||||
.service(register)
|
.service(register)
|
||||||
|
.service(gamenights)
|
||||||
})
|
})
|
||||||
.bind(("::1", 8080))?
|
.bind(("::1", 8080))?
|
||||||
.run()
|
.run()
|
||||||
|
65
backend-actix/src/request/authorization.rs
Normal file
65
backend-actix/src/request/authorization.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
use std::future::{Ready, ready};
|
||||||
|
|
||||||
|
use actix_web::{FromRequest, http, HttpRequest, dev::Payload, web::Data};
|
||||||
|
use chrono::Utc;
|
||||||
|
use jsonwebtoken::{encode, Header, EncodingKey, decode, DecodingKey, Validation};
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::{schema::user::{User, get_user}, DbPool};
|
||||||
|
|
||||||
|
use super::error::ApiError;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Claims {
|
||||||
|
exp: i64,
|
||||||
|
uid: Uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_claims(req: &HttpRequest) -> Result<Claims, ApiError> {
|
||||||
|
let token = req.headers()
|
||||||
|
.get(http::header::AUTHORIZATION)
|
||||||
|
.map(|h| h.to_str().unwrap().split_at(7).1.to_string());
|
||||||
|
|
||||||
|
let token = token.ok_or(ApiError{
|
||||||
|
status: 400,
|
||||||
|
message: "JWT-token was not specified in the Authorization header as Bearer: token".to_string()
|
||||||
|
})?;
|
||||||
|
|
||||||
|
println!("{:?}", token);
|
||||||
|
|
||||||
|
let secret = "secret";
|
||||||
|
Ok(decode::<Claims>(token.as_str(), &DecodingKey::from_secret(secret.as_bytes()), &Validation::default())?.claims)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_token(user: &User) -> Result<String, ApiError> {
|
||||||
|
let claims = Claims {
|
||||||
|
exp: Utc::now().timestamp() + chrono::Duration::days(7).num_seconds(),
|
||||||
|
uid: user.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("{:?}", claims);
|
||||||
|
|
||||||
|
let secret = "secret";
|
||||||
|
Ok(encode(
|
||||||
|
&Header::default(),
|
||||||
|
&claims,
|
||||||
|
&EncodingKey::from_secret(secret.as_bytes()))?)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRequest for User {
|
||||||
|
type Error = ApiError;
|
||||||
|
type Future = Ready<Result<Self, Self::Error>>;
|
||||||
|
|
||||||
|
fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
|
||||||
|
ready(
|
||||||
|
(|| -> Result<User, ApiError>{
|
||||||
|
let pool = req.app_data::<Data<DbPool>>().expect("No database configured");
|
||||||
|
let mut conn = pool.get().expect("couldn't get db connection from pool");
|
||||||
|
let uid = get_claims(req)?.uid;
|
||||||
|
|
||||||
|
Ok(get_user(&mut conn, uid)?)
|
||||||
|
})()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
14
backend-actix/src/request/gamenight_handlers.rs
Normal file
14
backend-actix/src/request/gamenight_handlers.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
use actix_web::{get, web, Responder, http::header::ContentType, HttpResponse};
|
||||||
|
|
||||||
|
use crate::{DbPool, request::{error::ApiError, responses::GameNightResponse}, schema::{self, user::User}};
|
||||||
|
|
||||||
|
#[get("/gamenights")]
|
||||||
|
pub async fn gamenights(pool: web::Data<DbPool>, _user: User) -> Result<impl Responder, ApiError> {
|
||||||
|
let mut conn = pool.get().expect("couldn't get db connection from pool");
|
||||||
|
let gamenights = schema::gamenights(&mut conn)?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok()
|
||||||
|
.content_type(ContentType::json())
|
||||||
|
.body(serde_json::to_string(&GameNightResponse { gamenights })?)
|
||||||
|
)
|
||||||
|
}
|
@ -1,8 +1,11 @@
|
|||||||
|
|
||||||
mod requests;
|
mod requests;
|
||||||
mod responses;
|
mod responses;
|
||||||
mod handler;
|
mod user_handlers;
|
||||||
|
mod gamenight_handlers;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod authorization;
|
||||||
|
|
||||||
pub use handler::login;
|
pub use user_handlers::login;
|
||||||
pub use handler::register;
|
pub use user_handlers::register;
|
||||||
|
pub use gamenight_handlers::gamenights;
|
@ -1,6 +1,8 @@
|
|||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::schema::gamenight::Gamenight;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct LoginResponse {
|
pub struct LoginResponse {
|
||||||
pub login_result: bool,
|
pub login_result: bool,
|
||||||
@ -27,4 +29,9 @@ impl LoginResponse {
|
|||||||
jwt_token: None
|
jwt_token: None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct GameNightResponse {
|
||||||
|
pub gamenights: Vec::<Gamenight>
|
||||||
}
|
}
|
@ -1,18 +1,14 @@
|
|||||||
|
|
||||||
use actix_web::http::header::ContentType;
|
use actix_web::http::header::ContentType;
|
||||||
use actix_web::{web, post, HttpResponse, Responder};
|
use actix_web::{web, post, HttpResponse, Responder, get};
|
||||||
use chrono::Utc;
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
use uuid::Uuid;
|
|
||||||
use validator::ValidateArgs;
|
use validator::ValidateArgs;
|
||||||
use crate::DbPool;
|
use crate::DbPool;
|
||||||
use crate::request::requests::{Login, Register};
|
use crate::request::requests::{Login, Register};
|
||||||
use crate::request::error::ApiError;
|
use crate::request::error::ApiError;
|
||||||
use crate::request::responses::LoginResponse;
|
use crate::request::responses::LoginResponse;
|
||||||
use crate::schema::user::Role;
|
use crate::request::authorization::get_token;
|
||||||
use crate::schema::{self};
|
use crate::schema::{self};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use jsonwebtoken::{encode, EncodingKey, Header};
|
|
||||||
|
|
||||||
impl Into<schema::user::LoginUser> for Login {
|
impl Into<schema::user::LoginUser> for Login {
|
||||||
fn into(self) -> schema::user::LoginUser {
|
fn into(self) -> schema::user::LoginUser {
|
||||||
@ -33,14 +29,9 @@ impl Into<schema::user::Register> for Register {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
struct Claims {
|
|
||||||
exp: i64,
|
|
||||||
uid: Uuid,
|
|
||||||
role: Role,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/login")]
|
|
||||||
|
#[get("/token")]
|
||||||
pub async fn login(pool: web::Data<DbPool>, login_data: web::Json<Login>) -> Result<impl Responder, ApiError> {
|
pub async fn login(pool: web::Data<DbPool>, login_data: web::Json<Login>) -> Result<impl Responder, ApiError> {
|
||||||
let data = login_data.into_inner();
|
let data = login_data.into_inner();
|
||||||
|
|
||||||
@ -50,18 +41,7 @@ pub async fn login(pool: web::Data<DbPool>, login_data: web::Json<Login>) -> Res
|
|||||||
})
|
})
|
||||||
.await??
|
.await??
|
||||||
{
|
{
|
||||||
let my_claims = Claims {
|
let token = get_token(&user)?;
|
||||||
exp: Utc::now().timestamp() + chrono::Duration::days(7).num_seconds(),
|
|
||||||
uid: user.id,
|
|
||||||
role: user.role,
|
|
||||||
};
|
|
||||||
|
|
||||||
let secret = "secret";
|
|
||||||
let token = encode(
|
|
||||||
&Header::default(),
|
|
||||||
&my_claims,
|
|
||||||
&EncodingKey::from_secret(secret.as_bytes()))?;
|
|
||||||
|
|
||||||
LoginResponse::success(user.id, token)
|
LoginResponse::success(user.id, token)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -74,7 +54,7 @@ pub async fn login(pool: web::Data<DbPool>, login_data: web::Json<Login>) -> Res
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/register")]
|
#[post("/user")]
|
||||||
pub async fn register(pool: web::Data<DbPool>, register_data: web::Json<Register>) -> Result<impl Responder, ApiError> {
|
pub async fn register(pool: web::Data<DbPool>, register_data: web::Json<Register>) -> Result<impl Responder, ApiError> {
|
||||||
let data1 = register_data.clone();
|
let data1 = register_data.clone();
|
||||||
let data2 = register_data.clone();
|
let data2 = register_data.clone();
|
20
backend-actix/src/schema/gamenight.rs
Normal file
20
backend-actix/src/schema/gamenight.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use diesel::{Insertable, Queryable, PgConnection, RunQueryDsl};
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
use crate::schema::schema::gamenight;
|
||||||
|
|
||||||
|
use super::error::DatabaseError;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Insertable, Queryable)]
|
||||||
|
#[diesel(table_name = gamenight)]
|
||||||
|
pub struct Gamenight {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub datetime: DateTime<Utc>,
|
||||||
|
pub owner_id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gamenights(conn: &mut PgConnection, ) -> Result<Vec::<Gamenight>, DatabaseError> {
|
||||||
|
Ok(gamenight::table.load::<Gamenight>(conn)?)
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
|
pub mod gamenight;
|
||||||
|
|
||||||
pub use user::login;
|
pub use user::login;
|
||||||
pub use user::register;
|
pub use user::register;
|
||||||
|
pub use gamenight::gamenights;
|
||||||
|
@ -85,6 +85,10 @@ pub fn login(conn: &mut PgConnection, user: LoginUser) -> Result<Option<User>, D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_user(conn: &mut PgConnection, id: Uuid) -> Result<User, DatabaseError> {
|
||||||
|
Ok(users::table.find(id).first(conn)?)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn unique_username(username: &String, conn: &mut PgConnection) -> Result<(), ValidationError> {
|
pub fn unique_username(username: &String, conn: &mut PgConnection) -> Result<(), ValidationError> {
|
||||||
match users::table
|
match users::table
|
||||||
.count()
|
.count()
|
||||||
|
Loading…
Reference in New Issue
Block a user