Compare commits
2 Commits
07493b83a5
...
29bfa8c60e
Author | SHA1 | Date | |
---|---|---|---|
29bfa8c60e | |||
31aa9dc066 |
1
server/Cargo.lock
generated
1
server/Cargo.lock
generated
@ -2203,6 +2203,7 @@ checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.2.15",
|
"getrandom 0.2.15",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -23,7 +23,7 @@ bitflags = { version = "2.8", features = [ "serde" ] }
|
|||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
uuid = { version = "1.12", features = ["v4", "fast-rng"] }
|
uuid = { version = "1.12", features = ["v4", "fast-rng", "serde"] }
|
||||||
serde_json = "1.0.137"
|
serde_json = "1.0.137"
|
||||||
rand = "0.9"
|
rand = "0.9"
|
||||||
rand_chacha = "0.9"
|
rand_chacha = "0.9"
|
||||||
|
@ -4,7 +4,7 @@ CREATE TABLE "members" (
|
|||||||
full_name text NOT NULL,
|
full_name text NOT NULL,
|
||||||
registration_token text NOT NULL UNIQUE,
|
registration_token text NOT NULL UNIQUE,
|
||||||
diploma text,
|
diploma text,
|
||||||
swim_groups bigint NOT NULL,
|
groups bigint NOT NULL,
|
||||||
groups bigint NOT NULL
|
roles bigint NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,75 +1,60 @@
|
|||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use argon2::{
|
use argon2::{
|
||||||
password_hash::{rand_core::OsRng, PasswordHasher, SaltString},
|
password_hash::{rand_core::OsRng, PasswordHasher, SaltString},
|
||||||
Argon2, PasswordHash, PasswordVerifier,
|
Argon2, PasswordHash, PasswordVerifier,
|
||||||
};
|
};
|
||||||
use axum::{
|
use axum::http::{header, HeaderMap};
|
||||||
extract::FromRequestParts,
|
use chrono::Utc;
|
||||||
http::{request::Parts, StatusCode},
|
|
||||||
RequestPartsExt,
|
|
||||||
};
|
|
||||||
use axum_extra::{
|
|
||||||
extract::cookie::{Cookie, CookieJar},
|
|
||||||
headers::{authorization::Bearer, Authorization},
|
|
||||||
typed_header::TypedHeaderRejectionReason,
|
|
||||||
TypedHeader,
|
|
||||||
};
|
|
||||||
use bearer::verify_bearer;
|
|
||||||
pub use error::AuthError;
|
pub use error::AuthError;
|
||||||
use rand::distr::Alphanumeric;
|
use rand::distr::Alphanumeric;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use rand_chacha::ChaCha20Rng;
|
use rand_chacha::ChaCha20Rng;
|
||||||
|
use sqlx::PgPool;
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
|
||||||
use crate::database::model::User;
|
use crate::{
|
||||||
|
database::model::{Session, UserMember},
|
||||||
|
model::{member::Roles, User},
|
||||||
|
};
|
||||||
|
|
||||||
mod bearer;
|
|
||||||
mod error;
|
mod error;
|
||||||
mod scopes;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub async fn get_user_from_header(
|
||||||
pub struct Permissions<'a>(pub HashSet<&'a str>);
|
pool: &PgPool,
|
||||||
|
headers: &HeaderMap,
|
||||||
|
) -> Result<(Roles, User), AuthError> {
|
||||||
|
let bearer_value = headers.get(header::AUTHORIZATION);
|
||||||
|
let bearer_value = bearer_value
|
||||||
|
.ok_or_else(|| AuthError::InvalidToken)?
|
||||||
|
.to_str()
|
||||||
|
.map_err(|_| AuthError::InvalidToken)?;
|
||||||
|
|
||||||
// Middleware for getting permissions
|
let token = get_token_from_bearer(bearer_value)?;
|
||||||
impl<S> FromRequestParts<S> for Permissions<'_>
|
|
||||||
where
|
|
||||||
S: Send + Sync,
|
|
||||||
{
|
|
||||||
type Rejection = crate::Error;
|
|
||||||
|
|
||||||
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
let session = match Session::from_token(&pool, &token).await {
|
||||||
// First check if the request has a beaerer token to authenticate
|
Ok(s) => s,
|
||||||
match parts.extract::<TypedHeader<Authorization<Bearer>>>().await {
|
Err(_) => return Err(AuthError::InvalidToken),
|
||||||
Ok(bearer) => {
|
};
|
||||||
verify_bearer(bearer.token().to_string()).map_err(|_| AuthError::InvalidToken)?;
|
|
||||||
|
|
||||||
let permissions = Permissions {
|
if session.expires_at < Utc::now() {
|
||||||
0: HashSet::from(["root"]),
|
return Err(AuthError::InvalidToken);
|
||||||
};
|
}
|
||||||
|
|
||||||
return Ok(permissions);
|
let db_user = match crate::database::model::User::get(&pool, session.user_id).await {
|
||||||
}
|
Ok(u) => u,
|
||||||
Err(err) => match err.reason() {
|
Err(_) => return Err(AuthError::InvalidToken),
|
||||||
TypedHeaderRejectionReason::Missing => (),
|
};
|
||||||
TypedHeaderRejectionReason::Error(_err) => {
|
|
||||||
return Err(AuthError::InvalidToken.into())
|
|
||||||
}
|
|
||||||
_ => return Err(AuthError::Unexpected.into()),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
match parts.extract::<CookieJar>().await {
|
let roles = UserMember::get_roles(&pool, &db_user.user_id)
|
||||||
Ok(jar) => {
|
.await
|
||||||
if let Some(session_token) = jar.get("session_token") {
|
.unwrap_or(Roles::MEMBER);
|
||||||
// TODO: Implement function to retrieve user permissions
|
|
||||||
tracing::info!("{session_token:?}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(AuthError::Unauthorized.into())
|
Ok((roles, db_user.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_token_from_bearer(bearer: &str) -> Result<String, AuthError> {
|
||||||
|
match bearer.strip_prefix("Bearer ") {
|
||||||
|
Some(token) => Ok(token.to_string()),
|
||||||
|
None => return Err(AuthError::InvalidToken),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
pub fn verify_bearer(token: String) -> Result<(), ()> {
|
|
||||||
let env_api_token = dotenvy::var("API_TOKEN").map_err(|_| ())?;
|
|
||||||
|
|
||||||
match env_api_token == token {
|
|
||||||
true => Ok(()),
|
|
||||||
false => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
use crate::bitflags_serde_impl;
|
|
||||||
use bitflags::bitflags;
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct Scopes: u64 {
|
|
||||||
const USER_READ = 1 << 0;
|
|
||||||
const USER_WRITE = 1 << 1;
|
|
||||||
const USER_DELETE = 1 << 2;
|
|
||||||
|
|
||||||
const MEMBER_CREATE = 1 << 3;
|
|
||||||
const MEMBER_READ = 1 << 4;
|
|
||||||
const MEMBER_WRITE = 1 << 5;
|
|
||||||
const MEMBER_DELETE = 1 << 6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags_serde_impl!(Scopes, u64);
|
|
@ -2,7 +2,7 @@ use rand::distr::{Alphanumeric, SampleString};
|
|||||||
use sqlx::{PgPool, Postgres, QueryBuilder};
|
use sqlx::{PgPool, Postgres, QueryBuilder};
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
use crate::model::member::{Groups, SwimGroups};
|
use crate::model::member::{Groups, Roles};
|
||||||
|
|
||||||
#[derive(Debug, Validate, sqlx::FromRow)]
|
#[derive(Debug, Validate, sqlx::FromRow)]
|
||||||
pub struct Member {
|
pub struct Member {
|
||||||
@ -12,8 +12,8 @@ pub struct Member {
|
|||||||
pub full_name: String,
|
pub full_name: String,
|
||||||
pub registration_token: Option<String>,
|
pub registration_token: Option<String>,
|
||||||
pub diploma: Option<String>,
|
pub diploma: Option<String>,
|
||||||
pub swim_groups: SwimGroups,
|
|
||||||
pub groups: Groups,
|
pub groups: Groups,
|
||||||
|
pub roles: Roles,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Member {
|
impl Member {
|
||||||
@ -53,7 +53,7 @@ impl Member {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut query_builder = QueryBuilder::new(
|
let mut query_builder = QueryBuilder::new(
|
||||||
"INSERT INTO members(member_id, first_name, full_name, registration_token, diploma, swim_groups, groups) "
|
"INSERT INTO members(member_id, first_name, full_name, registration_token, diploma, groups, roles) "
|
||||||
);
|
);
|
||||||
|
|
||||||
query_builder.push_values(members.into_iter(), |mut b, member| {
|
query_builder.push_values(members.into_iter(), |mut b, member| {
|
||||||
@ -64,8 +64,8 @@ impl Member {
|
|||||||
b.push_bind(member.full_name);
|
b.push_bind(member.full_name);
|
||||||
b.push_bind(registration_token);
|
b.push_bind(registration_token);
|
||||||
b.push_bind(member.diploma);
|
b.push_bind(member.diploma);
|
||||||
b.push_bind(member.swim_groups.bits() as i64);
|
|
||||||
b.push_bind(member.groups.bits() as i64);
|
b.push_bind(member.groups.bits() as i64);
|
||||||
|
b.push_bind(member.roles.bits() as i64);
|
||||||
});
|
});
|
||||||
|
|
||||||
let query = query_builder.build();
|
let query = query_builder.build();
|
||||||
@ -86,14 +86,14 @@ impl Member {
|
|||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"
|
"
|
||||||
UPDATE ONLY members
|
UPDATE ONLY members
|
||||||
SET first_name = $1, full_name = $2, diploma = $3, swim_groups = $4, groups = $5
|
SET first_name = $1, full_name = $2, diploma = $3, groups = $4, roles = $5
|
||||||
WHERE member_id = $6
|
WHERE member_id = $6
|
||||||
",
|
",
|
||||||
member.first_name,
|
member.first_name,
|
||||||
member.full_name,
|
member.full_name,
|
||||||
member.diploma,
|
member.diploma,
|
||||||
member.swim_groups.bits() as i64,
|
|
||||||
member.groups.bits() as i64,
|
member.groups.bits() as i64,
|
||||||
|
member.roles.bits() as i64,
|
||||||
member.member_id
|
member.member_id
|
||||||
)
|
)
|
||||||
.execute(&mut **transaction)
|
.execute(&mut **transaction)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use sqlx::{PgPool, Postgres};
|
use sqlx::{PgPool, Postgres};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Session {
|
pub struct Session {
|
||||||
pub session_id: uuid::Uuid,
|
pub session_id: uuid::Uuid,
|
||||||
pub user_id: uuid::Uuid,
|
pub user_id: uuid::Uuid,
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use sqlx::{PgPool, Postgres};
|
use sqlx::{PgPool, Postgres};
|
||||||
|
|
||||||
|
use crate::model::member::Roles;
|
||||||
|
|
||||||
#[derive(validator::Validate)]
|
#[derive(validator::Validate)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub user_id: uuid::Uuid,
|
pub user_id: uuid::Uuid,
|
||||||
@ -44,6 +46,14 @@ impl User {
|
|||||||
|
|
||||||
Ok(user)
|
Ok(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get(transaction: &PgPool, user_id: uuid::Uuid) -> Result<Self, sqlx::Error> {
|
||||||
|
let user = sqlx::query_as!(Self, "SELECT * FROM users WHERE user_id = $1", user_id)
|
||||||
|
.fetch_one(transaction)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(user)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -73,4 +83,20 @@ impl UserMember {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_roles(pool: &PgPool, user_id: &uuid::Uuid) -> Result<Roles, sqlx::Error> {
|
||||||
|
let roles = sqlx::query_scalar!(
|
||||||
|
"
|
||||||
|
SELECT roles FROM users_members INNER JOIN members ON users_members.member_id = members.member_id AND users_members.user_id = $1;
|
||||||
|
",
|
||||||
|
user_id
|
||||||
|
).fetch_all(pool).await?;
|
||||||
|
|
||||||
|
let roles: Vec<Roles> = roles.into_iter().map(|r| r.into()).collect();
|
||||||
|
let roles = roles
|
||||||
|
.into_iter()
|
||||||
|
.fold(Roles::empty(), |acc, flag| acc | flag);
|
||||||
|
|
||||||
|
Ok(roles)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,21 +13,22 @@ pub struct Member {
|
|||||||
pub name: Name,
|
pub name: Name,
|
||||||
pub registration_token: Option<String>,
|
pub registration_token: Option<String>,
|
||||||
pub diploma: Option<String>,
|
pub diploma: Option<String>,
|
||||||
pub swim_groups: SwimGroups,
|
|
||||||
pub groups: Groups,
|
pub groups: Groups,
|
||||||
|
pub roles: Roles,
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
pub struct Groups: u64 {
|
pub struct Roles: u64 {
|
||||||
const NONE = 1 << 0;
|
const MEMBER = 1 << 0;
|
||||||
const KADER = 1 << 1;
|
const KADER = 1 << 1;
|
||||||
const ZWEMZAKEN = 1 << 2;
|
const ZWEMZAKEN = 1 << 2;
|
||||||
const WEDSTRIJDEN = 1 << 3;
|
const WEDSTRIJDEN = 1 << 3;
|
||||||
|
const ADMIN = 1 << 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
pub struct SwimGroups: u64 {
|
pub struct Groups: u64 {
|
||||||
const NONE = 1 << 0;
|
const NONE = 1 << 0;
|
||||||
|
|
||||||
const A1 = 1 << 1;
|
const A1 = 1 << 1;
|
||||||
@ -76,15 +77,15 @@ bitflags! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<i64> for SwimGroups {
|
impl From<i64> for Groups {
|
||||||
fn from(value: i64) -> Self {
|
fn from(value: i64) -> Self {
|
||||||
Self::from_bits(value as u64).unwrap_or(SwimGroups::NONE)
|
Self::from_bits(value as u64).unwrap_or(Groups::empty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<i64> for Groups {
|
impl From<i64> for Roles {
|
||||||
fn from(value: i64) -> Self {
|
fn from(value: i64) -> Self {
|
||||||
Self::from_bits(value as u64).unwrap_or(Groups::NONE)
|
Self::from_bits(value as u64).unwrap_or(Roles::MEMBER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,8 +100,8 @@ impl From<DbMember> for Member {
|
|||||||
},
|
},
|
||||||
registration_token: value.registration_token,
|
registration_token: value.registration_token,
|
||||||
diploma: value.diploma,
|
diploma: value.diploma,
|
||||||
swim_groups: value.swim_groups,
|
|
||||||
groups: value.groups,
|
groups: value.groups,
|
||||||
|
roles: value.roles,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,8 +114,8 @@ impl From<Member> for DbMember {
|
|||||||
full_name: value.name.full,
|
full_name: value.name.full,
|
||||||
registration_token: None,
|
registration_token: None,
|
||||||
diploma: value.diploma,
|
diploma: value.diploma,
|
||||||
swim_groups: value.swim_groups,
|
|
||||||
groups: value.groups,
|
groups: value.groups,
|
||||||
|
roles: value.roles,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ use chrono::{DateTime, Duration, Utc};
|
|||||||
|
|
||||||
use crate::auth::generate_session_token;
|
use crate::auth::generate_session_token;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Session {
|
pub struct Session {
|
||||||
pub session_id: uuid::Uuid,
|
pub session_id: uuid::Uuid,
|
||||||
pub user_id: uuid::Uuid,
|
pub user_id: uuid::Uuid,
|
||||||
|
@ -1,5 +1,19 @@
|
|||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub id: uuid::Uuid,
|
pub id: uuid::Uuid,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub admin: bool,
|
pub admin: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crate::database::model::User as DbUser;
|
||||||
|
impl From<DbUser> for User {
|
||||||
|
fn from(db_user: DbUser) -> Self {
|
||||||
|
Self {
|
||||||
|
id: db_user.user_id,
|
||||||
|
email: db_user.email,
|
||||||
|
admin: db_user.admin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{auth::Permissions, AppState};
|
use crate::{auth::get_user_from_header, model::User, AppState};
|
||||||
use axum::{extract::State, http::StatusCode, routing::get, Router};
|
use axum::{extract::State, http::HeaderMap, routing::get, Json, Router};
|
||||||
|
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod member;
|
pub mod member;
|
||||||
@ -14,10 +14,10 @@ pub fn routes() -> Router<AppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn root(
|
async fn root(
|
||||||
State(_state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
permissions: Permissions<'_>,
|
headers: HeaderMap,
|
||||||
) -> Result<String, (StatusCode, String)> {
|
) -> Result<Json<User>, crate::Error> {
|
||||||
tracing::info!("{:?}", permissions);
|
let (_roles, user) = get_user_from_header(&state.pool, &headers).await?;
|
||||||
|
|
||||||
Ok("Hello world".to_string())
|
Ok(Json(user))
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use axum::{extract::State, routing::post, Router};
|
use axum::{extract::State, routing::post, Router};
|
||||||
|
|
||||||
use crate::{auth::Permissions, AppState};
|
use crate::AppState;
|
||||||
|
|
||||||
pub mod migrate;
|
pub mod migrate;
|
||||||
|
|
||||||
@ -12,7 +12,6 @@ pub fn routes() -> Router<AppState> {
|
|||||||
|
|
||||||
pub async fn get_members<'a>(
|
pub async fn get_members<'a>(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
permissions: Permissions<'a>,
|
|
||||||
body: String,
|
body: String,
|
||||||
) -> Result<(), crate::Error> {
|
) -> Result<(), crate::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2,16 +2,17 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{FromRef, State},
|
extract::{FromRef, State},
|
||||||
|
http::HeaderMap,
|
||||||
Json,
|
Json,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
auth::{AuthError, Permissions},
|
auth::{get_user_from_header, AuthError},
|
||||||
database::model::Member as DbMember,
|
database::model::Member as DbMember,
|
||||||
model::{
|
model::{
|
||||||
member::{Groups, Name, SwimGroups},
|
member::{Groups, Name, Roles},
|
||||||
Member,
|
Member,
|
||||||
},
|
},
|
||||||
util::convert_vec,
|
util::convert_vec,
|
||||||
@ -20,10 +21,12 @@ use crate::{
|
|||||||
|
|
||||||
pub async fn migrate_request<'a>(
|
pub async fn migrate_request<'a>(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
permissions: Permissions<'a>,
|
headers: HeaderMap,
|
||||||
body: String,
|
body: String,
|
||||||
) -> Result<Json<MigrationResponse>, crate::Error> {
|
) -> Result<Json<MigrationResponse>, crate::Error> {
|
||||||
if !permissions.0.contains("root") {
|
let (roles, _user) = get_user_from_header(&state.pool, &headers).await?;
|
||||||
|
|
||||||
|
if !roles.contains(Roles::ADMIN) {
|
||||||
return Err(AuthError::NoPermssions.into());
|
return Err(AuthError::NoPermssions.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,13 +53,8 @@ pub async fn migrate_request<'a>(
|
|||||||
|
|
||||||
pub async fn migrate_confirm<'a>(
|
pub async fn migrate_confirm<'a>(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
permissions: Permissions<'a>,
|
|
||||||
body: String,
|
body: String,
|
||||||
) -> Result<(), crate::Error> {
|
) -> Result<(), crate::Error> {
|
||||||
if !permissions.0.contains("root") {
|
|
||||||
return Err(AuthError::NoPermssions.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
tracing::info!("Migration is confirmed");
|
tracing::info!("Migration is confirmed");
|
||||||
|
|
||||||
let count = match body.trim().parse::<u32>() {
|
let count = match body.trim().parse::<u32>() {
|
||||||
@ -119,7 +117,7 @@ struct Row {
|
|||||||
#[serde(rename = "E-mail")]
|
#[serde(rename = "E-mail")]
|
||||||
email: String,
|
email: String,
|
||||||
#[serde(rename = "Verenigingssporten")]
|
#[serde(rename = "Verenigingssporten")]
|
||||||
swim_groups: String,
|
groups: String,
|
||||||
#[serde(rename = "Diploma dropdown 1")]
|
#[serde(rename = "Diploma dropdown 1")]
|
||||||
diploma: Option<String>,
|
diploma: Option<String>,
|
||||||
}
|
}
|
||||||
@ -164,22 +162,22 @@ impl Row {
|
|||||||
members
|
members
|
||||||
}
|
}
|
||||||
|
|
||||||
fn swim_groups_parsed(&self) -> SwimGroups {
|
fn groups_parsed(&self) -> Groups {
|
||||||
let mut swim_groups: Vec<String> = Vec::new();
|
let mut groups: Vec<String> = Vec::new();
|
||||||
|
|
||||||
let group_parts: Vec<&str> = self.swim_groups.split(", ").collect();
|
let group_parts: Vec<&str> = self.groups.split(", ").collect();
|
||||||
|
|
||||||
for group in group_parts {
|
for group in group_parts {
|
||||||
let hour_parts: Vec<&str> = group.split(" - ").collect();
|
let hour_parts: Vec<&str> = group.split(" - ").collect();
|
||||||
|
|
||||||
if let Some(group) = hour_parts.get(1) {
|
if let Some(group) = hour_parts.get(1) {
|
||||||
swim_groups.push(group.to_uppercase())
|
groups.push(group.to_uppercase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let swim_groups_string = swim_groups.join("|");
|
let groups_string = groups.join("|");
|
||||||
|
|
||||||
bitflags::parser::from_str(&swim_groups_string).unwrap_or(SwimGroups::empty())
|
bitflags::parser::from_str(&groups_string).unwrap_or(Groups::empty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,8 +199,8 @@ impl Into<Member> for Row {
|
|||||||
name,
|
name,
|
||||||
registration_token: None,
|
registration_token: None,
|
||||||
diploma: self.diploma.clone(),
|
diploma: self.diploma.clone(),
|
||||||
swim_groups: self.swim_groups_parsed(),
|
groups: self.groups_parsed(),
|
||||||
groups: Groups::empty(),
|
roles: Roles::MEMBER,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -264,8 +262,8 @@ fn generate_diff(members_new: Vec<Member>, members_old: Vec<Member>) -> MembersD
|
|||||||
name: new_member.name.clone(),
|
name: new_member.name.clone(),
|
||||||
registration_token: old_member.registration_token,
|
registration_token: old_member.registration_token,
|
||||||
diploma: new_member.diploma.clone(),
|
diploma: new_member.diploma.clone(),
|
||||||
swim_groups: new_member.swim_groups.clone(),
|
groups: new_member.groups,
|
||||||
groups: old_member.groups,
|
roles: old_member.roles,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
members_remove.push(old_member);
|
members_remove.push(old_member);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user