feat(api): implemented /user/info

This commit is contained in:
antifallobst 2023-09-10 17:46:32 +02:00
parent f9595513e0
commit 7bf9e57010
Signed by: antifallobst
GPG Key ID: 2B4F402172791BAF
8 changed files with 131 additions and 8 deletions

View File

@ -21,7 +21,7 @@ __(ND)__ -> Not designed yet.
- [ ] `/deactivate` - [ ] `/deactivate`
- [ ] `/activate` - [ ] `/activate`
- `/user/` - `/user/`
- [ ] `/info` - [X] `/info`
- [ ] `/follow` - [ ] `/follow`
- [ ] `/follows` - [ ] `/follows`
- [ ] `/followers` - [ ] `/followers`

View File

@ -1,28 +1,37 @@
# `/user/info` - GET # `/user/info` - GET
Returns information about the project.
Returns information about the user.
## Urlencoded Parameters ## Urlencoded Parameters
You have to set one parameter. You have to set one parameter.
Setting none or two parameters will result in a _400 Bad Request_ Response. Setting none or two parameters will result in a _400 Bad Request_ Response.
| Parameter | Description | | Parameter | Description |
|-----------|--------------------------------------------------------------------| |-----------|--------------------------------------------------------------------|
| name | The username of the user on which the operation will be performed. | | name | The username of the user on which the operation will be performed. |
| id | The userid of the user on which the operation will be performed. | | id | The userid of the user on which the operation will be performed. |
## Responses ## Responses
### 200 - Success ### 200 - Success
__Content - JSON:__ __Content - JSON:__
| Field | Description | | Field | Description |
|----------|-------------------------------------------------------------------| |----------|-------------------------------------------------------------------|
| id | The users unique id. | | id | The users unique id. |
| name | The users unique username. | | name | The users unique username. |
| joined | The datetime when the user joined. Represented as UNIX timestamp. | | joined | The datetime when the user joined. Represented as UNIX timestamp. |
| is_admin | A boolean if the user is an admin. | | is_admin | A boolean if the user is an admin. |
### 400 - Error: Bad Request ### 400 - Error: Bad Request
The request was malformed. The request was malformed.
### 403 - Error: Forbidden ### 403 - Error: Forbidden
Blocked for security reasons. Blocked for security reasons.
### 404 - Error: Not Found ### 404 - Error: Not Found
The user wasn't found. The user wasn't found.

View File

@ -13,6 +13,10 @@ pub struct ID {
flags: u16, flags: u16,
} }
pub enum AccountFlags {
Admin,
}
#[derive(Debug)] #[derive(Debug)]
pub struct Account { pub struct Account {
pub id: ID, pub id: ID,
@ -35,6 +39,14 @@ impl From<i64> for ID {
} }
} }
impl From<AccountFlags> for u16 {
fn from(value: AccountFlags) -> Self {
match value {
AccountFlags::Admin => 0b0000_0000_0000_0001,
}
}
}
impl ID { impl ID {
pub fn new(id: i64, flags: u16) -> Self { pub fn new(id: i64, flags: u16) -> Self {
Self { Self {
@ -200,4 +212,9 @@ impl Account {
.await?; .await?;
Ok(()) Ok(())
} }
pub fn has_flag(&self, flag: AccountFlags) -> bool {
let mask: u16 = flag.into();
(self.flags as u16 & mask) > 0
}
} }

View File

@ -1,5 +1,6 @@
mod account; mod account;
mod project; mod project;
mod user;
use actix_web::{web, App, HttpServer}; use actix_web::{web, App, HttpServer};
use anyhow::Result; use anyhow::Result;
@ -96,6 +97,7 @@ pub async fn start(port: u16, pool: PgPool) -> Result<()> {
.service(project::calls::create) .service(project::calls::create)
.service(project::calls::info) .service(project::calls::info)
.service(project::calls::delete) .service(project::calls::delete)
.service(user::calls::info)
.app_data(web::Data::new(ApiState { pool: pool.clone() })) .app_data(web::Data::new(ApiState { pool: pool.clone() }))
}) })
.bind(("0.0.0.0", port))? .bind(("0.0.0.0", port))?

33
src/api/user/calls.rs Normal file
View File

@ -0,0 +1,33 @@
use crate::api::user::{data, handlers};
use crate::api::ApiState;
use actix_web::{get, web, HttpResponse, Responder};
use log::error;
#[get("/user/info")]
async fn info(data: web::Data<ApiState>, query: web::Query<data::UserIdQuery>) -> impl Responder {
let request = if query.id.is_none() || query.username.is_none() {
if let Some(id) = query.id {
data::InfoRequest::Id(id)
} else if let Some(name) = query.into_inner().username {
data::InfoRequest::Name(name)
} else {
// No parameters were supplied
return HttpResponse::BadRequest().finish();
}
} else {
// Too many parameters were supplied
return HttpResponse::BadRequest().finish();
};
match handlers::info(&data.pool, request).await {
Ok(resp) => match resp {
data::InfoResponse::Success(b) => HttpResponse::Ok().json(web::Json(b)),
data::InfoResponse::Blocked => HttpResponse::Forbidden().finish(),
data::InfoResponse::NotFound => HttpResponse::NotFound().finish(),
},
Err(e) => {
error!("While handling info request: {e}");
HttpResponse::InternalServerError().finish()
}
}
}

28
src/api/user/data.rs Normal file
View File

@ -0,0 +1,28 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize)]
pub struct UserIdQuery {
pub username: Option<String>,
pub id: Option<i64>,
}
#[derive(Debug)]
pub enum InfoRequest {
Name(String),
Id(i64),
}
#[derive(Debug, Serialize)]
pub struct InfoSuccess {
pub id: i64,
pub name: String,
pub joined: i64,
pub is_admin: bool,
}
#[derive(Debug)]
pub enum InfoResponse {
Success(InfoSuccess),
Blocked,
NotFound,
}

31
src/api/user/handlers.rs Normal file
View File

@ -0,0 +1,31 @@
use crate::{
api::user::data,
accounts::{Account, AccountFlags},
security::is_sql_injection,
};
use anyhow::Result;
use sqlx::PgPool;
pub async fn info(pool: &PgPool, request: data::InfoRequest) -> Result<data::InfoResponse> {
let account = match match request {
data::InfoRequest::Name(name) => {
if is_sql_injection(&name) {
return Ok(data::InfoResponse::Blocked);
}
Account::from_username(pool, &name).await?
}
data::InfoRequest::Id(id) => Account::from_id(pool, id).await?,
} {
Some(p) => p,
None => return Ok(data::InfoResponse::NotFound),
};
let is_admin = account.has_flag(AccountFlags::Admin);
Ok(data::InfoResponse::Success(data::InfoSuccess {
id: account.id.id(),
name: account.username,
joined: account.joined.timestamp(),
is_admin,
}))
}

3
src/api/user/mod.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod calls;
pub mod data;
mod handlers;