Test user authentication
This commit is contained in:
parent
a33b94e5b3
commit
a0f1a0b81c
@ -35,3 +35,4 @@ CREATE TABLE roles(
|
|||||||
FOREIGN KEY(game_id) REFERENCES games(uuid)
|
FOREIGN KEY(game_id) REFERENCES games(uuid)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
INSERT INTO users VALUES ('admin', 'admin', '', true, true);
|
||||||
|
@ -11,7 +11,7 @@ use uuid::Uuid;
|
|||||||
use crate::{
|
use crate::{
|
||||||
asset_db::{self, AssetId, Assets},
|
asset_db::{self, AssetId, Assets},
|
||||||
database::{CharacterId, Database, SessionId, UserId},
|
database::{CharacterId, Database, SessionId, UserId},
|
||||||
types::{AppError, FatalError, Game, Message, Tabletop, User, Rgb},
|
types::{AppError, FatalError, Game, Message, Rgb, Tabletop, User},
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_BACKGROUND_COLOR: Rgb = Rgb {
|
const DEFAULT_BACKGROUND_COLOR: Rgb = Rgb {
|
||||||
@ -154,9 +154,7 @@ impl Core {
|
|||||||
asset_db::Error::Inaccessible => {
|
asset_db::Error::Inaccessible => {
|
||||||
AppError::Inaccessible(format!("{}", asset_id))
|
AppError::Inaccessible(format!("{}", asset_id))
|
||||||
}
|
}
|
||||||
asset_db::Error::Unexpected(err) => {
|
asset_db::Error::Unexpected(err) => AppError::Inaccessible(format!("{}", err)),
|
||||||
AppError::Inaccessible(format!("{}", err))
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -212,6 +210,25 @@ impl Core {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn save_user(
|
||||||
|
&self,
|
||||||
|
uuid: Option<UserId>,
|
||||||
|
username: &str,
|
||||||
|
password: &str,
|
||||||
|
admin: bool,
|
||||||
|
enabled: bool,
|
||||||
|
) -> ResultExt<UserId, AppError, FatalError> {
|
||||||
|
let state = self.0.read().await;
|
||||||
|
match state
|
||||||
|
.db
|
||||||
|
.save_user(uuid, username, password, admin, enabled)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(uuid) => ok(uuid),
|
||||||
|
Err(err) => fatal(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn set_password(
|
pub async fn set_password(
|
||||||
&self,
|
&self,
|
||||||
uuid: UserId,
|
uuid: UserId,
|
||||||
@ -290,7 +307,7 @@ mod test {
|
|||||||
]);
|
]);
|
||||||
let memory_db: Option<PathBuf> = None;
|
let memory_db: Option<PathBuf> = None;
|
||||||
let conn = DbConn::new(memory_db);
|
let conn = DbConn::new(memory_db);
|
||||||
conn.save_user(None, "admin", "aoeu", true, true)
|
conn.save_user(Some(UserId::from("admin")), "admin", "aoeu", true, true)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
conn.save_user(None, "gm_1", "aoeu", false, true)
|
conn.save_user(None, "gm_1", "aoeu", false, true)
|
||||||
|
@ -100,7 +100,7 @@ impl DiskDb {
|
|||||||
) -> Result<UserId, FatalError> {
|
) -> Result<UserId, FatalError> {
|
||||||
match user_id {
|
match user_id {
|
||||||
None => {
|
None => {
|
||||||
let user_id = UserId::new();
|
let user_id = UserId::default();
|
||||||
let mut stmt = self
|
let mut stmt = self
|
||||||
.conn
|
.conn
|
||||||
.prepare("INSERT INTO users VALUES (?, ?, ?, ?, ?)")
|
.prepare("INSERT INTO users VALUES (?, ?, ?, ?, ?)")
|
||||||
|
@ -173,7 +173,7 @@ mod test {
|
|||||||
let no_path: Option<PathBuf> = None;
|
let no_path: Option<PathBuf> = None;
|
||||||
let db = DiskDb::new(no_path).unwrap();
|
let db = DiskDb::new(no_path).unwrap();
|
||||||
|
|
||||||
db.save_user(None, "admin", "abcdefg", true, true).unwrap();
|
db.save_user(Some(UserId::from("admin")), "admin", "abcdefg", true, true).unwrap();
|
||||||
let game_id = db.save_game(None, "Candela").unwrap();
|
let game_id = db.save_game(None, "Candela").unwrap();
|
||||||
(db, game_id)
|
(db, game_id)
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,17 @@ use uuid::Uuid;
|
|||||||
pub struct UserId(String);
|
pub struct UserId(String);
|
||||||
|
|
||||||
impl UserId {
|
impl UserId {
|
||||||
pub fn new() -> Self {
|
|
||||||
Self(format!("{}", Uuid::new_v4().hyphenated()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_str(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for UserId {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(format!("{}", Uuid::new_v4().hyphenated()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&str> for UserId {
|
impl From<&str> for UserId {
|
||||||
fn from(s: &str) -> Self {
|
fn from(s: &str) -> Self {
|
||||||
Self(s.to_owned())
|
Self(s.to_owned())
|
||||||
|
@ -32,8 +32,8 @@ pub async fn healthcheck(core: Core) -> Vec<u8> {
|
|||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub struct AuthRequest {
|
pub struct AuthRequest {
|
||||||
username: String,
|
pub username: String,
|
||||||
password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn check_password(core: Core, req: Json<AuthRequest>) -> (StatusCode, Json<Option<SessionId>>) {
|
pub async fn check_password(core: Core, req: Json<AuthRequest>) -> (StatusCode, Json<Option<SessionId>>) {
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
use axum::{routing::{get, post}, Json, Router};
|
use axum::{
|
||||||
|
routing::{get, post},
|
||||||
|
Json, Router,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::Core,
|
core::Core,
|
||||||
@ -27,12 +30,19 @@ pub fn routes(core: Core) -> Router {
|
|||||||
mod test {
|
mod test {
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use axum::http::StatusCode;
|
||||||
use axum_test::TestServer;
|
use axum_test::TestServer;
|
||||||
|
use cool_asserts::assert_matches;
|
||||||
|
use result_extended::ResultExt;
|
||||||
|
|
||||||
use crate::{asset_db::FsAssets, core::Core, database::DbConn};
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::{
|
||||||
|
asset_db::FsAssets,
|
||||||
|
core::Core,
|
||||||
|
database::{Database, DbConn, SessionId, UserId},
|
||||||
|
};
|
||||||
|
|
||||||
fn setup() -> (Core, TestServer) {
|
fn setup_without_admin() -> (Core, TestServer) {
|
||||||
let memory_db: Option<PathBuf> = None;
|
let memory_db: Option<PathBuf> = None;
|
||||||
let conn = DbConn::new(memory_db);
|
let conn = DbConn::new(memory_db);
|
||||||
let core = Core::new(FsAssets::new(PathBuf::from("/home/savanni/Pictures")), conn);
|
let core = Core::new(FsAssets::new(PathBuf::from("/home/savanni/Pictures")), conn);
|
||||||
@ -41,14 +51,75 @@ mod test {
|
|||||||
(core, server)
|
(core, server)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn setup_admin_enabled() -> (Core, TestServer) {
|
||||||
|
let memory_db: Option<PathBuf> = None;
|
||||||
|
let conn = DbConn::new(memory_db);
|
||||||
|
conn.save_user(Some(UserId::from("admin")), "admin", "aoeu", true, true)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let core = Core::new(FsAssets::new(PathBuf::from("/home/savanni/Pictures")), conn);
|
||||||
|
let app = routes(core.clone());
|
||||||
|
let server = TestServer::new(app).unwrap();
|
||||||
|
(core, server)
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn it_returns_a_healthcheck() {
|
async fn it_returns_a_healthcheck() {
|
||||||
let (_core, server) = setup();
|
let (core, server) = setup_without_admin();
|
||||||
|
|
||||||
let response = server.get("/api/v1/health").await;
|
let response = server.get("/api/v1/health").await;
|
||||||
response.assert_status_ok();
|
response.assert_status_ok();
|
||||||
|
|
||||||
let b: crate::handlers::HealthCheck = response.json();
|
let b: crate::handlers::HealthCheck = response.json();
|
||||||
|
|
||||||
assert_eq!(b, crate::handlers::HealthCheck { ok: false });
|
assert_eq!(b, crate::handlers::HealthCheck { ok: false });
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
core.save_user(Some(UserId::from("admin")), "admin", "aoeu", true, true)
|
||||||
|
.await,
|
||||||
|
ResultExt::Ok(_)
|
||||||
|
);
|
||||||
|
|
||||||
|
let response = server.get("/api/v1/health").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
let b: crate::handlers::HealthCheck = response.json();
|
||||||
|
assert_eq!(b, crate::handlers::HealthCheck { ok: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn it_authenticates_a_user() {
|
||||||
|
let (_core, server) = setup_admin_enabled().await;
|
||||||
|
|
||||||
|
let response = server
|
||||||
|
.post("/api/v1/auth")
|
||||||
|
.json(&AuthRequest {
|
||||||
|
username: "admin".to_owned(),
|
||||||
|
password: "wrong".to_owned(),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
response.assert_status(StatusCode::UNAUTHORIZED);
|
||||||
|
|
||||||
|
let response = server
|
||||||
|
.post("/api/v1/auth")
|
||||||
|
.json(&AuthRequest {
|
||||||
|
username: "unknown".to_owned(),
|
||||||
|
password: "wrong".to_owned(),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
response.assert_status(StatusCode::UNAUTHORIZED);
|
||||||
|
|
||||||
|
let response = server
|
||||||
|
.post("/api/v1/auth")
|
||||||
|
.json(&AuthRequest {
|
||||||
|
username: "admin".to_owned(),
|
||||||
|
password: "aoeu".to_owned(),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
let session_id: Option<SessionId> = response.json();
|
||||||
|
assert!(session_id.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn it_returns_user_profile() {
|
||||||
|
unimplemented!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user