refactor(treewide)/feat(api): implemented /account/id - ran rustfmt over the codebase
This commit is contained in:
parent
2d7d66bed4
commit
27aadf46d5
|
@ -17,3 +17,5 @@ __Content - JSON:__
|
|||
|
||||
### 401 - Error: Unauthorized
|
||||
The provided access token doesn't allow you to perform this operation.
|
||||
### 403 - Error: Forbidden
|
||||
Blocked for security reasons.
|
|
@ -3,7 +3,7 @@ use argon2::{
|
|||
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
|
||||
Argon2,
|
||||
};
|
||||
use base64::{Engine, engine::general_purpose::STANDARD as Base64Engine};
|
||||
use base64::{engine::general_purpose::STANDARD as Base64Engine, Engine};
|
||||
use sha2::{Digest, Sha256};
|
||||
use sqlx::{postgres::PgPool, types::chrono as sqlx_chrono};
|
||||
|
||||
|
|
|
@ -13,7 +13,9 @@ async fn register(
|
|||
Ok(resp) => match resp {
|
||||
data::RegisterResponse::Success => HttpResponse::Ok().finish(),
|
||||
data::RegisterResponse::Conflict(b) => HttpResponse::Conflict().json(web::Json(b)),
|
||||
data::RegisterResponse::Unprocessable(b) => HttpResponse::UnprocessableEntity().json(web::Json(b)),
|
||||
data::RegisterResponse::Unprocessable(b) => {
|
||||
HttpResponse::UnprocessableEntity().json(web::Json(b))
|
||||
}
|
||||
data::RegisterResponse::Blocked => HttpResponse::Forbidden().finish(),
|
||||
},
|
||||
Err(e) => {
|
||||
|
@ -84,6 +86,21 @@ async fn delete(
|
|||
}
|
||||
}
|
||||
|
||||
#[get("/account/id")]
|
||||
async fn id(data: web::Data<ApiState>, auth: BearerAuth) -> impl Responder {
|
||||
match handlers::id(&data.pool, auth.token().to_string()).await {
|
||||
Ok(resp) => match resp {
|
||||
data::IdResponse::Success(b) => HttpResponse::Ok().json(web::Json(b)),
|
||||
data::IdResponse::Unauthorized => HttpResponse::Unauthorized().finish(),
|
||||
data::IdResponse::Blocked => HttpResponse::Forbidden().finish(),
|
||||
},
|
||||
Err(e) => {
|
||||
error!("While handling delete request: {e}");
|
||||
HttpResponse::InternalServerError().finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[delete("/account/tokens")]
|
||||
async fn tokens_delete(
|
||||
data: web::Data<ApiState>,
|
||||
|
|
|
@ -73,6 +73,19 @@ pub enum DeleteResponse {
|
|||
Unauthorized,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct IdSuccess {
|
||||
pub id: i64,
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum IdResponse {
|
||||
Success(IdSuccess),
|
||||
Unauthorized,
|
||||
Blocked,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct TokenDeleteRequest {
|
||||
pub token: String,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
accounts::Account,
|
||||
api::account::data,
|
||||
security::{is_sql_injection, AlphaExt, meet_password_criteria},
|
||||
security::{is_sql_injection, meet_password_criteria, AlphaExt},
|
||||
tokens::{AuthToken, VerificationToken},
|
||||
};
|
||||
use anyhow::Result;
|
||||
|
@ -52,11 +52,15 @@ pub async fn register(
|
|||
)?;
|
||||
|
||||
if !email_regex.is_match(&request.email) {
|
||||
return Ok(data::RegisterResponse::Unprocessable(data::RegisterUnprocessable::Email));
|
||||
return Ok(data::RegisterResponse::Unprocessable(
|
||||
data::RegisterUnprocessable::Email,
|
||||
));
|
||||
}
|
||||
|
||||
if !meet_password_criteria(&request.password) {
|
||||
return Ok(data::RegisterResponse::Unprocessable(data::RegisterUnprocessable::Password));
|
||||
return Ok(data::RegisterResponse::Unprocessable(
|
||||
data::RegisterUnprocessable::Password,
|
||||
));
|
||||
}
|
||||
|
||||
let account = Account::new(pool, &request.username, &request.email, &request.password).await?;
|
||||
|
@ -172,6 +176,31 @@ pub async fn delete(
|
|||
Ok(data::DeleteResponse::Success)
|
||||
}
|
||||
|
||||
pub async fn id(pool: &PgPool, auth: String) -> Result<data::IdResponse> {
|
||||
if !auth.is_alpha() {
|
||||
return Ok(data::IdResponse::Blocked);
|
||||
}
|
||||
|
||||
let token = match AuthToken::check(pool, &auth).await? {
|
||||
Some(t) => t,
|
||||
None => return Ok(data::IdResponse::Unauthorized),
|
||||
};
|
||||
|
||||
let account = match Account::from_id(pool, token.account.id()).await? {
|
||||
Some(a) => a,
|
||||
None => {
|
||||
return Err(anyhow::Error::msg(
|
||||
"Failed to get account data. Account not found in database",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(data::IdResponse::Success(data::IdSuccess {
|
||||
id: account.id.id(),
|
||||
username: account.username,
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn token_delete(
|
||||
pool: &PgPool,
|
||||
auth: String,
|
||||
|
|
|
@ -91,6 +91,7 @@ pub async fn start(port: u16, pool: PgPool) -> Result<()> {
|
|||
.service(account::calls::verify)
|
||||
.service(account::calls::authenticate)
|
||||
.service(account::calls::delete)
|
||||
.service(account::calls::id)
|
||||
.service(account::calls::tokens_delete)
|
||||
.service(account::calls::tokens_get)
|
||||
.service(project::calls::create)
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
use crate::api::ApiState;
|
||||
use crate::api::project::{data, handlers};
|
||||
use actix_web::{get, post, web, HttpResponse, Responder, delete};
|
||||
use crate::api::ApiState;
|
||||
use actix_web::{delete, get, post, web, HttpResponse, Responder};
|
||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||
use log::error;
|
||||
|
||||
#[post("/project/create")]
|
||||
async fn create(data: web::Data<ApiState>, auth: BearerAuth, body: web::Json<data::CreateRequest>) -> impl Responder {
|
||||
async fn create(
|
||||
data: web::Data<ApiState>,
|
||||
auth: BearerAuth,
|
||||
body: web::Json<data::CreateRequest>,
|
||||
) -> impl Responder {
|
||||
match handlers::create(&data.pool, auth.token().to_string(), body.into_inner()).await {
|
||||
Ok(resp) => match resp {
|
||||
data::CreateResponse::Success(b) => HttpResponse::Ok().json(web::Json(b)),
|
||||
|
@ -21,8 +25,10 @@ async fn create(data: web::Data<ApiState>, auth: BearerAuth, body: web::Json<dat
|
|||
}
|
||||
|
||||
#[get("/project/info")]
|
||||
async fn info(data: web::Data<ApiState>, query: web::Query<data::ProjectIdQuery>) -> impl Responder {
|
||||
|
||||
async fn info(
|
||||
data: web::Data<ApiState>,
|
||||
query: web::Query<data::ProjectIdQuery>,
|
||||
) -> impl Responder {
|
||||
let request = if query.id.is_none() || query.name.is_none() {
|
||||
if let Some(id) = query.id {
|
||||
data::InfoRequest::Id(id)
|
||||
|
@ -51,8 +57,11 @@ async fn info(data: web::Data<ApiState>, query: web::Query<data::ProjectIdQuery>
|
|||
}
|
||||
|
||||
#[delete("/project/delete")]
|
||||
async fn delete(data: web::Data<ApiState>, auth: BearerAuth, query: web::Query<data::ProjectIdQuery>) -> impl Responder {
|
||||
|
||||
async fn delete(
|
||||
data: web::Data<ApiState>,
|
||||
auth: BearerAuth,
|
||||
query: web::Query<data::ProjectIdQuery>,
|
||||
) -> impl Responder {
|
||||
let request = if query.id.is_none() || query.name.is_none() {
|
||||
if let Some(id) = query.id {
|
||||
data::DeleteRequest::Id(id)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ProjectIdQuery {
|
||||
pub name: Option<String>,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use crate::accounts::Account;
|
||||
use crate::projects::ProjectMemberFlags;
|
||||
use crate::{
|
||||
api::project::data,
|
||||
projects::Project,
|
||||
|
@ -6,8 +8,6 @@ use crate::{
|
|||
};
|
||||
use anyhow::{Error, Result};
|
||||
use sqlx::PgPool;
|
||||
use crate::accounts::Account;
|
||||
use crate::projects::ProjectMemberFlags;
|
||||
|
||||
pub async fn create(
|
||||
pool: &PgPool,
|
||||
|
@ -46,22 +46,28 @@ pub async fn info(pool: &PgPool, request: data::InfoRequest) -> Result<data::Inf
|
|||
}
|
||||
Project::from_name(pool, &name).await?
|
||||
}
|
||||
data::InfoRequest::Id(id) => Project::from_id(pool, id).await?
|
||||
}
|
||||
{
|
||||
data::InfoRequest::Id(id) => Project::from_id(pool, id).await?,
|
||||
} {
|
||||
Some(p) => p,
|
||||
None => return Ok(data::InfoResponse::NotFound)
|
||||
None => return Ok(data::InfoResponse::NotFound),
|
||||
};
|
||||
|
||||
let mut members = Vec::new();
|
||||
members.reserve(project.members.len());
|
||||
for &id in project.members.iter() {
|
||||
members.push((match Account::from_id(pool, id.id()).await? {
|
||||
Some(m) => m.username,
|
||||
None => return Err(Error::msg(format!("The account with id {} was not found", id.id()))),
|
||||
},
|
||||
id.id(),
|
||||
id.flags()));
|
||||
members.push((
|
||||
match Account::from_id(pool, id.id()).await? {
|
||||
Some(m) => m.username,
|
||||
None => {
|
||||
return Err(Error::msg(format!(
|
||||
"The account with id {} was not found",
|
||||
id.id()
|
||||
)))
|
||||
}
|
||||
},
|
||||
id.id(),
|
||||
id.flags(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(data::InfoResponse::Success(data::InfoSuccess {
|
||||
|
@ -94,14 +100,16 @@ pub async fn delete(
|
|||
}
|
||||
Project::from_name(pool, &name).await?
|
||||
}
|
||||
data::DeleteRequest::Id(id) => Project::from_id(pool, id).await?
|
||||
}
|
||||
{
|
||||
data::DeleteRequest::Id(id) => Project::from_id(pool, id).await?,
|
||||
} {
|
||||
Some(p) => p,
|
||||
None => return Ok(data::DeleteResponse::NotFound)
|
||||
None => return Ok(data::DeleteResponse::NotFound),
|
||||
};
|
||||
|
||||
if !project.member_has_flag(&token.account, ProjectMemberFlags::Owner).await {
|
||||
if !project
|
||||
.member_has_flag(&token.account, ProjectMemberFlags::Owner)
|
||||
.await
|
||||
{
|
||||
return Ok(data::DeleteResponse::Unauthorized);
|
||||
}
|
||||
|
||||
|
|
|
@ -53,15 +53,17 @@ impl Project {
|
|||
.fetch_one(pool)
|
||||
.await
|
||||
{
|
||||
Ok(row) => {
|
||||
Ok(Some(Self {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
description: row.description,
|
||||
created: row.created,
|
||||
members: row.members.iter().map(|&raw_id| raw_id.into()).collect::<Vec<ID>>(),
|
||||
}))
|
||||
},
|
||||
Ok(row) => Ok(Some(Self {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
description: row.description,
|
||||
created: row.created,
|
||||
members: row
|
||||
.members
|
||||
.iter()
|
||||
.map(|&raw_id| raw_id.into())
|
||||
.collect::<Vec<ID>>(),
|
||||
})),
|
||||
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||
Err(e) => Err(Error::new(e)),
|
||||
}
|
||||
|
@ -72,15 +74,17 @@ impl Project {
|
|||
.fetch_one(pool)
|
||||
.await
|
||||
{
|
||||
Ok(row) => {
|
||||
Ok(Some(Self {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
description: row.description,
|
||||
created: row.created,
|
||||
members: row.members.iter().map(|&raw_id| raw_id.into()).collect::<Vec<ID>>(),
|
||||
}))
|
||||
},
|
||||
Ok(row) => Ok(Some(Self {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
description: row.description,
|
||||
created: row.created,
|
||||
members: row
|
||||
.members
|
||||
.iter()
|
||||
.map(|&raw_id| raw_id.into())
|
||||
.collect::<Vec<ID>>(),
|
||||
})),
|
||||
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||
Err(e) => Err(Error::new(e)),
|
||||
}
|
||||
|
@ -99,12 +103,9 @@ impl Project {
|
|||
}
|
||||
|
||||
pub async fn delete(&self, pool: &PgPool) -> Result<()> {
|
||||
sqlx::query!(
|
||||
r#"DELETE FROM Projects WHERE id = $1;"#,
|
||||
self.id
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
sqlx::query!(r#"DELETE FROM Projects WHERE id = $1;"#, self.id)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -6,14 +6,18 @@ pub fn is_sql_injection(string: &String) -> bool {
|
|||
}
|
||||
|
||||
pub fn meet_password_criteria(string: &String) -> bool {
|
||||
if string.len() < 12 ||
|
||||
!string.chars().any(|c| c.is_numeric()) ||
|
||||
!string.chars().any(|c| c.is_lowercase()) ||
|
||||
!string.chars().any(|c| c.is_uppercase()) ||
|
||||
!string.chars().any(|c|{
|
||||
if string.len() < 12
|
||||
|| !string.chars().any(|c| c.is_numeric())
|
||||
|| !string.chars().any(|c| c.is_lowercase())
|
||||
|| !string.chars().any(|c| c.is_uppercase())
|
||||
|| !string.chars().any(|c| {
|
||||
let x = c as u8;
|
||||
(x >= 32 && x <= 47) || (x >= 58 && x <= 64) || (x >= 91 && x <= 96) || (x >= 123 && x <= 126)
|
||||
}) {
|
||||
(x >= 32 && x <= 47)
|
||||
|| (x >= 58 && x <= 64)
|
||||
|| (x >= 91 && x <= 96)
|
||||
|| (x >= 123 && x <= 126)
|
||||
})
|
||||
{
|
||||
return false;
|
||||
}
|
||||
true
|
||||
|
|
Loading…
Reference in New Issue