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:
Lys 2023-10-08 23:11:59 +03:00
parent 667fcca4e9
commit 67244e63b3
Signed by: lyssieth
GPG key ID: C9CF3D614FAA3940
7 changed files with 110 additions and 7 deletions

View file

@ -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
View 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
View file

@ -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",

View file

@ -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",

View file

@ -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);

View file

@ -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;
match user {
Ok(Some(u)) => {
if u.verify(&auth.token, &auth.salt) {
SubsonicResponse::new_empty() 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))
}
}
} }

View file

@ -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?,
)
}