You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/oab/src/api/user.rs

238 lines
7.2 KiB
Rust

3 years ago
//
// user.rs
// Copyright (C) 2022 veypi <i@veypi.com>
// 2022-07-09 03:10
// Distributed under terms of the Apache license.
//
3 years ago
use std::fmt::Debug;
3 years ago
use crate::{
libs,
models::{self, app_user, user, AUStatus, UserPlugin},
AppState, Error, Result,
};
2 years ago
use actix_web::{delete, get, head, http, patch, post, web, HttpResponse, Responder};
3 years ago
use base64;
use chrono::Local;
2 years ago
use proc::{access_read, access_update, crud_update};
3 years ago
use rand::Rng;
2 years ago
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, TransactionTrait};
3 years ago
use serde::{Deserialize, Serialize};
use tracing::info;
#[get("/user/{id}")]
3 years ago
#[access_read("user", id = "&id.clone()")]
pub async fn get(id: web::Path<String>, stat: web::Data<AppState>) -> Result<impl Responder> {
3 years ago
let n = id.into_inner();
let db = stat.db();
3 years ago
if !n.is_empty() {
let d: Option<models::entity::user::Model> =
models::entity::user::Entity::find_by_id(n).one(db).await?;
Ok(web::Json(d))
3 years ago
} else {
Err(Error::Missing("id".to_string()))
}
}
2 years ago
#[derive(Debug, Deserialize, Serialize)]
pub struct ListOptions {
name: Option<String>,
role_id: Option<String>,
app_id: Option<String>,
}
3 years ago
#[get("/user/")]
3 years ago
#[access_read("user")]
2 years ago
pub async fn list(
stat: web::Data<AppState>,
query: web::Query<ListOptions>,
) -> Result<impl Responder> {
let query = query.into_inner();
let res = if let Some(v) = query.name {
user::Entity::find()
.filter(user::Column::Username.contains(v))
.all(stat.db())
.await?
} else if let Some(v) = query.role_id {
models::user_role::Entity::find()
.filter(models::user_role::Column::RoleId.eq(v))
.find_also_related(user::Entity)
.all(stat.db())
.await?
.into_iter()
.filter_map(|(_, u)| return u)
.collect()
} else if let Some(v) = query.app_id {
models::app_user::Entity::find()
.filter(models::app_user::Column::AppId.eq(v))
.find_also_related(user::Entity)
.all(stat.db())
.await?
.into_iter()
.filter_map(|(_, u)| return u)
.collect()
} else {
user::Entity::find().all(stat.db()).await?
};
Ok(web::Json(res))
3 years ago
}
#[derive(Debug, Deserialize, Serialize)]
pub struct LoginOpt {
typ: Option<String>,
password: String,
}
#[head("/user/{id}")]
pub async fn login(
q: web::Query<LoginOpt>,
id: web::Path<String>,
stat: web::Data<AppState>,
) -> Result<HttpResponse> {
3 years ago
let id = id.into_inner();
let q = q.into_inner();
let filter = match q.typ {
3 years ago
Some(t) => match t.as_str() {
"phone" => user::Column::Phone.eq(id),
"email" => user::Column::Email.eq(id),
_ => user::Column::Username.eq(id),
3 years ago
},
_ => user::Column::Username.eq(id),
3 years ago
};
let p = match base64::decode(q.password.as_bytes()) {
Err(_) => return Err(Error::ArgInvalid("password".to_string())),
Ok(p) => p,
};
let p = match std::str::from_utf8(&p) {
Ok(p) => p,
Err(_) => return Err(Error::ArgInvalid("password".to_string())),
};
let u: Option<user::Model> = models::user::Entity::find()
.filter(filter)
.one(stat.db())
.await?;
3 years ago
let u = match u {
Some(u) => u,
None => return Err(Error::NotFound("user".to_string())),
};
3 years ago
3 years ago
u.check_pass(p)?;
let au: Option<app_user::Model> = app_user::Entity::find()
.filter(app_user::Column::AppId.eq(&stat.uuid))
.filter(app_user::Column::UserId.eq(&u.id))
.one(stat.db())
.await?;
let au = match au {
Some(au) => au,
3 years ago
None => {
// 未绑定应用时进行绑定操作
let aid = stat.uuid.clone();
let db = stat.db().begin().await?;
2 years ago
let s = libs::user::connect_to_app(u.id.clone(), aid, &db, None).await?;
db.commit().await?;
s
3 years ago
}
};
if au.status == AUStatus::OK as i32 {
let result = sqlx::query_as::<_, models::AccessCore>(
"select access.name, access.rid, access.level from access, user_role, role WHERE user_role.user_id = ? && access.role_id=user_role.role_id && role.id=user_role.role_id && role.app_id = ?",
3 years ago
)
.bind(&u.id)
.bind(stat.uuid.clone())
.fetch_all(stat.sqlx())
3 years ago
.await?;
3 years ago
Ok(HttpResponse::build(http::StatusCode::OK)
2 years ago
.insert_header(("auth_token", u.token(result).to_string(&stat.key)?))
3 years ago
.body("".to_string()))
} else {
Ok(HttpResponse::build(http::StatusCode::FORBIDDEN)
.insert_header(("error", au.status))
3 years ago
.body("".to_string()))
}
3 years ago
}
#[derive(Debug, Deserialize, Serialize)]
pub struct RegisterOpt {
username: String,
password: String,
}
#[post("/user/")]
pub async fn register(
q: web::Json<RegisterOpt>,
stat: web::Data<AppState>,
) -> Result<impl Responder> {
3 years ago
let q = q.into_inner();
// let mut tx = dbtx().await;
info!("{:#?}", q);
let u: Option<models::user::Model> = user::Entity::find()
.filter(user::Column::Username.eq(&q.username))
.one(stat.db())
.await?;
// 初始化用户信息
let u: models::user::ActiveModel = match u {
3 years ago
Some(_) => return Err(Error::ArgDuplicated(format!("username: {}", q.username))),
None => {
let p = match base64::decode(q.password.as_bytes()) {
Err(_) => return Err(Error::ArgInvalid("password".to_string())),
Ok(p) => p,
};
let p = match std::str::from_utf8(&p) {
Ok(p) => p,
Err(_) => return Err(Error::ArgInvalid("password".to_string())),
};
let mut u = models::user::Model::default();
u.username = q.username.clone();
u.id = uuid::Uuid::new_v4().to_string().replace("-", "");
3 years ago
u.update_pass(&p)?;
3 years ago
let mut rng = rand::thread_rng();
let idx: i64 = rng.gen_range(1..221);
u.icon = Some(format!("/media/icon/usr/{:04}.jpg", idx));
2 years ago
u.space = 300;
u.used = 0;
2 years ago
info!("{}", u.created.to_string());
u.created = Local::now().naive_utc();
u.updated = Local::now().naive_utc();
info!("{}", u.created.to_string());
u.into()
3 years ago
}
};
let db = stat.db().begin().await?;
3 years ago
3 years ago
// 创建用户
let u = u.insert(&db).await?;
3 years ago
// 关联应用
2 years ago
libs::user::connect_to_app(u.id.clone(), stat.uuid.clone(), &db, None).await?;
db.commit().await?;
Ok(web::Json(u))
3 years ago
}
2 years ago
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpdateOpt {
pub username: Option<String>,
pub icon: Option<String>,
pub nickname: Option<String>,
pub email: Option<String>,
pub phone: Option<String>,
}
#[patch("/user/{id}")]
#[access_update("user")]
#[crud_update(user, filter = "Id", props = "username, icon, nickname, email, phone")]
2 years ago
pub async fn update(
id: web::Path<String>,
stat: web::Data<AppState>,
data: web::Json<UpdateOpt>,
) -> Result<impl Responder> {
Ok("")
}
3 years ago
#[delete("/user/")]
pub async fn delete() -> impl Responder {
""
}