summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ignore2
-rw-r--r--curlie_guide8
-rw-r--r--scripts/create_db.sql9
-rw-r--r--src/api/album.rs2
-rw-r--r--src/api/artist.rs2
-rw-r--r--src/api/search_results.rs2
-rw-r--r--src/api/song.rs2
-rw-r--r--src/auth.rs129
-rw-r--r--src/database/album.rs13
-rw-r--r--src/database/artist.rs9
-rw-r--r--src/database/mod.rs4
-rw-r--r--src/database/search_results.rs6
-rw-r--r--src/database/song.rs12
-rw-r--r--src/database/user.rs84
-rw-r--r--src/main.rs80
15 files changed, 260 insertions, 104 deletions
diff --git a/.ignore b/.ignore
index 7f63ba7..60343c2 100644
--- a/.ignore
+++ b/.ignore
@@ -1,2 +1,2 @@
scripts/populate/
-scripts/
+ascripts/
diff --git a/curlie_guide b/curlie_guide
index 462ed48..7db110d 100644
--- a/curlie_guide
+++ b/curlie_guide
@@ -3,8 +3,12 @@ curlie
-f [POST/PUT/DELETE] (leave empty for GET)
(:8000/api/[song/album/artist] || :8000/auth/[encode_token/decode_token/protected])
argument=value (ex: id=2)
-header:value (for tokens: Accept:application/json Authorization:token_here)
-
+header:value (Authorization:token_here)
== decode-auth ==
curlie [-f POST] :8000/auth/decode-token token="token_here"
+
+
+
+
+(saving just in case: Accept:application/json)
diff --git a/scripts/create_db.sql b/scripts/create_db.sql
index 405fa96..56423f5 100644
--- a/scripts/create_db.sql
+++ b/scripts/create_db.sql
@@ -34,9 +34,18 @@ CREATE TABLE song (
FOREIGN KEY (album_id) REFERENCES album(id)
);
+CREATE OR REPLACE TABLE user (
+ id int auto_increment,
+ name varchar(255) unique,
+ password varchar(510),
+ primary key(id)
+);
+
ALTER TABLE song CONVERT TO CHARACTER SET utf8;
ALTER TABLE album CONVERT TO CHARACTER SET utf8;
ALTER TABLE artist CONVERT TO CHARACTER SET utf8;
+ALTER TABLE user CONVERT TO CHARACTER SET utf8;
GRANT ALL PRIVILEGES ON balalaika.* TO 'balalaika_user'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
+
diff --git a/src/api/album.rs b/src/api/album.rs
index 2c6dfbb..9094495 100644
--- a/src/api/album.rs
+++ b/src/api/album.rs
@@ -1,6 +1,6 @@
use crate::api::{get_response_from_query, Response};
-use crate::database::Delete;
use crate::database::album::{Album, AlbumPost, AlbumPut};
+use crate::database::Delete;
use crate::extractors::auth_token::AuthenticationToken;
use crate::AppState;
use actix_web::{delete, get, post, put, web, HttpResponse};
diff --git a/src/api/artist.rs b/src/api/artist.rs
index e8e5009..d36ccf4 100644
--- a/src/api/artist.rs
+++ b/src/api/artist.rs
@@ -1,6 +1,6 @@
use crate::api::{get_response_from_query, Response};
-use crate::database::Delete;
use crate::database::artist::{Artist, ArtistPost, ArtistPut};
+use crate::database::Delete;
use crate::extractors::auth_token::AuthenticationToken;
use crate::AppState;
use actix_web::{delete, get, post, put, web, HttpResponse};
diff --git a/src/api/search_results.rs b/src/api/search_results.rs
index 9ccb013..f53bce8 100644
--- a/src/api/search_results.rs
+++ b/src/api/search_results.rs
@@ -29,7 +29,7 @@ pub async fn search_results(
_ => (
Err(sqlx::Error::RowNotFound),
Err(sqlx::Error::RowNotFound),
- Err(sqlx::Error::RowNotFound)
+ Err(sqlx::Error::RowNotFound),
),
};
diff --git a/src/api/song.rs b/src/api/song.rs
index e046d22..1fec41c 100644
--- a/src/api/song.rs
+++ b/src/api/song.rs
@@ -1,6 +1,6 @@
use crate::api::{get_response_from_query, Response};
-use crate::database::Delete;
use crate::database::song::{Song, SongPost, SongPut};
+use crate::database::Delete;
use crate::extractors::auth_token::AuthenticationToken;
use crate::AppState;
use actix_web::{delete, get, post, put, web, HttpResponse};
diff --git a/src/auth.rs b/src/auth.rs
index e0c8ae9..5fdf079 100644
--- a/src/auth.rs
+++ b/src/auth.rs
@@ -1,19 +1,15 @@
-use actix_web::{web, HttpResponse, Scope};
+use crate::database::user::{User, UserForm};
+use crate::AppState;
+use actix_web::{delete, post, web, HttpResponse, Scope};
use chrono::{Duration, Utc};
-use jsonwebtoken::{
- decode, encode, errors::Error as JwtError, Algorithm, DecodingKey, EncodingKey, Header,
- TokenData, Validation,
-};
+use jsonwebtoken::{encode, EncodingKey, Header};
use serde::{Deserialize, Serialize};
-use crate::AppState;
-use crate::extractors::auth_token::AuthenticationToken;
-
pub fn auth_scope() -> Scope {
web::scope("/auth")
- .route("/encode-token/{id}", web::get().to(encode_token))
- .route("/decode-token", web::post().to(decode_token))
- .route("/protected", web::get().to(protected))
+ .service(register)
+ .service(login)
+ .service(delete_user)
}
#[derive(Serialize, Deserialize)]
@@ -44,44 +40,97 @@ struct DecodeBody {
token: String,
}
-async fn encode_token(path: web::Path<usize>, data: web::Data<AppState>) -> HttpResponse {
- let id: usize = path.into_inner();
+#[post("/register")]
+pub async fn register(
+ app_state: web::Data<AppState>,
+ request_data: web::Json<UserForm>,
+) -> HttpResponse {
+ let query = app_state.database.register(request_data.into_inner()).await;
+ match query {
+ Ok(_) => HttpResponse::Ok().json(Response {
+ message: "Registration executed with no errors".to_owned(),
+ }),
+ Err(e) => HttpResponse::BadRequest().json(Response {
+ message: format!("There was an issue in the request: {}", e).to_owned(),
+ }),
+ }
+}
+
+#[post("/login")]
+pub async fn login(
+ app_state: web::Data<AppState>,
+ request_data: web::Json<UserForm>,
+) -> HttpResponse {
+ let query = app_state.database.login(request_data.into_inner()).await;
+
+ let result = match query {
+ Ok(res) => res,
+ Err(e) => {
+ return HttpResponse::BadRequest().json(Response {
+ message: format!("There was an issue in the request: {}", e).to_owned(),
+ })
+ }
+ };
+
+ let user: User = match result {
+ Some(user) => user,
+ None => {
+ return HttpResponse::BadRequest().json(Response {
+ message: "Username/Password incorrect!".to_owned(),
+ })
+ }
+ };
+
+ let id: usize = match user.id {
+ Some(res) => res as usize,
+ None => {
+ return HttpResponse::BadRequest().json(Response {
+ message: "Internal error: user id not found".to_owned(),
+ })
+ }
+ };
+
+ return match encode_token(id, &app_state.secret).await {
+ Ok(token) => HttpResponse::Ok().json(EncodeResponse {
+ message: format!("Successfully logged in as {}", user.name.unwrap()).to_owned(),
+ token: token.to_owned(),
+ }),
+ Err(response) => response,
+ };
+}
+
+async fn encode_token(id: usize, secret: &String) -> Result<String, HttpResponse> {
let exp: usize = (Utc::now() + Duration::days(365)).timestamp() as usize;
let claims: Claims = Claims { id, exp };
- let token: String = match encode(
+ match encode(
&Header::default(),
&claims,
- &EncodingKey::from_secret(data.secret.as_str().as_ref()),
+ &EncodingKey::from_secret(secret.as_str().as_ref()),
) {
- Ok(res) => res,
- Err(_) => return HttpResponse::Ok().body("Token encoding didn't work\n"),
+ Ok(token) => return Ok(token),
+ Err(_) => return Err(HttpResponse::Ok().body("Token encoding didn't work\n")),
};
-
- HttpResponse::Ok().json(EncodeResponse {
- message: "success".to_owned(),
- token: token.to_owned(),
- })
}
-async fn decode_token(body: web::Json<DecodeBody>, data: web::Data<AppState>) -> HttpResponse {
- let decoded: Result<TokenData<Claims>, JwtError> = decode::<Claims>(
- &body.token,
- &DecodingKey::from_secret(data.secret.as_str().as_ref()),
- &Validation::new(Algorithm::HS256),
- );
+// todo! tell if the user has been deleted or not
+#[delete("/user")]
+pub async fn delete_user(
+ app_state: web::Data<AppState>,
+ request_data: web::Json<UserForm>,
+) -> HttpResponse {
+ let query = app_state
+ .database
+ .delete_user(request_data.into_inner())
+ .await;
- match decoded {
- Ok(token) => HttpResponse::Ok().json(DecodeResponse {
- message: "Authorized".to_string(),
- id: token.claims.id,
- }),
- Err(e) => HttpResponse::BadRequest().json(Response {
- message: e.to_string(),
+ match query {
+ Ok(_) => HttpResponse::Ok().json(Response {
+ message: "Deletion executed with no errors".to_owned(),
}),
+ Err(e) => {
+ return HttpResponse::BadRequest().json(Response {
+ message: format!("There was an issue in the request: {}", e).to_owned(),
+ })
+ }
}
}
-
-async fn protected(auth_token: AuthenticationToken) -> HttpResponse {
- println!("{:#?}", auth_token);
- HttpResponse::Ok().json(Response { message: "Authorized".to_owned() })
-}
diff --git a/src/database/album.rs b/src/database/album.rs
index 87054f1..d7ffae0 100644
--- a/src/database/album.rs
+++ b/src/database/album.rs
@@ -114,18 +114,22 @@ impl DatabaseWrapper {
}
pub async fn edit_album(&self, data: AlbumPut) -> Result<MySqlQueryResult, sqlx::Error> {
- if data.id.is_none() { return Err(sqlx::Error::RowNotFound); }
+ if data.id.is_none() {
+ return Err(sqlx::Error::RowNotFound);
+ }
let og_album: Album = match self.select_album_by_id(data.id.as_ref().unwrap()).await {
Ok(res) => match res.is_some() {
true => res.unwrap(),
false => return Err(sqlx::Error::RowNotFound),
- }
+ },
Err(_) => return Err(sqlx::Error::RowNotFound),
};
sqlx::query!(
"UPDATE album SET name=?, cover=? WHERE id=?",
- data.name.unwrap_or(og_album.name.unwrap_or(String::default())),
- data.cover.unwrap_or(og_album.cover.unwrap_or(String::default())),
+ data.name
+ .unwrap_or(og_album.name.unwrap_or(String::default())),
+ data.cover
+ .unwrap_or(og_album.cover.unwrap_or(String::default())),
data.id,
)
.execute(&self.db_pool)
@@ -149,5 +153,4 @@ impl DatabaseWrapper {
.execute(&self.db_pool)
.await
}
-
}
diff --git a/src/database/artist.rs b/src/database/artist.rs
index 5dda7a8..1d4e1cd 100644
--- a/src/database/artist.rs
+++ b/src/database/artist.rs
@@ -72,17 +72,20 @@ impl DatabaseWrapper {
}
pub async fn edit_artist(&self, data: ArtistPut) -> Result<MySqlQueryResult, sqlx::Error> {
- if data.id.is_none() { return Err(sqlx::Error::RowNotFound); }
+ if data.id.is_none() {
+ return Err(sqlx::Error::RowNotFound);
+ }
let og_artist: Artist = match self.select_artist_by_id(data.id.as_ref().unwrap()).await {
Ok(res) => match res.is_some() {
true => res.unwrap(),
false => return Err(sqlx::Error::RowNotFound),
- }
+ },
Err(_) => return Err(sqlx::Error::RowNotFound),
};
sqlx::query!(
"UPDATE artist SET name=? WHERE id=?",
- data.name.unwrap_or(og_artist.name.unwrap_or(String::default())),
+ data.name
+ .unwrap_or(og_artist.name.unwrap_or(String::default())),
data.id,
)
.execute(&self.db_pool)
diff --git a/src/database/mod.rs b/src/database/mod.rs
index d66b72b..884a597 100644
--- a/src/database/mod.rs
+++ b/src/database/mod.rs
@@ -1,7 +1,8 @@
-pub mod song;
pub mod album;
pub mod artist;
pub mod search_results;
+pub mod song;
+pub mod user;
use serde::{Deserialize, Serialize};
use sqlx::mysql::{MySqlPool, MySqlPoolOptions};
@@ -31,4 +32,3 @@ impl DatabaseWrapper {
Ok(DatabaseWrapper { db_pool: pool })
}
}
-
diff --git a/src/database/search_results.rs b/src/database/search_results.rs
index 027d160..3f0bf01 100644
--- a/src/database/search_results.rs
+++ b/src/database/search_results.rs
@@ -1,7 +1,7 @@
-use crate::database::DatabaseWrapper;
-use crate::database::song::Song;
-use crate::database::artist::Artist;
use crate::database::album::Album;
+use crate::database::artist::Artist;
+use crate::database::song::Song;
+use crate::database::DatabaseWrapper;
impl DatabaseWrapper {
pub async fn search_results_by_name(
diff --git a/src/database/song.rs b/src/database/song.rs
index 45c217b..95b05ba 100644
--- a/src/database/song.rs
+++ b/src/database/song.rs
@@ -140,18 +140,22 @@ impl DatabaseWrapper {
}
pub async fn edit_song(&self, data: SongPut) -> Result<MySqlQueryResult, sqlx::Error> {
- if data.id.is_none() { return Err(sqlx::Error::RowNotFound); }
+ if data.id.is_none() {
+ return Err(sqlx::Error::RowNotFound);
+ }
let og_song: Song = match self.select_song_by_id(data.id.as_ref().unwrap()).await {
Ok(res) => match res.is_some() {
true => res.unwrap(),
false => return Err(sqlx::Error::RowNotFound),
- }
+ },
Err(_) => return Err(sqlx::Error::RowNotFound),
};
sqlx::query!(
"UPDATE song SET name=?, lyrics=? WHERE id=?",
- data.name.unwrap_or(og_song.name.unwrap_or(String::default())),
- data.lyrics.unwrap_or(og_song.lyrics.unwrap_or(String::default())),
+ data.name
+ .unwrap_or(og_song.name.unwrap_or(String::default())),
+ data.lyrics
+ .unwrap_or(og_song.lyrics.unwrap_or(String::default())),
data.id,
)
.execute(&self.db_pool)
diff --git a/src/database/user.rs b/src/database/user.rs
new file mode 100644
index 0000000..ff7b1ab
--- /dev/null
+++ b/src/database/user.rs
@@ -0,0 +1,84 @@
+use crate::database::DatabaseWrapper;
+use serde::{Deserialize, Serialize};
+use sqlx::mysql::MySqlQueryResult;
+
+#[derive(Deserialize, Serialize)]
+pub struct User {
+ pub id: Option<i32>,
+ pub name: Option<String>,
+ password: Option<String>,
+}
+
+#[derive(Deserialize, Serialize)]
+pub struct UserForm {
+ name: Option<String>,
+ password: Option<String>,
+}
+
+impl DatabaseWrapper {
+ pub async fn register(&self, data: UserForm) -> Result<MySqlQueryResult, sqlx::Error> {
+ if data.name.is_none() || data.password.is_none() {
+ return Err(sqlx::Error::RowNotFound);
+ }
+
+ sqlx::query!(
+ "INSERT INTO user (name, password)
+ VALUE (?, PASSWORD(?))",
+ data.name,
+ data.password,
+ )
+ .execute(&self.db_pool)
+ .await
+ }
+
+ pub async fn login(&self, data: UserForm) -> Result<Option<User>, sqlx::Error> {
+ if data.name.is_none() || data.password.is_none() {
+ return Err(sqlx::Error::RowNotFound);
+ }
+
+ sqlx::query_as!(
+ User,
+ "SELECT * FROM user
+ WHERE ? = name
+ AND password(?) = password
+ ",
+ data.name,
+ data.password,
+ )
+ .fetch_optional(&self.db_pool)
+ .await
+ }
+
+ pub async fn delete_user(&self, data: UserForm) -> Result<MySqlQueryResult, sqlx::Error> {
+ if data.name.is_none() || data.password.is_none() {
+ return Err(sqlx::Error::RowNotFound);
+ }
+
+ match sqlx::query_as!(
+ User,
+ "SELECT * FROM user
+ WHERE name = ?
+ AND password = password(?)
+ ",
+ data.name,
+ data.password,
+ )
+ .fetch_one(&self.db_pool)
+ .await
+ {
+ Ok(_) => (),
+ Err(_) => return Err(sqlx::Error::RowNotFound),
+ };
+
+ sqlx::query!(
+ "DELETE FROM user
+ WHERE name = ?
+ AND password = password(?)
+ ",
+ data.name,
+ data.password,
+ )
+ .execute(&self.db_pool)
+ .await
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index 595723a..941df3a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -8,13 +8,13 @@ use dotenv::dotenv;
use std::env;
use std::sync::Arc;
-use utoipa::{
- openapi::security::{HttpAuthScheme, HttpBuilder, SecurityScheme},
- Modify, OpenApi, ToSchema,
-};
-
-use utoipa_swagger_ui::SwaggerUi;
-use database::artist::Artist;
+// use utoipa::{
+// openapi::security::{HttpAuthScheme, HttpBuilder, SecurityScheme},
+// Modify, OpenApi,
+// };
+//
+// use utoipa_swagger_ui::SwaggerUi;
+// use database::artist::Artist;
#[derive(Clone)]
struct AppState {
@@ -48,47 +48,47 @@ async fn main() -> std::io::Result<()> {
secret: jwt_secret,
};
- /* utoipa setup */
- #[derive(OpenApi)]
- #[openapi(
- paths(
- ),
- components(
- schemas(
- Artist
- )
- ),
- modifiers(&SecurityAddon)
- )]
- struct ApiDoc;
+ // /* utoipa setup */
+ // #[derive(OpenApi)]
+ // #[openapi(
+ // paths(
+ // ),
+ // components(
+ // schemas(
+ // Artist
+ // )
+ // ),
+ // modifiers(&SecurityAddon)
+ // )]
+ // struct ApiDoc;
- struct SecurityAddon;
- impl Modify for SecurityAddon {
- fn modify(&self, openapi : &mut utoipa::openapi::OpenApi) {
- let components = openapi.components.as_mut().unwrap();
- components.add_security_scheme(
- "bearer_auth",
- SecurityScheme::Http(
- HttpBuilder::new()
- .scheme(HttpAuthScheme::Bearer)
- .bearer_format("JWT")
- .build()
- ),
- );
- }
- }
+ // struct SecurityAddon;
+ // impl Modify for SecurityAddon {
+ // fn modify(&self, openapi : &mut utoipa::openapi::OpenApi) {
+ // let components = openapi.components.as_mut().unwrap();
+ // components.add_security_scheme(
+ // "bearer_auth",
+ // SecurityScheme::Http(
+ // HttpBuilder::new()
+ // .scheme(HttpAuthScheme::Bearer)
+ // .bearer_format("JWT")
+ // .build()
+ // ),
+ // );
+ // }
+ // }
- let openapi = ApiDoc::openapi();
+ // let openapi = ApiDoc::openapi();
/* main server setup */
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(app_state.clone()))
.route("/", web::get().to(root))
- .service(SwaggerUi::new("/docs/{_:.*}").url(
- "/docs/openapi.json",
- openapi.clone(),
- ))
+ // .service(SwaggerUi::new("/docs/{_:.*}").url(
+ // "/docs/openapi.json",
+ // openapi.clone(),
+ // ))
.service(api::api_scope())
.service(auth::auth_scope())
})