summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJokler <github@jokler.xyz>2026-10-05 16:07:20 +0200
committerJokler <github@jokler.xyz>2026-10-05 22:25:43 +0200
commit625ea9193202f678ab2799283ddfe2b282ba1e90 (patch)
treeccb98fdfca95b0787b730d5e9a3d3c9146eabf9a
parent0fed68e150b028b9ad62e75e0472a34224042501 (diff)
downloadjoklerpoints-625ea9193202f678ab2799283ddfe2b282ba1e90.tar.gz
joklerpoints-625ea9193202f678ab2799283ddfe2b282ba1e90.zip
Add user creation
-rw-r--r--src/admin.rs77
-rw-r--r--src/admin/user_creation.rs42
-rw-r--r--src/main.rs10
-rw-r--r--src/user.rs14
-rw-r--r--templates/admin.htm26
5 files changed, 142 insertions, 27 deletions
diff --git a/src/admin.rs b/src/admin.rs
index 2efe02f..912d0a3 100644
--- a/src/admin.rs
+++ b/src/admin.rs
@@ -5,21 +5,32 @@ use actix_http::error::ResponseError;
use actix_identity::Identity;
use actix_web::{http::header, web, HttpResponse};
use askama::Template;
-use serde::Deserialize;
+use serde::{Serialize, Deserialize};
use crate::error::ServiceError;
use crate::model::{DbExecutor, NewUser};
+use crate::user::LoginData;
use crate::util::UserRequest;
use crate::Secret;
pub mod user_creation;
-use user_creation::{PassHashExecutor, PassHashRequest, SaveUserRequest};
+use user_creation::{ChangePasswordRequest, PassHashExecutor, PassHashRequest, SaveUserRequest};
#[derive(Template)]
#[template(path = "admin.htm")]
-struct AdminTemplate;
+struct AdminTemplate {
+ success: OperationResult,
+}
+
+#[derive(Serialize, Deserialize, Debug, PartialEq)]
+#[serde(rename_all = "kebab-case")]
+pub enum OperationResult {
+ ChangePassword,
+ CreateUser,
+ Empty,
+}
-pub async fn index(ident: Identity, db: web::Data<Addr<DbExecutor>>) -> HttpResponse {
+pub async fn handle_index(ident: Identity, db: web::Data<Addr<DbExecutor>>) -> HttpResponse {
if let Some(name) = ident.identity() {
ident.remember(name.clone());
let user = match db.send(UserRequest(name.clone())).await.unwrap() {
@@ -35,7 +46,11 @@ pub async fn index(ident: Identity, db: web::Data<Addr<DbExecutor>>) -> HttpResp
return ServiceError::Unauthorized.error_response();
}
- let page = AdminTemplate.render().unwrap();
+ let page = AdminTemplate {
+ success: OperationResult::Empty,
+ }
+ .render()
+ .unwrap();
HttpResponse::Ok().content_type("text/html").body(page)
} else {
@@ -53,7 +68,7 @@ pub struct UserData {
pub password: String,
}
-pub async fn create_user(
+pub async fn handle_create_user(
ident: Identity,
params: web::Form<UserData>,
db: web::Data<Addr<DbExecutor>>,
@@ -95,7 +110,7 @@ pub async fn create_user(
name: new_login.user,
// TODO Let the admin pick
power_level: 0,
- balance: balance,
+ balance,
};
db.send(SaveUserRequest::new(new_user, hash))
@@ -112,3 +127,51 @@ pub async fn create_user(
.finish()
}
}
+
+pub async fn handle_change_password(
+ ident: Identity,
+ params: web::Form<LoginData>,
+ db: web::Data<Addr<DbExecutor>>,
+ hasher: web::Data<Addr<PassHashExecutor>>,
+ sec: web::Data<Secret>,
+) -> HttpResponse {
+ if let Some(name) = ident.identity() {
+ ident.remember(name.clone());
+ let user = db.send(UserRequest(name.clone())).await.unwrap().unwrap();
+ if user.power_level != 9001 {
+ return ServiceError::Unauthorized.error_response();
+ }
+
+ change_password(params.into_inner(), &db, &hasher, sec.0.clone()).await;
+
+ let page = AdminTemplate {
+ success: OperationResult::ChangePassword,
+ }
+ .render()
+ .unwrap();
+
+ HttpResponse::Ok().content_type("text/html").body(page)
+ } else {
+ HttpResponse::Found()
+ .header(header::LOCATION, "/user/login")
+ .finish()
+ }
+}
+
+pub async fn change_password(
+ login: LoginData,
+ db: &Addr<DbExecutor>,
+ hasher: &Addr<PassHashExecutor>,
+ secret: String,
+) {
+ let hash = hasher
+ .send(PassHashRequest::new(login.password, secret))
+ .await
+ .unwrap()
+ .unwrap();
+
+ db.send(ChangePasswordRequest(login.user, hash))
+ .await
+ .unwrap()
+ .unwrap();
+}
diff --git a/src/admin/user_creation.rs b/src/admin/user_creation.rs
index 563b660..d54b063 100644
--- a/src/admin/user_creation.rs
+++ b/src/admin/user_creation.rs
@@ -4,15 +4,15 @@ use diesel::MysqlConnection;
use diesel::{
dsl::sql,
types::{Bigint, Unsigned},
- RunQueryDsl,
+ ExpressionMethods, QueryDsl, RunQueryDsl,
};
use crate::error::ServiceError;
-use crate::model::{DbExecutor, NewPassword, NewUser};
-use crate::schema::passwords::dsl::passwords;
-use crate::schema::users::dsl::users;
+use crate::model::{DbExecutor, NewPassword, NewUser, User};
+use crate::schema::passwords::{columns as password_columns, dsl::passwords};
+use crate::schema::users::{columns as user_columns, dsl::users};
-static LAST_ID_SQL: &'static str = "SELECT LAST_INSERT_ID()";
+static LAST_ID_SQL: &str = "SELECT LAST_INSERT_ID()";
pub struct PassHashExecutor(pub Hasher<'static>);
@@ -74,7 +74,7 @@ impl Handler<SaveUserRequest> for DbExecutor {
let id = sql::<Unsigned<Bigint>>(LAST_ID_SQL).get_result(conn)?;
let new_pass = NewPassword {
- id: id,
+ id,
hash: &msg.pass_hash,
};
@@ -85,3 +85,33 @@ impl Handler<SaveUserRequest> for DbExecutor {
Ok(())
}
}
+
+pub struct ChangePasswordRequest(pub String, pub String);
+
+impl Message for ChangePasswordRequest {
+ type Result = Result<(), ServiceError>;
+}
+
+impl Handler<ChangePasswordRequest> for DbExecutor {
+ type Result = Result<(), ServiceError>;
+
+ fn handle(&mut self, req: ChangePasswordRequest, _: &mut Self::Context) -> Self::Result {
+ let conn: &MysqlConnection = &self.0.get().unwrap();
+ let (name, hash) = (req.0, req.1);
+
+ let user = users
+ .filter(user_columns::name.eq(name))
+ .first::<User>(conn)
+ .map_err(|_| ServiceError::NotFound)?;
+
+ if user.power_level == 9001 {
+ return Err(ServiceError::Unauthorized);
+ }
+
+ diesel::update(passwords.filter(password_columns::id.eq(user.id)))
+ .set(password_columns::hash.eq(hash))
+ .execute(conn)?;
+
+ Ok(())
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index 0f39622..c5de543 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -100,10 +100,14 @@ async fn main() -> std::io::Result<()> {
.route("/settings", web::get().to(user::settings))
.route("/reset-password", web::post().to(user::reset_password))
.route("/logout", web::get().to(user::logout))
+ .route("/admin", web::get().to(admin::handle_index))
.service(
- web::resource("/admin")
- .route(web::get().to(admin::index))
- .route(web::post().to(admin::create_user)),
+ web::scope("/admin")
+ .route("/create-user", web::post().to(admin::handle_create_user))
+ .route(
+ "/change-password",
+ web::post().to(admin::handle_change_password),
+ ),
)
.service(actix_files::Files::new("/static", "static/"))
})
diff --git a/src/user.rs b/src/user.rs
index 8cb2c5f..6ef4652 100644
--- a/src/user.rs
+++ b/src/user.rs
@@ -254,7 +254,7 @@ pub async fn confirm_transfer(
header::LOCATION,
format!(
"/transfer?{}",
- serde_urlencoded::to_string(query).unwrap_or(String::with_capacity(0))
+ serde_urlencoded::to_string(query).unwrap_or_else(|_| String::with_capacity(0))
),
)
.finish();
@@ -272,7 +272,7 @@ pub async fn confirm_transfer(
header::LOCATION,
format!(
"/transfer?{}",
- serde_urlencoded::to_string(query).unwrap_or(String::with_capacity(0))
+ serde_urlencoded::to_string(query).unwrap_or_else(|_| String::with_capacity(0))
),
)
.finish();
@@ -297,7 +297,7 @@ pub async fn confirm_transfer(
header::LOCATION,
format!(
"/transfer?{}",
- serde_urlencoded::to_string(query).unwrap_or(String::with_capacity(0))
+ serde_urlencoded::to_string(query).unwrap_or_else(|_| String::with_capacity(0))
),
)
.finish();
@@ -343,7 +343,7 @@ impl Default for PasswordResult {
pub async fn settings(ident: Identity, params: web::Query<PasswordQuery>) -> HttpResponse {
if let Some(name) = ident.identity() {
- ident.remember(name.clone());
+ ident.remember(name);
let query = params.into_inner();
HttpResponse::Ok().content_type("text/html").body(
@@ -398,7 +398,7 @@ pub async fn reset_password(
header::LOCATION,
format!(
"/settings?{}",
- serde_urlencoded::to_string(query).unwrap_or(String::with_capacity(0))
+ serde_urlencoded::to_string(query).unwrap_or_else(|_| String::with_capacity(0))
),
)
.finish();
@@ -422,7 +422,7 @@ pub async fn reset_password(
header::LOCATION,
format!(
"/settings?{}",
- serde_urlencoded::to_string(query).unwrap_or(String::with_capacity(0))
+ serde_urlencoded::to_string(query).unwrap_or_else(|_| String::with_capacity(0))
),
)
.finish();
@@ -448,7 +448,7 @@ pub async fn reset_password(
header::LOCATION,
format!(
"/settings?{}",
- serde_urlencoded::to_string(query).unwrap_or(String::with_capacity(0))
+ serde_urlencoded::to_string(query).unwrap_or_else(|_| String::with_capacity(0))
),
)
.finish()
diff --git a/templates/admin.htm b/templates/admin.htm
index db4aa03..7ca8ee9 100644
--- a/templates/admin.htm
+++ b/templates/admin.htm
@@ -3,19 +3,37 @@
{% block title %}Administration{% endblock %}
{% block content %}
-<h1>User Creation</h1>
-<form class="fixed-form" action="/admin" method="POST">
+<h1>Administration</h1>
+<h2>User Creation</h2>
+{% if success == OperationResult::CreateUser -%}
+ <div class="success">Successfully added</div>
+{% endif -%}
+<form action="/admin/create-user" method="POST">
<label for="user">Username</label>
<input type="text" placeholder="Enter Username" name="user" required>
<label for="password">Password</label>
<input type="password" placeholder="Enter Password" name="password" required>
- <label for="user">Balance</label>
- <input type="text" placeholder="Enter Balance" name="balance" required>
+ <label for="balance">Balance</label>
+ <input type="balance" placeholder="Enter Balance" name="balance" required>
<button type="submit">Create User</button>
</form>
+
+<h2>Change User Password</h2>
+{% if success == OperationResult::ChangePassword -%}
+ <div class="success">Successfully changed</div>
+{% endif -%}
+<form action="/admin/change-password" method="POST">
+ <label for="user">Username</label>
+ <input type="text" placeholder="Enter Username" name="user" required>
+
+ <label for="password">New Password</label>
+ <input type="password" placeholder="Enter Password" name="password" required>
+
+ <button type="submit">Change Password</button>
+</form>
{% endblock %}
<!-- vim: expandtab sw=2 ts=2