1
0
mirror of https://github.com/ellmau/adf-obdd.git synced 2025-12-20 09:39:38 +01:00

Add API for getting and updating user

This commit is contained in:
monsterkrampe 2023-03-31 15:07:54 +02:00
parent e562631f1c
commit 565683721d
No known key found for this signature in database
GPG Key ID: B8ADC1F5A5CE5057
2 changed files with 139 additions and 4 deletions

View File

@ -19,7 +19,9 @@ mod user;
use adf::{add_adf_problem, solve_adf_problem};
use config::{AppState, ASSET_DIRECTORY, COOKIE_DURATION};
use user::{create_username_index, delete_account, login, logout, register};
use user::{
create_username_index, delete_account, login, logout, register, update_user, user_info,
};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
@ -73,7 +75,9 @@ async fn main() -> std::io::Result<()> {
.service(register)
.service(delete_account)
.service(login)
.service(logout),
.service(logout)
.service(user_info)
.service(update_user),
)
.service(
web::scope("/adf")

View File

@ -1,9 +1,9 @@
use actix_identity::Identity;
use actix_web::{delete, post, web, HttpMessage, HttpRequest, HttpResponse, Responder};
use actix_web::{delete, get, post, put, web, HttpMessage, HttpRequest, HttpResponse, Responder};
use argon2::password_hash::rand_core::OsRng;
use argon2::password_hash::SaltString;
use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
use mongodb::results::DeleteResult;
use mongodb::results::{DeleteResult, UpdateResult};
use mongodb::{bson::doc, options::IndexOptions, Client, IndexModel};
use serde::{Deserialize, Serialize};
@ -22,6 +22,12 @@ struct UserPayload {
password: String,
}
#[derive(Deserialize, Serialize)]
struct UserInfo {
username: String,
temp: bool,
}
// Creates an index on the "username" field to force the values to be unique.
pub(crate) async fn create_username_index(client: &Client) {
let options = IndexOptions::builder().unique(true).build();
@ -215,3 +221,128 @@ async fn logout(app_state: web::Data<AppState>, id: Option<Identity>) -> impl Re
},
}
}
// Get current user
#[get("/info")]
async fn user_info(app_state: web::Data<AppState>, identity: Option<Identity>) -> impl Responder {
let user_coll: mongodb::Collection<User> = app_state
.mongodb_client
.database(DB_NAME)
.collection(USER_COLL);
match identity.map(|id| id.id()) {
None => {
HttpResponse::Unauthorized().body("You need to login get your account information.")
}
Some(Err(err)) => HttpResponse::InternalServerError().body(err.to_string()),
Some(Ok(username)) => {
match user_coll
.find_one(doc! { "username": &username }, None)
.await
{
Ok(Some(user)) => {
let info = UserInfo {
username: user.username,
temp: user.password.is_none(),
};
HttpResponse::Ok().json(info)
}
Ok(None) => HttpResponse::NotFound().body("Logged in user does not exist anymore."),
Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
}
}
}
}
// Update current user
#[put("/update")]
async fn update_user(
req: HttpRequest,
app_state: web::Data<AppState>,
identity: Option<Identity>,
user: web::Json<UserPayload>,
) -> impl Responder {
let mut user: UserPayload = user.into_inner();
let user_coll = app_state
.mongodb_client
.database(DB_NAME)
.collection(USER_COLL);
let adf_coll: mongodb::Collection<AdfProblem> = app_state
.mongodb_client
.database(DB_NAME)
.collection(ADF_COLL);
match identity {
None => {
HttpResponse::Unauthorized().body("You need to login get your account information.")
}
Some(id) => match id.id() {
Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
Ok(username) => {
if &user.username != &username && username_exists(&user_coll, &user.username).await
{
return HttpResponse::Conflict()
.body("Username is already taken. Please pick another one!");
}
let pw = &user.password;
let salt = SaltString::generate(&mut OsRng);
let hashed_pw = Argon2::default()
.hash_password(pw.as_bytes(), &salt)
.expect("Error while hashing password!")
.to_string();
user.password = hashed_pw;
let result = user_coll
.replace_one(
doc! { "username": &username },
User {
username: user.username.clone(),
password: Some(user.password),
},
None,
)
.await;
match result {
Ok(UpdateResult {
modified_count: 0, ..
}) => HttpResponse::InternalServerError().body("Account could not be updated."),
Ok(UpdateResult {
modified_count: 1, ..
}) => {
// re-login with new username
Identity::login(&req.extensions(), user.username.clone()).unwrap();
// update all adf problems of user
match adf_coll
.update_many(
doc! { "username": &username },
doc! { "$set": { "username": &user.username } },
None,
)
.await
{
Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
Ok(UpdateResult {
modified_count: 0, ..
}) => HttpResponse::InternalServerError()
.body("Account could not be updated."),
Ok(UpdateResult {
modified_count: _, ..
}) => HttpResponse::Ok().json(UserInfo {
username: user.username,
temp: false,
}),
}
}
Ok(_) => unreachable!(
"replace_one replaces at most one entry so all cases are covered already"
),
Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
}
}
},
}
}