Add tokens routes
This commit is contained in:
parent
3081757536
commit
544513d118
28
moneymgr_backend/Cargo.lock
generated
28
moneymgr_backend/Cargo.lock
generated
@ -1598,6 +1598,9 @@ name = "ipnet"
|
|||||||
version = "2.11.0"
|
version = "2.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is_terminal_polyfill"
|
name = "is_terminal_polyfill"
|
||||||
@ -1660,6 +1663,29 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
|
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy-regex"
|
||||||
|
version = "3.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126"
|
||||||
|
dependencies = [
|
||||||
|
"lazy-regex-proc_macros",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy-regex-proc_macros"
|
||||||
|
version = "3.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@ -1826,6 +1852,8 @@ dependencies = [
|
|||||||
"diesel_migrations",
|
"diesel_migrations",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"ipnet",
|
||||||
|
"lazy-regex",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"light-openid",
|
"light-openid",
|
||||||
"log",
|
"log",
|
||||||
|
@ -23,4 +23,6 @@ tokio = "1.44.1"
|
|||||||
futures-util = "0.3.31"
|
futures-util = "0.3.31"
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.140"
|
||||||
light-openid = "1.0.4"
|
light-openid = "1.0.4"
|
||||||
rand = "0.9.0"
|
rand = "0.9.0"
|
||||||
|
ipnet = { version = "2.11.0", features = ["serde"] }
|
||||||
|
lazy-regex = "3.4.1"
|
@ -10,20 +10,19 @@ CREATE TABLE users
|
|||||||
CREATE TABLE token
|
CREATE TABLE token
|
||||||
(
|
(
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
label VARCHAR(150) NOT NULL,
|
name VARCHAR(150) NOT NULL,
|
||||||
time_create BIGINT NOT NULL,
|
time_create BIGINT NOT NULL,
|
||||||
time_update BIGINT NOT NULL,
|
|
||||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||||
token_value VARCHAR(150) NOT NULL,
|
token_value VARCHAR(150) NOT NULL,
|
||||||
time_used BIGINT NOT NULL,
|
time_used BIGINT NOT NULL,
|
||||||
max_inactivity INTEGER,
|
max_inactivity INTEGER NOT NULL,
|
||||||
ip_restriction VARCHAR(50),
|
ip_net VARCHAR(50),
|
||||||
read_only BOOLEAN NOT NULL DEFAULT true,
|
read_only BOOLEAN NOT NULL DEFAULT true,
|
||||||
right_account BOOLEAN NOT NULL DEFAULT false,
|
right_account BOOLEAN NOT NULL DEFAULT false,
|
||||||
right_movement BOOLEAN NOT NULL DEFAULT false,
|
right_movement BOOLEAN NOT NULL DEFAULT false,
|
||||||
right_inbox BOOLEAN NOT NULL DEFAULT false,
|
right_inbox BOOLEAN NOT NULL DEFAULT false,
|
||||||
right_attachment BOOLEAN NOT NULL DEFAULT false,
|
right_attachment BOOLEAN NOT NULL DEFAULT false,
|
||||||
right_user BOOLEAN NOT NULL DEFAULT false
|
right_auth BOOLEAN NOT NULL DEFAULT false
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE attachment
|
CREATE TABLE attachment
|
||||||
@ -33,7 +32,7 @@ CREATE TABLE attachment
|
|||||||
mime_type VARCHAR(150) NOT NULL,
|
mime_type VARCHAR(150) NOT NULL,
|
||||||
sha512 VARCHAR(130) NOT NULL,
|
sha512 VARCHAR(130) NOT NULL,
|
||||||
file_size INTEGER NOT NULL,
|
file_size INTEGER NOT NULL,
|
||||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE RESTRICT
|
user_id INTEGER NOT NULL REFERENCES users ON DELETE SET NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE account
|
CREATE TABLE account
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
//! # Project constants
|
||||||
|
|
||||||
|
/// Length of generated tokens
|
||||||
|
pub const TOKENS_LEN: usize = 50;
|
||||||
|
|
||||||
/// Header used to authenticate API requests made using a token
|
/// Header used to authenticate API requests made using a token
|
||||||
pub const API_TOKEN_HEADER: &str = "X-Auth-Token";
|
pub const API_TOKEN_HEADER: &str = "X-Auth-Token";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ use crate::app_config::AppConfig;
|
|||||||
use crate::controllers::{HttpFailure, HttpResult};
|
use crate::controllers::{HttpFailure, HttpResult};
|
||||||
use crate::extractors::auth_extractor::{AuthExtractor, AuthenticatedMethod};
|
use crate::extractors::auth_extractor::{AuthExtractor, AuthenticatedMethod};
|
||||||
use crate::extractors::money_session::MoneySession;
|
use crate::extractors::money_session::MoneySession;
|
||||||
use crate::services::users_service;
|
use crate::services::{tokens_service, users_service};
|
||||||
use actix_remote_ip::RemoteIP;
|
use actix_remote_ip::RemoteIP;
|
||||||
use actix_web::{HttpResponse, web};
|
use actix_web::{HttpResponse, web};
|
||||||
use light_openid::primitives::OpenIDConfig;
|
use light_openid::primitives::OpenIDConfig;
|
||||||
@ -118,6 +118,10 @@ pub async fn sign_out(auth: AuthExtractor, session: MoneySession) -> HttpResult
|
|||||||
session.unset_current_user()?;
|
session.unset_current_user()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AuthenticatedMethod::Token(token) => {
|
||||||
|
tokens_service::delete(token.user_id(), token.id()).await?;
|
||||||
|
}
|
||||||
|
|
||||||
AuthenticatedMethod::Dev => {
|
AuthenticatedMethod::Dev => {
|
||||||
// Nothing to be done, user is always authenticated
|
// Nothing to be done, user is always authenticated
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ use std::error::Error;
|
|||||||
|
|
||||||
pub mod auth_controller;
|
pub mod auth_controller;
|
||||||
pub mod server_controller;
|
pub mod server_controller;
|
||||||
|
pub mod tokens_controller;
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum HttpFailure {
|
pub enum HttpFailure {
|
||||||
|
@ -8,15 +8,45 @@ pub async fn robots_txt() -> HttpResponse {
|
|||||||
.body("User-agent: *\nDisallow: /\n")
|
.body("User-agent: *\nDisallow: /\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
pub struct LenConstraints {
|
||||||
|
min: usize,
|
||||||
|
max: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LenConstraints {
|
||||||
|
pub fn new(min: usize, max: usize) -> Self {
|
||||||
|
Self { min, max }
|
||||||
|
}
|
||||||
|
pub fn not_empty(max: usize) -> Self {
|
||||||
|
Self { min: 1, max }
|
||||||
|
}
|
||||||
|
pub fn max_only(max: usize) -> Self {
|
||||||
|
Self { min: 0, max }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_str(&self, s: &str) -> bool {
|
||||||
|
s.len() >= self.min && s.len() <= self.max
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_u32(&self, v: u32) -> bool {
|
||||||
|
v >= self.min as u32 && v <= self.max as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize)]
|
#[derive(serde::Serialize)]
|
||||||
pub struct ServerConstraints {
|
pub struct ServerConstraints {
|
||||||
// TODO
|
pub token_name: LenConstraints,
|
||||||
|
pub token_ip_net: LenConstraints,
|
||||||
|
pub token_max_inactivity: LenConstraints,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ServerConstraints {
|
impl Default for ServerConstraints {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
// TODO
|
token_name: LenConstraints::new(5, 255),
|
||||||
|
token_ip_net: LenConstraints::max_only(44),
|
||||||
|
token_max_inactivity: LenConstraints::new(3600, 3600 * 24 * 365),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
87
moneymgr_backend/src/controllers/tokens_controller.rs
Normal file
87
moneymgr_backend/src/controllers/tokens_controller.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
use crate::controllers::HttpResult;
|
||||||
|
use crate::controllers::server_controller::ServerConstraints;
|
||||||
|
use crate::extractors::auth_extractor::{AuthExtractor, AuthenticatedMethod};
|
||||||
|
use crate::models::tokens::{Token, TokenID};
|
||||||
|
use crate::services::tokens_service;
|
||||||
|
use crate::services::tokens_service::NewTokenInfo;
|
||||||
|
use actix_web::{HttpResponse, web};
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct CreateTokenBody {
|
||||||
|
name: String,
|
||||||
|
ip_net: Option<ipnet::IpNet>,
|
||||||
|
max_inactivity: u32,
|
||||||
|
read_only: bool,
|
||||||
|
right_account: bool,
|
||||||
|
right_movement: bool,
|
||||||
|
right_inbox: bool,
|
||||||
|
right_attachment: bool,
|
||||||
|
right_auth: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
pub struct CreateTokenResult {
|
||||||
|
#[serde(flatten)]
|
||||||
|
info: Token,
|
||||||
|
token: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new token
|
||||||
|
pub async fn create(auth: AuthExtractor, req: web::Json<CreateTokenBody>) -> HttpResult {
|
||||||
|
if matches!(auth.method, AuthenticatedMethod::Token(_)) {
|
||||||
|
return Ok(HttpResponse::Forbidden()
|
||||||
|
.json("It is not allowed to create a token using another token!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let constraints = ServerConstraints::default();
|
||||||
|
|
||||||
|
if !lazy_regex::regex!("^[a-zA-Z0-9 :-]+$").is_match(&req.name) {
|
||||||
|
return Ok(HttpResponse::BadRequest().json("Token name contains invalid characters!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !constraints.token_name.check_str(&req.name) {
|
||||||
|
return Ok(HttpResponse::BadRequest().json("Invalid token name length!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !constraints
|
||||||
|
.token_max_inactivity
|
||||||
|
.check_u32(req.max_inactivity)
|
||||||
|
{
|
||||||
|
return Ok(HttpResponse::BadRequest().json("Invalid token max inactivity!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let token = tokens_service::create(NewTokenInfo {
|
||||||
|
user_id: auth.user_id(),
|
||||||
|
max_inactivity: req.max_inactivity,
|
||||||
|
ip_net: req.ip_net,
|
||||||
|
name: req.name.clone(),
|
||||||
|
read_only: req.read_only,
|
||||||
|
right_account: req.right_account,
|
||||||
|
right_movement: req.right_movement,
|
||||||
|
right_inbox: req.right_inbox,
|
||||||
|
right_attachment: req.right_attachment,
|
||||||
|
right_auth: req.right_auth,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::Created().json(CreateTokenResult {
|
||||||
|
token: token.token_value.to_string(),
|
||||||
|
info: token,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the list of tokens of the user
|
||||||
|
pub async fn get_list(auth: AuthExtractor) -> HttpResult {
|
||||||
|
Ok(HttpResponse::Ok().json(tokens_service::get_list_user(auth.user_id()).await?))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct TokenIDInPath {
|
||||||
|
id: TokenID,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete an API access token
|
||||||
|
pub async fn delete(auth: AuthExtractor, path: web::Path<TokenIDInPath>) -> HttpResult {
|
||||||
|
tokens_service::delete(auth.user_id(), path.id).await?;
|
||||||
|
Ok(HttpResponse::Accepted().finish())
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
use crate::app_config::AppConfig;
|
use crate::app_config::AppConfig;
|
||||||
use crate::extractors::money_session::MoneySession;
|
use crate::extractors::money_session::MoneySession;
|
||||||
use crate::models::users::User;
|
use crate::models::tokens::Token;
|
||||||
|
use crate::models::users::{User, UserID};
|
||||||
use crate::services::users_service;
|
use crate::services::users_service;
|
||||||
use actix_web::dev::Payload;
|
use actix_web::dev::Payload;
|
||||||
use actix_web::error::ErrorPreconditionFailed;
|
use actix_web::error::ErrorPreconditionFailed;
|
||||||
@ -12,6 +13,8 @@ pub enum AuthenticatedMethod {
|
|||||||
Cookie,
|
Cookie,
|
||||||
/// User is authenticated through command line, for debugging purposes only
|
/// User is authenticated through command line, for debugging purposes only
|
||||||
Dev,
|
Dev,
|
||||||
|
// TODO : token implementation
|
||||||
|
Token(Token),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Authentication extractor. Extract authentication information from request
|
/// Authentication extractor. Extract authentication information from request
|
||||||
@ -20,6 +23,13 @@ pub struct AuthExtractor {
|
|||||||
pub user: User,
|
pub user: User,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AuthExtractor {
|
||||||
|
/// Get current user ID
|
||||||
|
pub fn user_id(&self) -> UserID {
|
||||||
|
self.user.id()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromRequest for AuthExtractor {
|
impl FromRequest for AuthExtractor {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = futures_util::future::LocalBoxFuture<'static, Result<Self, Self::Error>>;
|
type Future = futures_util::future::LocalBoxFuture<'static, Result<Self, Self::Error>>;
|
||||||
|
@ -9,7 +9,7 @@ use actix_web::middleware::Logger;
|
|||||||
use actix_web::{App, HttpServer, web};
|
use actix_web::{App, HttpServer, web};
|
||||||
use moneymgr_backend::app_config::AppConfig;
|
use moneymgr_backend::app_config::AppConfig;
|
||||||
use moneymgr_backend::connections::{db_connection, s3_connection};
|
use moneymgr_backend::connections::{db_connection, s3_connection};
|
||||||
use moneymgr_backend::controllers::{auth_controller, server_controller};
|
use moneymgr_backend::controllers::*;
|
||||||
use moneymgr_backend::services::users_service;
|
use moneymgr_backend::services::users_service;
|
||||||
use moneymgr_backend::{constants, routines};
|
use moneymgr_backend::{constants, routines};
|
||||||
|
|
||||||
@ -90,6 +90,16 @@ async fn main() -> std::io::Result<()> {
|
|||||||
"/api/auth/sign_out",
|
"/api/auth/sign_out",
|
||||||
web::get().to(auth_controller::sign_out),
|
web::get().to(auth_controller::sign_out),
|
||||||
)
|
)
|
||||||
|
// Tokens controller
|
||||||
|
.route("/api/tokens", web::post().to(tokens_controller::create))
|
||||||
|
.route(
|
||||||
|
"/api/tokens/list",
|
||||||
|
web::get().to(tokens_controller::get_list),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/api/tokens/{id}",
|
||||||
|
web::delete().to(tokens_controller::delete),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.bind(AppConfig::get().listen_address.as_str())?
|
.bind(AppConfig::get().listen_address.as_str())?
|
||||||
.run()
|
.run()
|
||||||
|
@ -1 +1,2 @@
|
|||||||
|
pub mod tokens;
|
||||||
pub mod users;
|
pub mod users;
|
||||||
|
72
moneymgr_backend/src/models/tokens.rs
Normal file
72
moneymgr_backend/src/models/tokens.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
use crate::models::users::UserID;
|
||||||
|
use crate::schema::*;
|
||||||
|
use crate::utils::time_utils::time;
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use std::cmp::min;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct TokenID(pub i32);
|
||||||
|
|
||||||
|
#[derive(Default, Queryable, Debug, Clone, serde::Serialize)]
|
||||||
|
pub struct Token {
|
||||||
|
id: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub time_create: i64,
|
||||||
|
user_id: i32,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub token_value: String,
|
||||||
|
pub time_used: i64,
|
||||||
|
pub max_inactivity: i32,
|
||||||
|
ip_net: Option<String>,
|
||||||
|
pub read_only: bool,
|
||||||
|
pub right_account: bool,
|
||||||
|
pub right_movement: bool,
|
||||||
|
pub right_inbox: bool,
|
||||||
|
pub right_attachment: bool,
|
||||||
|
pub right_auth: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Token {
|
||||||
|
pub fn id(&self) -> TokenID {
|
||||||
|
TokenID(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn user_id(&self) -> UserID {
|
||||||
|
UserID(self.user_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ip_net(&self) -> Option<ipnet::IpNet> {
|
||||||
|
self.ip_net
|
||||||
|
.as_ref()
|
||||||
|
.map(|i| ipnet::IpNet::from_str(i).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shall_update_time_used(&self) -> bool {
|
||||||
|
let refresh_interval = min(600, self.max_inactivity / 10);
|
||||||
|
|
||||||
|
(self.time_used as u64) < time() - refresh_interval as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_expired(&self) -> bool {
|
||||||
|
(self.time_used + self.max_inactivity as i64) < time() as i64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Insertable)]
|
||||||
|
#[diesel(table_name = token)]
|
||||||
|
pub struct NewToken<'a> {
|
||||||
|
pub name: &'a str,
|
||||||
|
pub user_id: i32,
|
||||||
|
pub time_create: i64,
|
||||||
|
pub time_used: i64,
|
||||||
|
pub max_inactivity: i32,
|
||||||
|
pub ip_net: Option<&'a str>,
|
||||||
|
pub token_value: &'a str,
|
||||||
|
pub read_only: bool,
|
||||||
|
pub right_account: bool,
|
||||||
|
pub right_movement: bool,
|
||||||
|
pub right_inbox: bool,
|
||||||
|
pub right_attachment: bool,
|
||||||
|
pub right_auth: bool,
|
||||||
|
}
|
@ -19,6 +19,7 @@ pub async fn main_routine() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn exec_routine() -> anyhow::Result<()> {
|
async fn exec_routine() -> anyhow::Result<()> {
|
||||||
// TODO
|
// TODO : remove orphan attachment
|
||||||
|
// TODO : remove outdated tokens
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -55,22 +55,21 @@ diesel::table! {
|
|||||||
token (id) {
|
token (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
#[max_length = 150]
|
#[max_length = 150]
|
||||||
label -> Varchar,
|
name -> Varchar,
|
||||||
time_create -> Int8,
|
time_create -> Int8,
|
||||||
time_update -> Int8,
|
|
||||||
user_id -> Int4,
|
user_id -> Int4,
|
||||||
#[max_length = 150]
|
#[max_length = 150]
|
||||||
token_value -> Varchar,
|
token_value -> Varchar,
|
||||||
time_used -> Int8,
|
time_used -> Int8,
|
||||||
max_inactivity -> Nullable<Int4>,
|
max_inactivity -> Int4,
|
||||||
#[max_length = 50]
|
#[max_length = 50]
|
||||||
ip_restriction -> Nullable<Varchar>,
|
ip_net -> Nullable<Varchar>,
|
||||||
read_only -> Bool,
|
read_only -> Bool,
|
||||||
right_account -> Bool,
|
right_account -> Bool,
|
||||||
right_movement -> Bool,
|
right_movement -> Bool,
|
||||||
right_inbox -> Bool,
|
right_inbox -> Bool,
|
||||||
right_attachment -> Bool,
|
right_attachment -> Bool,
|
||||||
right_user -> Bool,
|
right_auth -> Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1,2 @@
|
|||||||
|
pub mod tokens_service;
|
||||||
pub mod users_service;
|
pub mod users_service;
|
||||||
|
108
moneymgr_backend/src/services/tokens_service.rs
Normal file
108
moneymgr_backend/src/services/tokens_service.rs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
use diesel::prelude::*;
|
||||||
|
|
||||||
|
use crate::connections::db_connection::db;
|
||||||
|
use crate::constants;
|
||||||
|
use crate::models::tokens::{NewToken, Token, TokenID};
|
||||||
|
use crate::models::users::UserID;
|
||||||
|
use crate::schema::token;
|
||||||
|
use crate::utils::rand_utils::rand_string;
|
||||||
|
use crate::utils::time_utils::time;
|
||||||
|
|
||||||
|
pub struct NewTokenInfo {
|
||||||
|
pub user_id: UserID,
|
||||||
|
pub name: String,
|
||||||
|
pub max_inactivity: u32,
|
||||||
|
pub ip_net: Option<ipnet::IpNet>,
|
||||||
|
pub read_only: bool,
|
||||||
|
pub right_account: bool,
|
||||||
|
pub right_movement: bool,
|
||||||
|
pub right_inbox: bool,
|
||||||
|
pub right_attachment: bool,
|
||||||
|
pub right_auth: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new token
|
||||||
|
pub async fn create(new_token: NewTokenInfo) -> anyhow::Result<Token> {
|
||||||
|
let ip_net = new_token.ip_net.map(|i| i.to_string());
|
||||||
|
let token = rand_string(constants::TOKENS_LEN);
|
||||||
|
let t = NewToken {
|
||||||
|
name: &new_token.name,
|
||||||
|
user_id: new_token.user_id.0,
|
||||||
|
time_create: time() as i64,
|
||||||
|
time_used: time() as i64,
|
||||||
|
max_inactivity: new_token.max_inactivity as i32,
|
||||||
|
read_only: new_token.read_only,
|
||||||
|
ip_net: ip_net.as_deref(),
|
||||||
|
token_value: &token,
|
||||||
|
right_auth: new_token.right_auth,
|
||||||
|
right_account: new_token.right_account,
|
||||||
|
right_movement: new_token.right_movement,
|
||||||
|
right_inbox: new_token.right_inbox,
|
||||||
|
right_attachment: new_token.right_attachment,
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = diesel::insert_into(token::table)
|
||||||
|
.values(&t)
|
||||||
|
.get_result(&mut db()?)?;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a single token by its id
|
||||||
|
pub async fn get_by_id(token_id: TokenID) -> anyhow::Result<Token> {
|
||||||
|
Ok(token::table
|
||||||
|
.filter(token::dsl::id.eq(token_id.0))
|
||||||
|
.get_result(&mut db()?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a single token by its name
|
||||||
|
pub fn get_by_name(name: &str) -> anyhow::Result<Token> {
|
||||||
|
Ok(token::table
|
||||||
|
.filter(token::dsl::name.eq(name))
|
||||||
|
.get_result(&mut db()?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a single token by its value
|
||||||
|
pub async fn get_by_value(value: &str) -> anyhow::Result<Token> {
|
||||||
|
Ok(token::table
|
||||||
|
.filter(token::dsl::token_value.eq(value))
|
||||||
|
.get_result(&mut db()?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the token of a user
|
||||||
|
pub async fn get_list_user(id: UserID) -> anyhow::Result<Vec<Token>> {
|
||||||
|
Ok(token::table
|
||||||
|
.filter(token::dsl::user_id.eq(id.0))
|
||||||
|
.get_results(&mut db()?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update last used value of a token
|
||||||
|
pub async fn update_time_used(token: &Token) -> anyhow::Result<()> {
|
||||||
|
diesel::update(token::dsl::token.filter(token::dsl::id.eq(token.id().0)))
|
||||||
|
.set(token::dsl::time_used.eq(time() as i64))
|
||||||
|
.execute(&mut db()?)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete the token of a user
|
||||||
|
pub async fn delete(user_id: UserID, token_id: TokenID) -> anyhow::Result<()> {
|
||||||
|
diesel::delete(
|
||||||
|
token::dsl::token.filter(
|
||||||
|
token::dsl::id
|
||||||
|
.eq(token_id.0)
|
||||||
|
.and(token::dsl::user_id.eq(user_id.0)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.execute(&mut db()?)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove outdated token
|
||||||
|
pub async fn cleanup() -> anyhow::Result<()> {
|
||||||
|
let query = format!(
|
||||||
|
"DELETE from token where last_used + max_inactivity < {};",
|
||||||
|
time()
|
||||||
|
);
|
||||||
|
diesel::sql_query(query).execute(&mut db()?)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user