mirror of
https://github.com/n08i40k/schedule-parser-rusted.git
synced 2025-12-06 17:57:47 +03:00
0.4.0
Авторизация через токен вк Слияние schedule_parser с проектом Перенос схемы запросов/ответов в файлы эндпоинтов Переход с библиотеки jwt на jsonwebtokens
This commit is contained in:
@@ -1,22 +1,19 @@
|
||||
use self::schema::*;
|
||||
use crate::AppState;
|
||||
use crate::database::driver;
|
||||
use crate::database::models::{User, UserRole};
|
||||
use crate::routes::auth::schema;
|
||||
use crate::{utility, AppState};
|
||||
use crate::database::models::UserRole;
|
||||
use crate::routes::auth::shared::{Error, parse_vk_id};
|
||||
use actix_web::{post, web};
|
||||
use objectid::ObjectId;
|
||||
use rand::{Rng, rng};
|
||||
use web::Json;
|
||||
|
||||
#[post("/sign-up")]
|
||||
pub async fn sign_up(
|
||||
data: Json<schema::sign_up::Request>,
|
||||
app_state: web::Data<AppState>,
|
||||
) -> schema::sign_up::Response {
|
||||
use schema::sign_up::*;
|
||||
|
||||
async fn sign_up(data: SignUpData, app_state: &web::Data<AppState>) -> Response {
|
||||
// If user selected forbidden role.
|
||||
if data.role == UserRole::Admin {
|
||||
return Response::err(ErrorCode::DisallowedRole);
|
||||
}
|
||||
|
||||
// If specified group doesn't exist in schedule.
|
||||
let schedule_opt = app_state.schedule.lock().unwrap();
|
||||
|
||||
if let Some(schedule) = &*schedule_opt {
|
||||
@@ -25,37 +22,193 @@ pub async fn sign_up(
|
||||
}
|
||||
}
|
||||
|
||||
if driver::users::contains_by_username(&app_state.database, data.username.clone()) {
|
||||
// If user with specified username already exists.
|
||||
if driver::users::contains_by_username(&app_state.database, &data.username) {
|
||||
return Response::err(ErrorCode::UsernameAlreadyExists);
|
||||
}
|
||||
|
||||
let id = ObjectId::new().unwrap().to_string();
|
||||
let access_token = utility::jwt::encode(&id);
|
||||
|
||||
let user = User {
|
||||
id,
|
||||
username: data.username.clone(),
|
||||
password: bcrypt::hash(data.password.as_str(), bcrypt::DEFAULT_COST).unwrap(),
|
||||
vk_id: None,
|
||||
access_token,
|
||||
group: data.group.clone(),
|
||||
role: data.role.clone(),
|
||||
version: data.version.clone(),
|
||||
};
|
||||
// If user with specified VKID already exists.
|
||||
if let Some(id) = data.vk_id {
|
||||
if driver::users::contains_by_vk_id(&app_state.database, id) {
|
||||
return Response::err(ErrorCode::VkAlreadyExists);
|
||||
}
|
||||
}
|
||||
|
||||
let user = data.to_user();
|
||||
driver::users::insert(&app_state.database, &user).unwrap();
|
||||
|
||||
Response::ok(&user)
|
||||
}
|
||||
|
||||
#[post("/sign-up")]
|
||||
pub async fn sign_up_default(data_json: Json<Request>, app_state: web::Data<AppState>) -> Response {
|
||||
let data = data_json.into_inner();
|
||||
|
||||
sign_up(
|
||||
SignUpData {
|
||||
username: data.username,
|
||||
password: data.password,
|
||||
vk_id: None,
|
||||
group: data.group,
|
||||
role: data.role,
|
||||
version: data.version,
|
||||
},
|
||||
&app_state,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[post("/sign-up-vk")]
|
||||
pub async fn sign_up_vk(data_json: Json<vk::Request>, app_state: web::Data<AppState>) -> Response {
|
||||
let data = data_json.into_inner();
|
||||
|
||||
match parse_vk_id(&data.access_token) {
|
||||
Ok(id) => {
|
||||
sign_up(
|
||||
SignUpData {
|
||||
username: data.username,
|
||||
password: rng()
|
||||
.sample_iter(&rand::distr::Alphanumeric)
|
||||
.take(16)
|
||||
.map(char::from)
|
||||
.collect(),
|
||||
vk_id: Some(id),
|
||||
group: data.group,
|
||||
role: data.role,
|
||||
version: data.version,
|
||||
},
|
||||
&app_state,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Err(err) => {
|
||||
if err != Error::Expired {
|
||||
eprintln!("Failed to parse vk id token!");
|
||||
eprintln!("{:?}", err);
|
||||
}
|
||||
|
||||
Response::err(ErrorCode::InvalidVkAccessToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod schema {
|
||||
use crate::database::models::{User, UserRole};
|
||||
use crate::routes::schema::{ErrorToHttpCode, IResponse, user};
|
||||
use crate::utility;
|
||||
use actix_web::http::StatusCode;
|
||||
use objectid::ObjectId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// WEB
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Request {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
pub group: String,
|
||||
pub role: UserRole,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
pub mod vk {
|
||||
use crate::database::models::UserRole;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Request {
|
||||
pub access_token: String,
|
||||
pub username: String,
|
||||
pub group: String,
|
||||
pub role: UserRole,
|
||||
pub version: String,
|
||||
}
|
||||
}
|
||||
|
||||
pub type Response = IResponse<user::ResponseOk, ResponseErr>;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ResponseOk {
|
||||
id: String,
|
||||
access_token: String,
|
||||
group: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ResponseErr {
|
||||
code: ErrorCode,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum ErrorCode {
|
||||
DisallowedRole,
|
||||
InvalidGroupName,
|
||||
UsernameAlreadyExists,
|
||||
InvalidVkAccessToken,
|
||||
VkAlreadyExists,
|
||||
}
|
||||
|
||||
pub trait ResponseExt {
|
||||
fn ok(user: &User) -> Self;
|
||||
fn err(code: ErrorCode) -> Self;
|
||||
}
|
||||
|
||||
impl ResponseExt for Response {
|
||||
fn ok(user: &User) -> Self {
|
||||
IResponse(Ok(user::ResponseOk::from_user(&user)))
|
||||
}
|
||||
|
||||
fn err(code: ErrorCode) -> Response {
|
||||
Self(Err(ResponseErr { code }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ErrorToHttpCode for ResponseErr {
|
||||
fn to_http_status_code(&self) -> StatusCode {
|
||||
StatusCode::NOT_ACCEPTABLE
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal
|
||||
|
||||
pub struct SignUpData {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
pub vk_id: Option<i32>,
|
||||
pub group: String,
|
||||
pub role: UserRole,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl SignUpData {
|
||||
pub fn to_user(self) -> User {
|
||||
let id = ObjectId::new().unwrap().to_string();
|
||||
let access_token = utility::jwt::encode(&id);
|
||||
|
||||
User {
|
||||
id,
|
||||
username: self.username,
|
||||
password: bcrypt::hash(self.password, bcrypt::DEFAULT_COST).unwrap(),
|
||||
vk_id: self.vk_id,
|
||||
access_token,
|
||||
group: self.group,
|
||||
role: self.role,
|
||||
version: self.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::app_state::app_state;
|
||||
use crate::database::driver;
|
||||
use crate::database::models::UserRole;
|
||||
use crate::routes::auth::schema;
|
||||
use crate::routes::auth::sign_up::sign_up;
|
||||
use crate::test_env::tests::{static_app_state, test_app, test_env};
|
||||
use crate::routes::auth::sign_up::schema::Request;
|
||||
use crate::routes::auth::sign_up::sign_up_default;
|
||||
use crate::test_env::tests::{static_app_state, test_app, test_app_state, test_env};
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::dev::ServiceResponse;
|
||||
use actix_web::http::Method;
|
||||
@@ -68,11 +221,11 @@ mod tests {
|
||||
}
|
||||
|
||||
async fn sign_up_client(data: SignUpPartial) -> ServiceResponse {
|
||||
let app = test_app(app_state(), sign_up).await;
|
||||
let app = test_app(test_app_state(), sign_up_default).await;
|
||||
|
||||
let req = test::TestRequest::with_uri("/sign-up")
|
||||
.method(Method::POST)
|
||||
.set_json(schema::sign_up::Request {
|
||||
.set_json(Request {
|
||||
username: data.username.clone(),
|
||||
password: "example".to_string(),
|
||||
group: data.group.clone(),
|
||||
@@ -91,7 +244,7 @@ mod tests {
|
||||
test_env();
|
||||
|
||||
let app_state = static_app_state();
|
||||
driver::users::delete_by_username(&app_state.database, "test::sign_up_valid".to_string());
|
||||
driver::users::delete_by_username(&app_state.database, &"test::sign_up_valid".to_string());
|
||||
|
||||
// test
|
||||
|
||||
@@ -114,7 +267,7 @@ mod tests {
|
||||
let app_state = static_app_state();
|
||||
driver::users::delete_by_username(
|
||||
&app_state.database,
|
||||
"test::sign_up_multiple".to_string(),
|
||||
&"test::sign_up_multiple".to_string(),
|
||||
);
|
||||
|
||||
let create = sign_up_client(SignUpPartial {
|
||||
@@ -150,4 +303,19 @@ mod tests {
|
||||
|
||||
assert_eq!(resp.status(), StatusCode::NOT_ACCEPTABLE);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn sign_up_invalid_group() {
|
||||
test_env();
|
||||
|
||||
// test
|
||||
let resp = sign_up_client(SignUpPartial {
|
||||
username: "test::sign_up_invalid_group".to_string(),
|
||||
group: "invalid_group".to_string(),
|
||||
role: UserRole::Student,
|
||||
})
|
||||
.await;
|
||||
|
||||
assert_eq!(resp.status(), StatusCode::NOT_ACCEPTABLE);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user