feat: basic authentication.
Gotta improve it, but right now this can ping. Theoretically any client should be able to connect.
This commit is contained in:
parent
667fcca4e9
commit
67244e63b3
7 changed files with 110 additions and 7 deletions
|
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "SELECT * FROM users WHERE name = ?",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"ordinal": 1,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "password",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "is_admin",
|
||||||
|
"ordinal": 3,
|
||||||
|
"type_info": "Bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 1
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "fbf4d83d9836cf85d01059e679bcbf7dc463eea0579afacc0267f0f8c33640d3"
|
||||||
|
}
|
||||||
11
.vscode/settings.json
vendored
Normal file
11
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"sqltools.useNodeRuntime": true,
|
||||||
|
"sqltools.connections": [
|
||||||
|
{
|
||||||
|
"previewLimit": 50,
|
||||||
|
"driver": "SQLite",
|
||||||
|
"name": "rave-users",
|
||||||
|
"database": "${workspaceFolder:rave}/users.db"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -1003,6 +1003,12 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "md5"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.6.4"
|
version = "2.6.4"
|
||||||
|
|
@ -1398,6 +1404,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
|
"md5",
|
||||||
"poem",
|
"poem",
|
||||||
"quick-xml",
|
"quick-xml",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ publish = ["crates-io"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
color-eyre = "0.6.2"
|
color-eyre = "0.6.2"
|
||||||
|
md5 = "0.7.0"
|
||||||
poem = { version = "1.3.58", features = [
|
poem = { version = "1.3.58", features = [
|
||||||
"compression",
|
"compression",
|
||||||
"cookie",
|
"cookie",
|
||||||
|
|
|
||||||
|
|
@ -1 +1,9 @@
|
||||||
-- Add migration script here
|
-- Add migration script here
|
||||||
|
CREATE TABLE users (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
password TEXT NOT NULL,
|
||||||
|
is_admin BOOLEAN NOT NULL DEFAULT FALSE
|
||||||
|
);
|
||||||
|
INSERT INTO users (name, password, is_admin)
|
||||||
|
VALUES ('admin', 'admin', TRUE);
|
||||||
|
|
@ -1,8 +1,28 @@
|
||||||
use crate::{authentication::Authentication, subsonic::SubsonicResponse};
|
use poem::web::Data;
|
||||||
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
authentication::Authentication,
|
||||||
|
subsonic::{self, SubsonicResponse},
|
||||||
|
user,
|
||||||
|
};
|
||||||
|
|
||||||
#[poem::handler]
|
#[poem::handler]
|
||||||
pub fn ping(auth: Authentication) -> SubsonicResponse {
|
pub async fn ping(Data(pool): Data<&SqlitePool>, auth: Authentication) -> SubsonicResponse {
|
||||||
dbg!(auth);
|
let user = user::get_user(pool, &auth.username).await;
|
||||||
|
|
||||||
SubsonicResponse::new_empty()
|
match user {
|
||||||
|
Ok(Some(u)) => {
|
||||||
|
if u.verify(&auth.token, &auth.salt) {
|
||||||
|
SubsonicResponse::new_empty()
|
||||||
|
} else {
|
||||||
|
SubsonicResponse::new_error(subsonic::Error::WrongUsernameOrPassword(None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None) => SubsonicResponse::new_error(subsonic::Error::WrongUsernameOrPassword(None)),
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Error getting user: {}", e);
|
||||||
|
SubsonicResponse::new_error(subsonic::Error::WrongUsernameOrPassword(None))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
src/user.rs
24
src/user.rs
|
|
@ -1,11 +1,29 @@
|
||||||
|
use color_eyre::Result;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub id: i32,
|
pub id: i64,
|
||||||
pub created_at: time::OffsetDateTime,
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// I hate this. It's stored in plaintext. Why?
|
/// I hate this. It's stored in plaintext. Why?
|
||||||
pub password: String,
|
password: String,
|
||||||
pub is_admin: bool,
|
pub is_admin: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl User {
|
||||||
|
pub fn verify(&self, hashed_password: &str, salt: &str) -> bool {
|
||||||
|
let ours = md5::compute(format!("{}{}", self.password, salt));
|
||||||
|
let ours = format!("{ours:x}");
|
||||||
|
|
||||||
|
ours == hashed_password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_user(pool: &SqlitePool, name: &str) -> Result<Option<User>> {
|
||||||
|
Ok(
|
||||||
|
sqlx::query_as!(User, "SELECT * FROM users WHERE name = ?", name)
|
||||||
|
.fetch_optional(pool)
|
||||||
|
.await?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue