diff --git a/core/src/errors/users.rs b/core/src/errors/users.rs index 240724c..8c07477 100644 --- a/core/src/errors/users.rs +++ b/core/src/errors/users.rs @@ -2,6 +2,9 @@ use eva::data; use crate::types::user; +#[data(error, display("there is no such auth session"))] +pub struct NoSuchAuthSession; + #[data(copy, display(name))] pub enum WhatTaken { Nickname, diff --git a/core/src/requests/users.rs b/core/src/requests/users.rs index 2b3d32e..fd731ca 100644 --- a/core/src/requests/users.rs +++ b/core/src/requests/users.rs @@ -1,6 +1,6 @@ ///! # Users functionality ///! -///! Subject for actions. +///! Subject of actions. use eva::{data, time::Timestamp}; use crate::{ @@ -8,6 +8,44 @@ use crate::{ types::{True, file, patch::Patch, session, user}, }; +pub mod begin_auth { + use super::*; + + #[data] + pub struct Args { + pub method: user::AuthoredAuth, + } + + #[data] + pub struct Ok { + pub auth_session: user::AuthSessionId, + } + + #[data(error, display("_"))] + pub enum Err {} +} + +pub mod finish_auth { + use super::*; + + #[data] + pub struct Args { + pub auth_session: user::AuthSessionId, + } + + #[data] + pub struct Ok { + pub session: session::Token, + pub method: user::AuthoredAuth, + } + + #[data(error)] + pub enum Err { + #[display("{_0}")] + NoSuchAuthSession(#[from] errors::NoSuchAuthSession), + } +} + pub mod check_auth { use super::*; diff --git a/core/src/service/authz.rs b/core/src/service/authz.rs index 97182da..a7db5c2 100644 --- a/core/src/service/authz.rs +++ b/core/src/service/authz.rs @@ -5,5 +5,6 @@ use crate::{service::AuxFut, types::session}; #[auto_impl(&mut)] pub trait AuthenticationMut: Send + Sync { fn authenticate(&mut self, session: session::Token) -> impl AuxFut<()>; + fn clear(&mut self); } diff --git a/core/src/service/users.rs b/core/src/service/users.rs index 7479ec6..bce7722 100644 --- a/core/src/service/users.rs +++ b/core/src/service/users.rs @@ -1,7 +1,9 @@ use eva::auto_impl; use crate::{ - requests::users::{check_auth, confirm_sign_up, get, sign_in, sign_up, update}, + requests::users::{ + begin_auth, check_auth, confirm_sign_up, finish_auth, get, sign_in, sign_up, update, + }, service::CallStep, }; @@ -15,6 +17,13 @@ pub trait UsersRef: Send + Sync { #[auto_impl(&mut)] pub trait UsersMut: UsersRef { + fn begin_auth( + &mut self, + ) -> impl CallStep; + fn finish_auth( + &mut self, + ) -> impl CallStep; + fn sign_in(&mut self) -> impl CallStep; fn sign_up(&mut self) -> impl CallStep; diff --git a/core/src/types/entity.rs b/core/src/types/entity.rs index 1d3269d..5716c05 100644 --- a/core/src/types/entity.rs +++ b/core/src/types/entity.rs @@ -84,6 +84,7 @@ pub enum Kind { Session = 0, File, Upload, + AuthSession, User, Author, Game, diff --git a/core/src/types/user.rs b/core/src/types/user.rs index e4012c3..e1982ce 100644 --- a/core/src/types/user.rs +++ b/core/src/types/user.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::{borrow::Cow, num::NonZeroI64}; use serde::{Serialize, de}; @@ -10,6 +10,37 @@ use eva::{ use crate::types::{entity, file, slug::Slug}; +entity::define_eid! { + pub struct AuthSessionId(AuthSession); +} + +#[str(newtype, copy)] +pub struct TelegramUsername(Slug<23>); + +#[data(copy, ord, display("tg:{_0}"))] +pub struct TelegramId(pub NonZeroI64); + +#[data] +pub struct TelegramAuth { + pub bot_username: TelegramUsername, +} + +#[data] +pub enum AuthoredAuth { + Telegram(#[from] TelegramAuth), +} + +#[data] +pub enum DirectAuth { + Password, +} + +#[data] +pub enum AuthMethod { + Authored(#[from] AuthoredAuth), + Direct(DirectAuth), +} + /// Miniature for the user. #[data] pub struct Mini { diff --git a/http/src/client/users.rs b/http/src/client/users.rs index 0639e5f..ab16640 100644 --- a/http/src/client/users.rs +++ b/http/src/client/users.rs @@ -2,7 +2,9 @@ use super::*; use eva::str::format_compact; -use viendesu_core::requests::users::{check_auth, confirm_sign_up, get, sign_in, sign_up, update}; +use viendesu_core::requests::users::{ + begin_auth, check_auth, confirm_sign_up, finish_auth, get, sign_in, sign_up, update, +}; use crate::requests::users as requests; @@ -24,6 +26,25 @@ impl UsersRef for Hidden<'_> { } impl UsersMut for Hidden<'_> { + fn begin_auth( + &mut self, + ) -> impl CallStep { + self.do_call(Method::POST, |begin_auth::Args { method }| { + ("/users/begin-auth".into(), requests::BeginAuth { method }) + }) + } + + fn finish_auth( + &mut self, + ) -> impl CallStep { + self.do_call(Method::POST, |finish_auth::Args { auth_session }| { + ( + format_compact!("/users/finish-auth/{auth_session}"), + requests::FinishAuth {}, + ) + }) + } + fn confirm_sign_up( &mut self, ) -> impl CallStep diff --git a/http/src/requests/users.rs b/http/src/requests/users.rs index 526bc15..29a38ac 100644 --- a/http/src/requests/users.rs +++ b/http/src/requests/users.rs @@ -8,6 +8,22 @@ use viendesu_core::{ use super::status_code; +#[data] +pub struct BeginAuth { + pub method: core::AuthoredAuth, +} + +impl_req!(BeginAuth => [reqs::begin_auth::Ok; reqs::begin_auth::Err]); +status_code::direct!(reqs::begin_auth::Ok => OK); +status_code::map!(reqs::begin_auth::Err => []); + +#[data] +pub struct FinishAuth {} + +impl_req!(FinishAuth => [reqs::finish_auth::Ok; reqs::finish_auth::Err]); +status_code::direct!(reqs::finish_auth::Ok => OK); +status_code::map!(reqs::finish_auth::Err => [NoSuchAuthSession]); + #[data] pub struct CheckAuth {} @@ -99,4 +115,5 @@ const _: () = { direct!(AlreadyTaken => BAD_REQUEST); direct!(NotFoundOrCompleted => BAD_REQUEST); direct!(InvalidSignUpToken => BAD_REQUEST); + direct!(NoSuchAuthSession => NOT_FOUND); }; diff --git a/http/src/server/mod.rs b/http/src/server/mod.rs index 18d8a2c..0680239 100644 --- a/http/src/server/mod.rs +++ b/http/src/server/mod.rs @@ -21,14 +21,13 @@ pub trait Types: Send + Sync + 'static { type Service: Service + Clone; } -pub async fn serve( - rx: SlaveRx, - config: config::Config, - router: axum::Router, -) -> eyre::Result<()> { +pub async fn serve(rx: SlaveRx, config: config::Config, router: axum::Router) -> eyre::Result<()> { // TODO: use it. _ = rx; - let config::Config { unencrypted, ssl: _ } = config; + let config::Config { + unencrypted, + ssl: _, + } = config; let unencrypted = unencrypted.expect("SSL-only currently is not supported"); if !unencrypted.enable { diff --git a/http/src/server/routes.rs b/http/src/server/routes.rs index 955144d..8c0bea7 100644 --- a/http/src/server/routes.rs +++ b/http/src/server/routes.rs @@ -27,9 +27,11 @@ pub fn make(router: RouterScope) -> RouterScope { // == Routes == fn users(router: RouterScope) -> RouterScope { - use crate::requests::users::{CheckAuth, ConfirmSignUp, Get, SignIn, SignUp, Update}; + use crate::requests::users::{ + BeginAuth, CheckAuth, ConfirmSignUp, FinishAuth, Get, SignIn, SignUp, Update, + }; use viendesu_core::requests::users::{ - check_auth, confirm_sign_up, get, sign_in, sign_up, update, + begin_auth, check_auth, confirm_sign_up, finish_auth, get, sign_in, sign_up, update, }; fn convert_update(u: Update) -> update::Update { @@ -59,7 +61,32 @@ fn users(router: RouterScope) -> RouterScope { }), ) .route( - "/check_auth", + "/begin-auth", + post(async |mut session: SessionOf, ctx: Ctx| { + session + .users_mut() + .begin_auth() + .call(begin_auth::Args { + method: ctx.request.method, + }) + .await + }), + ) + .route( + "/finish-auth/{authsessid}", + post( + async |mut session: SessionOf, mut ctx: Ctx| { + let auth_session: user::AuthSessionId = ctx.path().await?; + session + .users_mut() + .finish_auth() + .call(finish_auth::Args { auth_session }) + .await + }, + ), + ) + .route( + "/check-auth", get(async |mut session: SessionOf, _ctx: Ctx| { session.users().check_auth().call(check_auth::Args {}).await }),