Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

backend: refactor auth from request #551

Merged
merged 7 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 68 additions & 0 deletions nghe-api/src/auth/form.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use nghe_proc_macro::api_derive;
use serde::Deserialize;

use super::token;

#[api_derive]
#[serde(untagged)]
#[cfg_attr(test, derive(PartialEq))]
pub enum Form<'u, 's> {
Token(token::Auth<'u, 's>),
}

pub trait Trait<'u, 's, R>: for<'de> Deserialize<'de> {
fn new(request: R, auth: Form<'u, 's>) -> Self;
fn auth<'form>(&'form self) -> &'form Form<'u, 's>;
fn request(self) -> R;
}

impl<'u, 't> From<token::Auth<'u, 't>> for Form<'u, 't> {
fn from(value: token::Auth<'u, 't>) -> Self {
Self::Token(value)
}
}

#[cfg(test)]
mod tests {
use rstest::rstest;

use super::*;
use crate::auth::token::Token;

#[api_derive]
#[derive(PartialEq)]
pub struct Test<'u, 't> {
value: Option<u32>,
#[serde(flatten, borrow)]
form: Form<'u, 't>,
}

#[rstest]
#[case(
"t=26719a1196d2a940705a59634eb18eab&\
u=username&s=c19b2d&value=10",
Some(Test {
value: Some(10),
form: token::Auth {
username: "username".into(),
salt: "c19b2d".into(),
token: Token::new(b"sesame", "c19b2d")
}.into()
}
))]
#[case(
"t=26719a1196d2a940705a59634eb18eab&u=username&s=c19b2d",
Some(Test {
value: None,
form: token::Auth {
username: "username".into(),
salt: "c19b2d".into(),
token: Token::new(b"sesame", "c19b2d")
}.into()
}
))]
#[case("u=username&s=c19b2d", None)]
fn test_deserialize(#[case] input: &str, #[case] result: Option<Test<'_, '_>>) {
assert_eq!(serde_html_form::from_str(input).ok(), result);
}
}
58 changes: 3 additions & 55 deletions nghe-api/src/auth/mod.rs
Original file line number Diff line number Diff line change
@@ -1,57 +1,5 @@
mod token;
pub mod form;
pub mod token;

use std::borrow::Cow;

use nghe_proc_macro::api_derive;
pub use form::Form;
pub use token::Token;

#[api_derive]
#[derive(Clone)]
#[cfg_attr(any(test, feature = "test"), derive(Default))]
pub struct Auth<'u, 't> {
#[serde(rename = "u")]
pub username: Cow<'u, str>,
#[serde(rename = "s")]
pub salt: Cow<'t, str>,
#[serde(rename = "t")]
pub token: Token,
}

impl Auth<'_, '_> {
pub fn check(&self, password: impl AsRef<[u8]>) -> bool {
let password = password.as_ref();
let password_token = Token::new(password, self.salt.as_bytes());
password_token == self.token
}
}

#[cfg(test)]
mod tests {
use fake::faker::internet::en::Password;
use fake::Fake;

use super::*;

#[test]
fn test_check_success() {
let password = Password(16..32).fake::<String>().into_bytes();
let client_salt = Password(8..16).fake::<String>();
let client_token = Token::new(&password, &client_salt);
assert!(
Auth { salt: (&client_salt).into(), token: client_token, ..Default::default() }
.check(password)
);
}

#[test]
fn test_check_failed() {
let password = Password(16..32).fake::<String>().into_bytes();
let client_salt = Password(8..16).fake::<String>();
let wrong_client_salt = Password(8..16).fake::<String>();
let client_token = Token::new(&password, &client_salt);
assert!(
!Auth { salt: (&wrong_client_salt).into(), token: client_token, ..Default::default() }
.check(password)
);
}
}
36 changes: 27 additions & 9 deletions nghe-api/src/auth/token.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
use std::borrow::Cow;

use nghe_proc_macro::api_derive;

#[api_derive(request = false, response = false, eq = false)]
#[api_derive(request = false, response = false)]
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(any(test, feature = "test"), derive(Default))]
pub struct Token([u8; 16]);

#[api_derive]
#[derive(Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub struct Auth<'u, 's> {
#[serde(rename = "u")]
pub username: Cow<'u, str>,
#[serde(rename = "s")]
pub salt: Cow<'s, str>,
#[serde(rename = "t")]
pub token: Token,
}

impl Token {
pub fn new(password: impl AsRef<[u8]>, salt: impl AsRef<[u8]>) -> Self {
let password = password.as_ref();
Expand Down Expand Up @@ -46,15 +59,20 @@ mod serde {

#[cfg(test)]
mod tests {
use serde_json::{from_value, json};
use rstest::rstest;

use super::*;

#[test]
fn test_tokenize() {
assert_eq!(
from_value::<Token>(json!("26719a1196d2a940705a59634eb18eab")).unwrap(),
Token::new(b"sesame", "c19b2d")
);
#[api_derive]
#[cfg_attr(test, derive(PartialEq))]
pub struct Test {
token: Token,
}

#[rstest]
#[case("token=26719a1196d2a940705a59634eb18eab", Some(Token::new(b"sesame", "c19b2d")))]
#[case("token=26719a1196d2a940705a59634eb18eab1", None)]
fn test_deserialize(#[case] input: &str, #[case] result: Option<Token>) {
assert_eq!(serde_html_form::from_str(input).ok(), result.map(|token| Test { token }));
}
}
2 changes: 1 addition & 1 deletion nghe-api/src/bookmarks/get_playqueue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::id3;

#[api_derive]
#[endpoint(path = "getPlayQueue")]
pub struct Request {}
pub struct Request;

#[api_derive]
#[derive(Default)]
Expand Down
3 changes: 3 additions & 0 deletions nghe-api/src/browsing/get_artists.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,21 @@ pub struct Request {
}

#[api_derive]
#[cfg_attr(feature = "test", derive(PartialEq))]
pub struct Index {
pub name: String,
pub artist: Vec<id3::artist::Artist>,
}

#[api_derive]
#[cfg_attr(feature = "test", derive(PartialEq))]
pub struct Artists {
pub ignored_articles: String,
pub index: Vec<Index>,
}

#[api_derive]
#[cfg_attr(feature = "test", derive(PartialEq))]
pub struct Response {
pub artists: Artists,
}
2 changes: 1 addition & 1 deletion nghe-api/src/browsing/get_genres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::id3;

#[api_derive]
#[endpoint(path = "getGenres")]
pub struct Request {}
pub struct Request;

#[api_derive]
pub struct Genres {
Expand Down
2 changes: 1 addition & 1 deletion nghe-api/src/browsing/get_music_folders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use uuid::Uuid;

#[api_derive]
#[endpoint(path = "getMusicFolders")]
pub struct Request {}
pub struct Request;

#[api_derive]
pub struct MusicFolder {
Expand Down
2 changes: 1 addition & 1 deletion nghe-api/src/common/filesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use nghe_proc_macro::api_derive;

#[repr(i16)]
#[api_derive(fake = true)]
#[derive(ConstParamTy)]
#[derive(Clone, Copy, PartialEq, Eq, ConstParamTy)]
pub enum Type {
Local,
S3,
Expand Down
2 changes: 1 addition & 1 deletion nghe-api/src/common/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub trait Trait: Debug + Copy {
}

#[api_derive]
#[derive(IntoStaticStr, EnumString)]
#[derive(Clone, Copy, IntoStaticStr, EnumString)]
#[strum(serialize_all = "lowercase")]
#[cfg_attr(feature = "test", derive(strum::AsRefStr))]
pub enum Transcode {
Expand Down
7 changes: 5 additions & 2 deletions nghe-api/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use serde::de::DeserializeOwned;
use serde::{Serialize, Serializer};

use super::constant;
use crate::auth;

#[api_derive(debug = false)]
struct RootResponse<B> {
Expand Down Expand Up @@ -35,9 +36,11 @@ pub trait FormURL {
const URL_FORM_VIEW: &'static str;
}

pub trait FormRequest = FormURL + DeserializeOwned;
pub trait FormRequest<'u, 's>: FormURL + DeserializeOwned {
type AuthForm: auth::form::Trait<'u, 's, Self> + Send;
}

pub trait FormEndpoint: FormRequest {
pub trait FormEndpoint: for<'u, 's> FormRequest<'u, 's> {
type Response: Serialize;
}

Expand Down
3 changes: 3 additions & 0 deletions nghe-api/src/common/typed_uuid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ use nghe_proc_macro::api_derive;
use uuid::Uuid;

#[api_derive(fake = true)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Type {
Artist,
Album,
Song,
}

#[api_derive(request = false, response = false, fake = true)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct TypedUuid {
pub ty: Type,
pub id: Uuid,
Expand Down Expand Up @@ -80,6 +82,7 @@ mod tests {
use super::*;

#[api_derive]
#[derive(PartialEq)]
struct Test {
pub id: TypedUuid,
}
Expand Down
3 changes: 2 additions & 1 deletion nghe-api/src/id3/artist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use strum::IntoStaticStr;
use uuid::Uuid;

#[api_derive(response = false)]
#[derive(IntoStaticStr)]
#[derive(PartialEq, Eq, IntoStaticStr)]
#[strum(serialize_all = "lowercase")]
pub enum Role {
Artist,
Expand All @@ -20,6 +20,7 @@ pub enum Role {
#[derive(Builder)]
#[builder(on(_, required))]
#[builder(state_mod(vis = "pub"))]
#[cfg_attr(feature = "test", derive(PartialEq))]
pub struct Artist {
#[serde(flatten)]
pub required: Required,
Expand Down
1 change: 1 addition & 0 deletions nghe-api/src/id3/artist/required.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use nghe_proc_macro::api_derive;
use uuid::Uuid;

#[api_derive]
#[cfg_attr(feature = "test", derive(PartialEq))]
pub struct Required {
pub id: Uuid,
pub name: String,
Expand Down
2 changes: 1 addition & 1 deletion nghe-api/src/id3/genre/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use nghe_proc_macro::api_derive;
pub use with_count::WithCount;

#[api_derive]
#[cfg_attr(feature = "test", derive(Clone, Hash))]
#[cfg_attr(feature = "test", derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash))]
pub struct Genre {
pub name: String,
}
Expand Down
6 changes: 3 additions & 3 deletions nghe-api/src/lists/get_album_list2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use uuid::Uuid;
use crate::id3;

// TODO: Optimize this after https://github.com/serde-rs/serde/issues/1183
#[api_derive(serde_as = true, copy = false)]
#[api_derive(serde_as = true)]
#[serde(tag = "type")]
#[cfg_attr(test, derive(Default))]
#[cfg_attr(test, derive(Default, PartialEq))]
pub enum Type {
#[cfg_attr(test, default)]
Random,
Expand All @@ -27,7 +27,7 @@ pub enum Type {

#[api_derive]
#[endpoint(path = "getAlbumList2")]
#[cfg_attr(test, derive(Default))]
#[cfg_attr(test, derive(Default, PartialEq))]
pub struct Request {
#[serde(flatten, rename = "type")]
pub ty: Type,
Expand Down
2 changes: 1 addition & 1 deletion nghe-api/src/media_annotation/scrobble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use uuid::Uuid;

#[api_derive(serde_as = true)]
#[endpoint(path = "scrobble")]
#[cfg_attr(test, derive(Default))]
#[cfg_attr(test, derive(Default, PartialEq))]
pub struct Request {
#[serde(rename = "id")]
pub ids: Vec<Uuid>,
Expand Down
2 changes: 1 addition & 1 deletion nghe-api/src/media_retrieval/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use uuid::Uuid;
use crate::common::format;

#[api_derive(request = false)]
#[derive(Default)]
#[derive(Default, Clone, Copy)]
pub enum Format {
#[default]
Raw,
Expand Down
4 changes: 3 additions & 1 deletion nghe-api/src/playlists/create_playlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ use uuid::Uuid;

use super::playlist;

#[api_derive(request = true, copy = false)]
#[api_derive(request = true)]
#[serde(untagged)]
#[cfg_attr(test, derive(PartialEq))]
pub enum CreateOrUpdate {
Create { name: String },
Update { playlist_id: Uuid },
}

#[api_derive]
#[endpoint(path = "createPlaylist")]
#[cfg_attr(test, derive(PartialEq))]
pub struct Request {
#[serde(flatten)]
pub create_or_update: CreateOrUpdate,
Expand Down
Loading
Loading