Skip to content
Clone

refactor: remove unused modules and clean up imports for improved maintainability

ZhenYi committed
parent 44c6130 commit 13aafa3 Browse files

121 files changed

+1472 −4437

File tree 121
Modified app/src/main.rs View app/src/main.rs
@@ -67,10 +67,8 @@
6767 .ok()
6868 })
6969 .unwrap_or_default();
70 let mail = auth::email::MailPublisher::new(
71 queue::Publisher::new(conn.clone()),
72 email_pool.mail_topic,
73 );
70 let mail =
71 auth::email::MailPublisher::new(queue::Publisher::new(conn.clone()), email_pool.mail_topic);
7472 let mut state = export::AppState::with_identity(
7573 app_config,
7674 database,
@@ -141,17 +139,17 @@
141139 .wrap(auth::envelope::EnvelopeContext)
142140 .wrap(webapi::build_cors(&state.config.web))
143141 .wrap(from_fn(export::middleware::trace_request))
144 .wrap(from_fn(export::telemetry::http_telemetry))
145 .wrap(from_fn(auth::auth_session::revalidate::revalidate))
146 .wrap(from_fn(auth::auth_session::csrf::csrf))
147 .service(web::resource("/api/docs").route(web::get().to(|| async {
148 actix_web::HttpResponse::Found()
149 .insert_header((actix_web::http::header::LOCATION, "/api/docs/"))
150 .finish()
151 })))
152 .service(SwaggerUi::new("/api/docs/{_:.*}").url("/api/docs/openapi.json", doc.clone()))
153 .service(webapi::scope(state))
154 .default_service(web::to(auth::assets::serve))
142 .wrap(from_fn(export::telemetry::http_telemetry))
143 .wrap(from_fn(auth::share::session::revalidate::revalidate))
144 .wrap(from_fn(auth::share::session::csrf::csrf))
145 .service(web::resource("/api/docs").route(web::get().to(|| async {
146 actix_web::HttpResponse::Found()
147 .insert_header((actix_web::http::header::LOCATION, "/api/docs/"))
148 .finish()
149 })))
150 .service(SwaggerUi::new("/api/docs/{_:.*}").url("/api/docs/openapi.json", doc.clone()))
151 .service(webapi::scope(state))
152 .default_service(web::to(auth::assets::serve))
155153 })
156154 .disable_signals()
157155 .bind(addr)
Renamed lib/auth/src/internal/authority.rs → lib/auth/src/api/authority.rs View lib/auth/src/api/authority.rs
@@ -13,16 +13,16 @@
1313use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect};
1414use tonic::{Request, Response, Status};
1515
16use crate::AuthSvc;
16use crate::AuthService;
17use crate::api::{context, convert, error, store};
1718use crate::errors::AuthError;
18use crate::internal::{context, convert, error, store};
1919
2020pub struct AuthorityApi {
21 svc: AuthSvc,
21 svc: AuthService,
2222}
2323
2424impl AuthorityApi {
25 pub fn new(svc: AuthSvc) -> Self {
25 pub fn new(svc: AuthService) -> Self {
2626 Self { svc }
2727 }
2828
@@ -319,7 +319,7 @@
319319 if user_ids.is_empty() {
320320 return Ok(Response::new(BatchGetUserResponse { users: Vec::new() }));
321321 }
322 if user_ids.len() > crate::internal::store::MAX_BATCH_USER_IDS {
322 if user_ids.len() > crate::api::store::MAX_BATCH_USER_IDS {
323323 return Err(error::invalid_argument(
324324 "too many user ids in one batch",
325325 request_id,
@@ -430,7 +430,7 @@
430430 payload: ListUsersRequest,
431431 ) -> Result<Response<ListUsersResponse>, Status> {
432432 let filter = payload.filter.unwrap_or_default();
433 if filter.user_ids.len() > crate::internal::store::MAX_BATCH_USER_IDS {
433 if filter.user_ids.len() > crate::api::store::MAX_BATCH_USER_IDS {
434434 return Err(error::invalid_argument(
435435 "too many user ids in one filter",
436436 request_id,
Renamed lib/auth/src/internal/context.rs → lib/auth/src/api/context.rs View lib/auth/src/api/context.rs
@@ -12,7 +12,7 @@
1212use tonic::{Request, Status};
1313use uuid::Uuid;
1414
15use crate::internal::error;
15use crate::api::error;
1616
1717pub const RPC_KEY_METADATA: &str = "x-rpc-key";
1818pub const REQUEST_ID_METADATA: &str = "gitdata-request-id";
Renamed lib/auth/src/internal/convert.rs → lib/auth/src/api/convert.rs View lib/auth/src/api/convert.rs
@@ -3,7 +3,7 @@
33use chrono::{DateTime, NaiveDate, TimeZone, Utc};
44use prost_types::Timestamp;
55
6use crate::internal::store;
6use crate::api::store;
77
88pub const DEFAULT_PAGE_SIZE: i32 = 20;
99pub const MAX_PAGE_SIZE: i32 = 100;
Renamed lib/auth/src/internal/error.rs → lib/auth/src/api/error.rs View lib/auth/src/api/error.rs

No content changes.

Renamed lib/auth/src/internal/identity.rs → lib/auth/src/api/identity.rs View lib/auth/src/api/identity.rs
@@ -7,17 +7,17 @@
77use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
88use tonic::{Request, Response, Status};
99
10use crate::AuthSvc;
10use crate::AuthService;
11use crate::api::{context, error, store};
1112use crate::errors::{AuthError, AuthResult};
12use crate::internal::{context, error, store};
1313
1414#[derive(Clone)]
1515pub struct IdentityApi {
16 svc: AuthSvc,
16 svc: AuthService,
1717}
1818
1919impl IdentityApi {
20 pub fn new(svc: AuthSvc) -> Self {
20 pub fn new(svc: AuthService) -> Self {
2121 Self { svc }
2222 }
2323
@@ -40,7 +40,7 @@
4040}
4141
4242fn bot_public(user: &users::Model) -> crate::protos::auth::common::v1::UserPublic {
43 crate::internal::store::user_public(user, String::new(), None)
43 crate::api::store::user_public(user, String::new(), None)
4444}
4545
4646impl IdentityApi {
@@ -117,7 +117,7 @@
117117 if let Err(err) = store::drop_user_session_states(self.svc.cache(), user.id, keep).await {
118118 tracing::warn!(%err, user_id = user.id, "failed to drop revoked sessions");
119119 }
120 crate::me::service::invalidate_me_cache(self.svc.cache(), user.id).await;
120 crate::services::me::invalidate_me_cache(self.svc.cache(), user.id).await;
121121
122122 store::audit(
123123 db,
Renamed lib/auth/src/internal/mfa.rs → lib/auth/src/api/mfa.rs View lib/auth/src/api/mfa.rs
@@ -8,17 +8,17 @@
88use serde_json::json;
99use tonic::{Request, Response, Status};
1010
11use crate::AuthSvc;
11use crate::AuthService;
12use crate::api::{context, convert, error, store};
1213use crate::errors::{AuthError, AuthResult};
13use crate::internal::{context, convert, error, store};
1414
1515#[derive(Clone)]
1616pub struct MfaApi {
17 svc: AuthSvc,
17 svc: AuthService,
1818}
1919
2020impl MfaApi {
21 pub fn new(svc: AuthSvc) -> Self {
21 pub fn new(svc: AuthService) -> Self {
2222 Self { svc }
2323 }
2424
Renamed lib/auth/src/internal/organization.rs → lib/auth/src/api/organization.rs View lib/auth/src/api/organization.rs
@@ -7,9 +7,9 @@
77use tonic::{Request, Response, Status};
88use uuid::Uuid;
99
10use crate::AuthSvc;
10use crate::AuthService;
11use crate::api::{context, convert, error, store};
1112use crate::errors::{AuthError, AuthResult};
12use crate::internal::{context, convert, error, store};
1313use crate::models::space::{
1414 MessageNotifications as DomainNotifications, OrgJoinMethod as DomainJoin,
1515 OrgMemberRole as DomainRole, OrganizationJoinPolicy as DomainJoinPolicy,
@@ -17,12 +17,6 @@
1717 PermissionEffect as DomainEffect, organization_member_permissions, organization_member_roles,
1818 organization_members, organization_roles, organizations,
1919};
20use crate::organization::types::{
21 AddMemberBody, CreateOrganizationBody, CreateRoleBody, JoinRequestSummary, JoinRuleSummary,
22 MAX_BATCH_PERMISSIONS, PermissionGrant, SetMemberRoleBody, SetRolePermissionsBody,
23 UpdateBanBody, UpdateMemberBody, UpdateOrganizationBody,
24};
25use crate::organization::{member, role, service};
2620use crate::protos::auth::internal::v1::{
2721 GetOrganizationRequest as AdminGetOrganizationRequest,
2822 GetOrganizationResponse as AdminGetOrganizationResponse,
@@ -40,6 +34,12 @@
4034 OrganizationJoinMethod as OrgJoinMethodProto, OrganizationMemberRole as OrgMemberRoleProto,
4135 OrganizationStatus as OrganizationStatusProto, PermissionEffect as PermissionEffectProto, *,
4236};
37use crate::services::organization::types::{
38 AddMemberBody, CreateOrganizationBody, CreateRoleBody, JoinRequestSummary, JoinRuleSummary,
39 MAX_BATCH_PERMISSIONS, PermissionGrant, SetMemberRoleBody, SetRolePermissionsBody,
40 UpdateBanBody, UpdateMemberBody, UpdateOrganizationBody,
41};
42use crate::services::organization::{member, role, service};
4343
4444fn uuid_of(value: &str, request_id: Option<&str>) -> Result<Uuid, Status> {
4545 Uuid::parse_str(value.trim())
@@ -98,7 +98,9 @@
9898 }
9999}
100100
101fn org_info(summary: crate::organization::types::OrganizationSummary) -> OrganizationInfo {
101fn org_info(
102 summary: crate::services::organization::types::OrganizationSummary,
103) -> OrganizationInfo {
102104 OrganizationInfo {
103105 id: summary.id.to_string(),
104106 slug: summary.slug,
@@ -124,7 +126,9 @@
124126 }
125127}
126128
127fn member_info(summary: crate::organization::types::MemberSummary) -> OrganizationMemberInfo {
129fn member_info(
130 summary: crate::services::organization::types::MemberSummary,
131) -> OrganizationMemberInfo {
128132 OrganizationMemberInfo {
129133 id: summary.id.to_string(),
130134 organization_id: summary.organization_id.to_string(),
@@ -159,7 +163,7 @@
159163 }
160164}
161165
162fn role_info(summary: crate::organization::types::RoleSummary) -> OrganizationRoleInfo {
166fn role_info(summary: crate::services::organization::types::RoleSummary) -> OrganizationRoleInfo {
163167 OrganizationRoleInfo {
164168 id: summary.id.to_string(),
165169 organization_id: summary.organization_id.to_string(),
@@ -189,7 +193,9 @@
189193 }
190194}
191195
192fn invite_info(summary: crate::organization::types::InviteSummary) -> OrganizationInviteInfo {
196fn invite_info(
197 summary: crate::services::organization::types::InviteSummary,
198) -> OrganizationInviteInfo {
193199 OrganizationInviteInfo {
194200 id: summary.id.to_string(),
195201 organization_id: summary.organization_id.to_string(),
@@ -233,11 +239,11 @@
233239
234240#[derive(Clone)]
235241pub struct OrganizationApi {
236 svc: AuthSvc,
242 svc: AuthService,
237243}
238244
239245impl OrganizationApi {
240 pub fn new(svc: AuthSvc) -> Self {
246 pub fn new(svc: AuthService) -> Self {
241247 Self { svc }
242248 }
243249
@@ -2303,11 +2309,11 @@
23032309/// 平台侧管理面。与成员视角的服务分开,因为这里的判定与 actor 的组织身份无关。
23042310#[derive(Clone)]
23052311pub struct OrganizationAdminApi {
2306 svc: AuthSvc,
2312 svc: AuthService,
23072313}
23082314
23092315impl OrganizationAdminApi {
2310 pub fn new(svc: AuthSvc) -> Self {
2316 pub fn new(svc: AuthService) -> Self {
23112317 Self { svc }
23122318 }
23132319
@@ -2744,7 +2750,7 @@
27442750}
27452751
27462752async fn member_id_of(
2747 svc: &AuthSvc,
2753 svc: &AuthService,
27482754 organization_id: Uuid,
27492755 member_id: &str,
27502756 user_id: i64,
Renamed lib/auth/src/internal/session.rs → lib/auth/src/api/session.rs View lib/auth/src/api/session.rs
@@ -7,20 +7,20 @@
77use tonic::{Request, Response, Status};
88use uuid::Uuid;
99
10use crate::AuthSvc;
10use crate::AuthService;
11use crate::api::{context, convert, error, store};
1112use crate::errors::AuthError;
12use crate::internal::{context, convert, error, store};
1313
1414pub const MAX_LIST_PAGE: u64 = 100;
1515pub const SESSION_LIST_MAX: usize = 200;
1616
1717#[derive(Clone)]
1818pub struct SessionApi {
19 svc: AuthSvc,
19 svc: AuthService,
2020}
2121
2222impl SessionApi {
23 pub fn new(svc: AuthSvc) -> Self {
23 pub fn new(svc: AuthService) -> Self {
2424 Self { svc }
2525 }
2626
@@ -277,7 +277,7 @@
277277 .exec(self.svc.db().write())
278278 .await
279279 .map_err(|err| error::status(&AuthError::from(err), request_id))?;
280 crate::me::service::invalidate_me_cache(self.svc.cache(), request.user_id).await;
280 crate::services::me::invalidate_me_cache(self.svc.cache(), request.user_id).await;
281281 security_version = store::load_user(self.svc.db(), request.user_id)
282282 .await
283283 .map_err(|err| error::status(&err, request_id))?
@@ -332,7 +332,7 @@
332332 revoked_sessions = revoked.rows_affected as u32;
333333 }
334334
335 crate::me::service::invalidate_me_cache(self.svc.cache(), request.user_id).await;
335 crate::services::me::invalidate_me_cache(self.svc.cache(), request.user_id).await;
336336 store::audit(
337337 self.svc.db(),
338338 request.user_id,
Renamed lib/auth/src/internal/ssh_key.rs → lib/auth/src/api/ssh_key.rs View lib/auth/src/api/ssh_key.rs
@@ -8,17 +8,17 @@
88use tonic::{Request, Response, Status};
99use uuid::Uuid;
1010
11use crate::AuthSvc;
11use crate::AuthService;
12use crate::api::{context, convert, error, store};
1213use crate::errors::AuthError;
13use crate::internal::{context, convert, error, store};
1414
1515#[derive(Clone)]
1616pub struct SshKeyApi {
17 svc: AuthSvc,
17 svc: AuthService,
1818}
1919
2020impl SshKeyApi {
21 pub fn new(svc: AuthSvc) -> Self {
21 pub fn new(svc: AuthService) -> Self {
2222 Self { svc }
2323 }
2424
@@ -109,7 +109,7 @@
109109 request_id,
110110 ));
111111 }
112 crate::ssh_key::service::parse_public_key(public_key)
112 crate::services::ssh_key::service::parse_public_key(public_key)
113113 .map_err(|err| error::status(&err, request_id))?
114114 .1
115115 } else {
@@ -166,7 +166,7 @@
166166 .svc
167167 .add_ssh_key_for_user(
168168 request.user_id,
169 crate::ssh_key::types::SshKeyAddRequest {
169 crate::services::ssh_key::types::SshKeyAddRequest {
170170 name: request.name,
171171 description: Some(request.description).filter(|text| !text.is_empty()),
172172 public_key: request.public_key,
@@ -313,7 +313,7 @@
313313 .map_err(|err| error::status(&AuthError::from(err), request_id))?;
314314
315315 if request.bump_security_version {
316 crate::me::service::invalidate_me_cache(self.svc.cache(), request.user_id).await;
316 crate::services::me::invalidate_me_cache(self.svc.cache(), request.user_id).await;
317317 }
318318
319319 if let Err(err) = store::audit(
Renamed lib/auth/src/internal/store.rs → lib/auth/src/api/store.rs View lib/auth/src/api/store.rs
@@ -7,9 +7,9 @@
77use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
88use uuid::Uuid;
99
10use crate::api::context::RequestMeta;
11use crate::api::convert;
1012use crate::errors::{AuthError, AuthResult};
11use crate::internal::context::RequestMeta;
12use crate::internal::convert;
1313use crate::share::user as user_store;
1414
1515pub const ALLOWED_APP_SCOPES: [&str; 12] = [
Modified lib/auth/src/handlers/account/accept_agreement.rs View lib/auth/src/handlers/account/accept_agreement.rs
@@ -5,8 +5,8 @@
55use serde::{Deserialize, Serialize};
66use utoipa::ToSchema;
77
8use crate::api_error::ApiError;
9use crate::service::AuthService;
8use crate::AuthService;
9use crate::errors::ApiError;
1010use crate::services::agreements::AcceptAgreementResp;
1111use ::session::share::session;
1212
Modified lib/auth/src/handlers/account/account_deletion.rs View lib/auth/src/handlers/account/account_deletion.rs
@@ -6,8 +6,8 @@
66use time::OffsetDateTime;
77use utoipa::ToSchema;
88
9use crate::api_error::{ApiError, AuthBusinessError};
10use crate::service::AuthService;
9use crate::AuthService;
10use crate::errors::{ApiError, AuthBusinessError};
1111
1212#[derive(Debug, Clone, Serialize, ToSchema)]
1313pub struct AccountDeletionDto {
Modified lib/auth/src/handlers/account/agreement_page.rs View lib/auth/src/handlers/account/agreement_page.rs
@@ -1,9 +1,8 @@
11use export::http::{HeaderMap, Html, Path, State};
22use i18n::catalog;
33
4use crate::api_error::ApiError;
5use crate::service::AuthService;
6use crate::templates;
4use crate::errors::ApiError;
5use crate::{AuthService, pages};
76
87pub async fn agreement_page(
98 State(service): State<AuthService>,
@@ -21,10 +20,10 @@
2120 let doc = service.list_agreements().into_iter().find(|d| d.key == key);
2221 match doc {
2322 Some(doc) => {
24 let html_content = templates::render_markdown(&doc.content_md);
25 Ok(templates::page_agreement(&doc, &html_content, locale))
23 let html_content = pages::render_markdown(&doc.content_md);
24 Ok(pages::page_agreement(&doc, &html_content, locale))
2625 }
27 None => Ok(templates::page_message(
26 None => Ok(pages::page_message(
2827 false,
2928 catalog::agreement_not_found_title(locale),
3029 catalog::agreement_not_found_body(locale),
Modified lib/auth/src/handlers/account/create_report.rs View lib/auth/src/handlers/account/create_report.rs
@@ -3,8 +3,8 @@
33use ::session::Session;
44use export::rest::{AppResponseData, NoData};
55
6use crate::api_error::ApiError;
7use crate::service::AuthService;
6use crate::AuthService;
7use crate::errors::ApiError;
88use crate::services::reports::{CreateReportCmd, ReportDto};
99use ::session::share::session;
1010
Modified lib/auth/src/handlers/account/delete_notification.rs View lib/auth/src/handlers/account/delete_notification.rs
@@ -3,8 +3,8 @@
33use export::rest::{AppResponseData, NoData};
44use uuid::Uuid;
55
6use crate::api_error::ApiError;
7use crate::service::AuthService;
6use crate::AuthService;
7use crate::errors::ApiError;
88use ::session::share::session;
99
1010#[utoipa::path(
Modified lib/auth/src/handlers/account/devices.rs View lib/auth/src/handlers/account/devices.rs
@@ -1,3 +1,5 @@
1use export::AuthApi;
2
13use crate::protos::auth::common::v1::PageRequest;
24use ::session::Session;
35use ::session::share::session;
@@ -10,8 +12,8 @@
1012use utoipa::ToSchema;
1113use uuid::Uuid;
1214
13use crate::api_error::ApiError;
14use crate::service::AuthService;
15use crate::AuthService;
16use crate::errors::ApiError;
1517use crate::share::rpc;
1618
1719const SESSION_PAGE_SIZE: i32 = 100;
Modified lib/auth/src/handlers/account/get_preferences.rs View lib/auth/src/handlers/account/get_preferences.rs
@@ -2,8 +2,8 @@
22use export::http::State;
33use export::rest::{AppResponseData, NoData};
44
5use crate::api_error::ApiError;
6use crate::service::AuthService;
5use crate::AuthService;
6use crate::errors::ApiError;
77use crate::services::preferences::UserPreferencesDto;
88use ::session::share::session;
99
Modified lib/auth/src/handlers/account/get_user.rs View lib/auth/src/handlers/account/get_user.rs
@@ -3,8 +3,8 @@
33use export::http::{Path, State};
44use export::rest::{AppResponseData, NoData};
55
6use crate::api_error::ApiError;
7use crate::service::AuthService;
6use crate::AuthService;
7use crate::errors::ApiError;
88use crate::share::account::types::UserPublic;
99
1010#[utoipa::path(
Modified lib/auth/src/handlers/account/list_agreements.rs View lib/auth/src/handlers/account/list_agreements.rs
@@ -1,8 +1,8 @@
11use export::http::State;
22use export::rest::{AppResponseData, NoData};
33
4use crate::api_error::ApiError;
5use crate::service::AuthService;
4use crate::AuthService;
5use crate::errors::ApiError;
66use crate::share::account::types::AgreementDoc;
77
88#[utoipa::path(
Modified lib/auth/src/handlers/account/list_notifications.rs View lib/auth/src/handlers/account/list_notifications.rs
@@ -4,8 +4,8 @@
44use serde::Deserialize;
55use utoipa::IntoParams;
66
7use crate::api_error::ApiError;
8use crate::service::AuthService;
7use crate::AuthService;
8use crate::errors::ApiError;
99use crate::services::notifications::NotificationDto;
1010use ::session::share::session;
1111
Modified lib/auth/src/handlers/account/mark_notifications_read.rs View lib/auth/src/handlers/account/mark_notifications_read.rs
@@ -4,8 +4,8 @@
44use serde::Deserialize;
55use uuid::Uuid;
66
7use crate::api_error::ApiError;
8use crate::service::AuthService;
7use crate::AuthService;
8use crate::errors::ApiError;
99use ::session::share::session;
1010
1111#[derive(Debug, Deserialize, utoipa::ToSchema)]
Modified lib/auth/src/handlers/account/me.rs View lib/auth/src/handlers/account/me.rs
@@ -3,8 +3,8 @@
33use serde::Serialize;
44use utoipa::ToSchema;
55
6use crate::api_error::ApiError;
7use crate::service::AuthService;
6use crate::AuthService;
7use crate::errors::ApiError;
88use crate::services::profile::{ProfileDto, UserMeDto};
99use ::session::Session;
1010use ::session::share::session;
@@ -31,6 +31,6 @@
3131 session: Session,
3232) -> Result<AppResponseData<MeResp>, ApiError> {
3333 let uid = session::require_uid(&session)?;
34 let (user, profile) = service.me(uid).await.map_err(ApiError::from)?;
34 let (user, profile) = service.account_me(uid).await.map_err(ApiError::from)?;
3535 Ok(AppResponseData::ok(MeResp { user, profile }))
3636}
Modified lib/auth/src/handlers/account/password.rs View lib/auth/src/handlers/account/password.rs
@@ -1,3 +1,5 @@
1use export::AuthApi;
2
13use ::session::Session;
24use ::session::share::session;
35use export::http::{HeaderMap, Json, State};
@@ -5,8 +7,8 @@
57use serde::Deserialize;
68use utoipa::ToSchema;
79
8use crate::api_error::ApiError;
9use crate::service::AuthService;
10use crate::AuthService;
11use crate::errors::ApiError;
1012use crate::share::rpc;
1113
1214#[derive(Debug, Deserialize, ToSchema)]
Modified lib/auth/src/handlers/account/sessions.rs View lib/auth/src/handlers/account/sessions.rs
@@ -1,3 +1,5 @@
1use export::AuthApi;
2
13use crate::protos::auth::common::v1::{PageRequest, SessionInfo};
24use crate::protos::auth::v1::ListUserSessionsResponse;
35use ::session::Session;
@@ -10,8 +12,8 @@
1012use utoipa::ToSchema;
1113use uuid::Uuid;
1214
13use crate::api_error::ApiError;
14use crate::service::AuthService;
15use crate::AuthService;
16use crate::errors::ApiError;
1517use crate::share::rpc;
1618
1719const SESSION_PAGE_SIZE: i32 = 100;
Modified lib/auth/src/handlers/account/two_factor.rs View lib/auth/src/handlers/account/two_factor.rs
@@ -1,3 +1,5 @@
1use export::AuthApi;
2
13use crate::protos::auth::common::v1::MfaStatus;
24use ::session::Session;
35use ::session::share::session;
@@ -6,8 +8,8 @@
68use serde::{Deserialize, Serialize};
79use utoipa::ToSchema;
810
9use crate::api_error::ApiError;
10use crate::service::AuthService;
11use crate::AuthService;
12use crate::errors::ApiError;
1113use crate::share::rpc;
1214
1315#[derive(Debug, Serialize, ToSchema)]
Modified lib/auth/src/handlers/account/unread_notifications_count.rs View lib/auth/src/handlers/account/unread_notifications_count.rs
@@ -4,8 +4,8 @@
44use serde::Serialize;
55use utoipa::ToSchema;
66
7use crate::api_error::ApiError;
8use crate::service::AuthService;
7use crate::AuthService;
8use crate::errors::ApiError;
99use ::session::share::session;
1010
1111#[derive(Debug, Clone, Serialize, ToSchema)]
Modified lib/auth/src/handlers/account/update_preferences.rs View lib/auth/src/handlers/account/update_preferences.rs
@@ -3,8 +3,8 @@
33use ::session::Session;
44use export::rest::{AppResponseData, NoData};
55
6use crate::api_error::ApiError;
7use crate::service::AuthService;
6use crate::AuthService;
7use crate::errors::ApiError;
88use crate::services::preferences::{UpdatePreferencesCmd, UserPreferencesDto};
99use ::session::share::session;
1010
Modified lib/auth/src/handlers/account/update_profile.rs View lib/auth/src/handlers/account/update_profile.rs
@@ -3,8 +3,8 @@
33use ::session::Session;
44use export::rest::{AppResponseData, NoData};
55
6use crate::api_error::ApiError;
7use crate::service::AuthService;
6use crate::AuthService;
7use crate::errors::ApiError;
88use crate::services::profile::UpdateProfileCmd;
99use ::session::share::session;
1010
Modified lib/auth/src/handlers/account/users.rs View lib/auth/src/handlers/account/users.rs
@@ -5,8 +5,8 @@
55use serde::Deserialize;
66use utoipa::IntoParams;
77
8use crate::api_error::ApiError;
9use crate::service::AuthService;
8use crate::AuthService;
9use crate::errors::ApiError;
1010use crate::share::account::types::UserPublic;
1111
1212#[derive(Debug, Deserialize, IntoParams)]
Deleted lib/auth/src/handlers/identity/challenge/handler.rs
@@ -1,37 +0,0 @@
1use crate::session::{RequestId, Session};
2use actix_web::web;
3
4use crate::envelope::{Envelope, NoData};
5use crate::errors::AuthError;
6use crate::handlers::identity::challenge::types::ChallengeResponse;
7
8#[utoipa::path(
9 post,
10 path = "/api/v1/auth/challenge",
11 tag = "auth",
12 operation_id = "auth_challenge",
13 summary = "签发工作量证明挑战",
14 description = "把挑战写进当前会话并返回。客户端需要为 `context` 找出一枚满足 `difficulty`(前导零比特数)的 nonce;`pow.v1` 算法与前端求解器一致。",
15 responses(
16 (
17 status = 200,
18 description = "挑战已签发并写入会话",
19 body = Envelope<ChallengeResponse>,
20 example = json!({"code":200,"message":"success","data":{"context":"3xJk9Qp2mV7sLbT1rZa8nHc4wDf6YgU0","difficulty":16,"issued_at":1767139200,"expires_at":1767139500}})
21 ),
22 (
23 status = 500,
24 description = "`internal`(挑战无法写入会话)/ `cache` / `session` / `serialization`",
25 body = Envelope<NoData>,
26 example = json!({"code": "internal", "message": "internal server error"})
27 )
28 )
29)]
30pub async fn challenge(
31 svc: web::Data<crate::AuthSvc>,
32 session: Session,
33 request_id: RequestId,
34) -> Result<Envelope<ChallengeResponse>, AuthError> {
35 let challenge = svc.challenge(session, request_id.0).await?;
36 Ok(Envelope::ok(challenge))
37}
Deleted lib/auth/src/handlers/identity/challenge/mod.rs
@@ -1,5 +0,0 @@
1pub mod handler;
2pub mod service;
3pub mod types;
4
5pub use types::{ChallengeResponse, PowProofRequest, SESSION_CHALLENGE};
Deleted lib/auth/src/handlers/identity/challenge/service.rs
@@ -1,50 +0,0 @@
1use crate::events::{Labels, label};
2use crate::session::Session;
3
4use crate::AuthSvc;
5use crate::errors::AuthResult;
6use crate::handlers::identity::challenge::types::{ChallengeResponse, PowProofRequest};
7
8impl AuthSvc {
9 #[tracing::instrument(skip_all, fields(request_id = %request_id, client_ip = ?session.client_ip().ok().flatten()))]
10 pub async fn challenge(
11 &self,
12 session: Session,
13 request_id: String,
14 ) -> AuthResult<ChallengeResponse> {
15 let response = crate::pow::store::issue(&session, self.pow()).await?;
16 self.emit_labeled(
17 label::AUTH_CHALLENGE_CREATE,
18 Labels::new()
19 .with_opt(label::USER_ID, session.user_id().ok().flatten())
20 .with(label::REQUEST_ID, &request_id),
21 &response,
22 Some(request_id.as_str()),
23 )
24 .await;
25 Ok(response)
26 }
27
28 #[tracing::instrument(skip_all, fields(request_id = %request_id, client_ip = ?session.client_ip().ok().flatten()))]
29 pub async fn challenge_verify_pow(
30 &self,
31 session: Session,
32 proof: &PowProofRequest,
33 expected_payload: &[u8],
34 request_id: String,
35 ) -> AuthResult<()> {
36 crate::pow::store::verify(&session, self.cache(), proof, expected_payload).await?;
37 self.emit_labeled(
38 label::AUTH_CHALLENGE_USED,
39 Labels::new()
40 .with_opt(label::USER_ID, session.user_id().ok().flatten())
41 .with(label::REQUEST_ID, &request_id),
42 serde_json::json!({
43 "user_id": session.user_id().ok().flatten(),
44 }),
45 Some(request_id.as_str()),
46 )
47 .await;
48 Ok(())
49 }
50}
Deleted lib/auth/src/handlers/identity/challenge/types.rs
@@ -1,18 +0,0 @@
1use serde::{Deserialize, Serialize};
2use utoipa::ToSchema;
3
4pub const SESSION_CHALLENGE: &str = "challenge";
5
6#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
7pub struct ChallengeResponse {
8 pub context: String,
9 pub difficulty: u32,
10 pub issued_at: u64,
11 pub expires_at: u64,
12}
13
14#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
15pub struct PowProofRequest {
16 pub nonce: u64,
17 pub payload: Vec<u8>,
18}
Deleted lib/auth/src/handlers/identity/login/handler.rs
@@ -1,124 +0,0 @@
1use crate::session::{RequestId, Session};
2use actix_web::web;
3
4use crate::envelope::{Envelope, NoData};
5use crate::errors::AuthError;
6
7use crate::handlers::identity::login::types::{
8 LoginCodeRequest, LoginCodeResponse, LoginCodeVerifyRequest, LoginVerifyRequest,
9 UserLoginRequest, UserLoginResponse,
10};
11
12#[utoipa::path(
13 post,
14 path = "/api/v1/auth/login",
15 tag = "auth",
16 operation_id = "auth_login",
17 summary = "口令登录",
18 description = "用口令登录。`identity` 可以是用户名、邮箱(大小写不敏感)或用户编号,`password_hash` 是客户端先算好的口令摘要,工作量证明的载荷必须是 `identity`。账号启用了多因子时返回 `mfa_required: true` 与 `challenge`,此时会话尚未绑定,需再调 `/api/v1/auth/login/verify`。",
19 request_body = UserLoginRequest,
20 responses(
21 (status = 200, description = "登录成功;`mfa_required` 为真时还需二次验证", body = Envelope<UserLoginResponse>, example = json!({"code":200,"message":"success","data":{"user_id":1024,"username":"ada","displayname":"Ada","mfa_required":false,"challenge":null}})),
22 (status = 400, description = "工作量证明过期、载荷不匹配或请求不合法(`expired`、`invalid_proof`、`invalid_payload`、`malformed_request`)", body = Envelope<NoData>, example = json!({"code": "expired", "message": "challenge expired"})),
23 (status = 401, description = "身份或口令不正确(`invalid_credentials`)", body = Envelope<NoData>, example = json!({"code": "invalid_credentials", "message": "invalid credentials"})),
24 (status = 403, description = "账号被禁止登录,原因见 `message`(`login_forbidden`)", body = Envelope<NoData>, example = json!({"code": "login_forbidden", "message": "account is banned"})),
25 (status = 429, description = "失败次数超出配额(`rate_limited`)", body = Envelope<NoData>, example = json!({"code": "rate_limited", "message": "too many attempts"})),
26 (status = 500, description = "服务端故障(`internal`、`database`、`cache`、`session`、`serialization`)", body = Envelope<NoData>, example = json!({"code": "internal", "message": "internal server error"}))
27 )
28)]
29pub async fn login(
30 svc: web::Data<crate::AuthSvc>,
31 session: Session,
32 body: web::Json<UserLoginRequest>,
33 request_id: RequestId,
34) -> Result<Envelope<UserLoginResponse>, AuthError> {
35 let response = svc.login(session, body.into_inner(), request_id.0).await?;
36 Ok(Envelope::ok(response))
37}
38
39#[utoipa::path(
40 post,
41 path = "/api/v1/auth/login/code",
42 tag = "auth",
43 operation_id = "auth_login_code",
44 summary = "发送邮箱登录验证码",
45 description = "向账号邮箱发送 6 位登录验证码,`expires_in_secs` 为有效期。工作量证明的载荷必须是 `email`。同一账号的发码间隔受 `email_code_rate_limit_secs` 限制。",
46 request_body = LoginCodeRequest,
47 responses(
48 (status = 200, description = "验证码已发送", body = Envelope<LoginCodeResponse>, example = json!({"code":200,"message":"success","data":{"expires_in_secs":600}})),
49 (status = 400, description = "邮箱格式不合法或工作量证明过期、载荷不匹配(`invalid_email`、`expired`、`invalid_proof`、`invalid_payload`、`malformed_request`)", body = Envelope<NoData>, example = json!({"code": "invalid_email", "message": "invalid email address"})),
50 (status = 401, description = "邮箱未注册或账号不可用(`invalid_credentials`)", body = Envelope<NoData>, example = json!({"code": "invalid_credentials", "message": "invalid credentials"})),
51 (status = 403, description = "账号被禁止登录,原因见 `message`(`login_forbidden`)", body = Envelope<NoData>, example = json!({"code": "login_forbidden", "message": "account is banned"})),
52 (status = 429, description = "发码过于频繁(`rate_limited`)", body = Envelope<NoData>, example = json!({"code": "rate_limited", "message": "too many requests"})),
53 (status = 500, description = "服务端故障,含邮件发送失败(`internal`、`database`、`cache`、`session`、`email`、`serialization`)", body = Envelope<NoData>, example = json!({"code": "internal", "message": "internal server error"}))
54 )
55)]
56pub async fn login_code(
57 svc: web::Data<crate::AuthSvc>,
58 session: Session,
59 body: web::Json<LoginCodeRequest>,
60 request_id: RequestId,
61) -> Result<Envelope<LoginCodeResponse>, AuthError> {
62 let response = svc
63 .login_code(session, body.into_inner(), request_id.0)
64 .await?;
65 Ok(Envelope::ok(response))
66}
67
68#[utoipa::path(
69 post,
70 path = "/api/v1/auth/login/code/verify",
71 tag = "auth",
72 operation_id = "auth_login_code_verify",
73 summary = "邮箱验证码换会话",
74 description = "用邮箱登录验证码换会话。工作量证明的载荷必须是 `email`。验证码一旦核对通过即作废,连续输错 `max_verify_attempts` 次后需重新申请。账号启用了多因子时返回 `mfa_required: true` 与 `challenge`。",
75 request_body = LoginCodeVerifyRequest,
76 responses(
77 (status = 200, description = "登录成功;`mfa_required` 为真时还需二次验证", body = Envelope<UserLoginResponse>, example = json!({"code":200,"message":"success","data":{"user_id":1024,"username":"ada","displayname":"Ada","mfa_required":false,"challenge":null}})),
78 (status = 400, description = "验证码不正确或已过期,或邮箱格式、工作量证明不合法(`invalid_email_code`、`invalid_email`、`expired`、`invalid_proof`、`invalid_payload`、`malformed_request`)", body = Envelope<NoData>, example = json!({"code": "invalid_email_code", "message": "invalid email code"})),
79 (status = 401, description = "邮箱未注册或账号不可用(`invalid_credentials`)", body = Envelope<NoData>, example = json!({"code": "invalid_credentials", "message": "invalid credentials"})),
80 (status = 403, description = "账号被禁止登录,原因见 `message`(`login_forbidden`)", body = Envelope<NoData>, example = json!({"code": "login_forbidden", "message": "account is banned"})),
81 (status = 429, description = "验证码尝试次数超限或请求过频(`email_code_attempts_exceeded`、`rate_limited`)", body = Envelope<NoData>, example = json!({"code": "email_code_attempts_exceeded", "message": "too many attempts"})),
82 (status = 500, description = "服务端故障(`internal`、`database`、`cache`、`session`、`serialization`)", body = Envelope<NoData>, example = json!({"code": "internal", "message": "internal server error"}))
83 )
84)]
85pub async fn login_code_verify(
86 svc: web::Data<crate::AuthSvc>,
87 session: Session,
88 body: web::Json<LoginCodeVerifyRequest>,
89 request_id: RequestId,
90) -> Result<Envelope<UserLoginResponse>, AuthError> {
91 let response = svc
92 .login_code_verify(session, body.into_inner(), request_id.0)
93 .await?;
94 Ok(Envelope::ok(response))
95}
96
97#[utoipa::path(
98 post,
99 path = "/api/v1/auth/login/verify",
100 tag = "auth",
101 operation_id = "auth_login_verify",
102 summary = "二次验证换会话",
103 description = "用 6 位 TOTP 动态码或一枚未使用的恢复码完成二次验证并绑定会话。`challenge` 必须与 `/api/v1/auth/login` 或 `/api/v1/auth/login/code/verify` 返回的一致,挑战有效期 300 秒,同一动态码时间步不能重复使用,恢复码用掉即作废。",
104 request_body = LoginVerifyRequest,
105 responses(
106 (status = 200, description = "登录成功,会话已绑定", body = Envelope<UserLoginResponse>, example = json!({"code":200,"message":"success","data":{"user_id":1024,"username":"ada","displayname":"Ada","mfa_required":false,"challenge":null}})),
107 (status = 400, description = "挑战过期或对不上、动态码错误、动态码时间步已用过、恢复码无效(`expired`、`malformed_request`、`totp_code_invalid`、`totp_step_reused`、`recovery_code_invalid`)", body = Envelope<NoData>, example = json!({"code": "totp_code_invalid", "message": "invalid totp code"})),
108 (status = 401, description = "会话里没有待验证挑战、账号不可用或多因子记录已被撤销(`unauthenticated`、`invalid_credentials`、`mfa_required`)", body = Envelope<NoData>, example = json!({"code": "mfa_required", "message": "multi-factor authentication required"})),
109 (status = 403, description = "账号被禁止登录,原因见 `message`(`login_forbidden`)", body = Envelope<NoData>, example = json!({"code": "login_forbidden", "message": "account is banned"})),
110 (status = 429, description = "尝试次数超限或恢复码已用尽(`rate_limited`、`recovery_codes_exhausted`)", body = Envelope<NoData>, example = json!({"code": "rate_limited", "message": "too many attempts"})),
111 (status = 500, description = "服务端故障(`internal`、`database`、`cache`、`session`、`serialization`)", body = Envelope<NoData>, example = json!({"code": "internal", "message": "internal server error"}))
112 )
113)]
114pub async fn login_verify(
115 svc: web::Data<crate::AuthSvc>,
116 session: Session,
117 body: web::Json<LoginVerifyRequest>,
118 request_id: RequestId,
119) -> Result<Envelope<UserLoginResponse>, AuthError> {
120 let response = svc
121 .login_verify(session, body.into_inner(), request_id.0)
122 .await?;
123 Ok(Envelope::ok(response))
124}
Deleted lib/auth/src/handlers/identity/login/mod.rs
@@ -1,8 +0,0 @@
1pub mod handler;
2pub mod service;
3pub mod types;
4
5pub use types::{
6 LoginCodeRequest, LoginCodeResponse, LoginCodeVerifyRequest, LoginVerifyRequest,
7 UserLoginRequest, UserLoginResponse,
8};
Deleted lib/auth/src/handlers/identity/login/types.rs
@@ -1,68 +0,0 @@
1use serde::{Deserialize, Serialize};
2use utoipa::ToSchema;
3
4use crate::handlers::identity::challenge::types::PowProofRequest;
5
6pub const SESSION_LOGIN_MFA: &str = "login_mfa";
7pub const HASH_KEY_SHA256_V1: &str = "sha256-v1";
8pub const LOGIN_ATTEMPTS_MAX: u64 = 5;
9pub const LOGIN_ATTEMPTS_WINDOW_SECS: u64 = 300;
10pub const MFA_CHALLENGE_TTL_SECS: u64 = 300;
11
12#[derive(Clone, Deserialize, Serialize, ToSchema)]
13pub struct UserLoginRequest {
14 pub identity: String,
15 pub password_hash: String,
16 pub pow_payload: PowProofRequest,
17}
18
19impl std::fmt::Debug for UserLoginRequest {
20 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 formatter
22 .debug_struct("UserLoginRequest")
23 .field("identity", &self.identity)
24 .field("password_hash", &"<redacted>")
25 .field("pow_payload", &self.pow_payload)
26 .finish()
27 }
28}
29
30#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
31pub struct UserLoginResponse {
32 pub user_id: i64,
33 pub username: String,
34 pub displayname: Option<String>,
35 pub mfa_required: bool,
36 pub challenge: Option<String>,
37}
38
39#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
40pub struct LoginCodeRequest {
41 pub email: String,
42 pub pow_payload: PowProofRequest,
43}
44
45#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
46pub struct LoginCodeResponse {
47 pub expires_in_secs: u64,
48}
49
50#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
51pub struct LoginCodeVerifyRequest {
52 pub email: String,
53 pub email_code: String,
54 pub pow_payload: PowProofRequest,
55}
56
57#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
58pub struct LoginVerifyRequest {
59 pub challenge: String,
60 pub code: String,
61}
62
63#[derive(Clone, Debug, Deserialize, Serialize)]
64pub struct LoginMfaChallenge {
65 pub user_id: i64,
66 pub challenge: String,
67 pub expires_at: u64,
68}
Deleted lib/auth/src/handlers/identity/logout/handler.rs
@@ -1,27 +0,0 @@
1use crate::session::{RequestId, Session};
2use actix_web::web;
3
4use crate::envelope::{Envelope, NoData};
5use crate::errors::AuthError;
6use crate::handlers::identity::logout::types::LogoutResponse;
7
8#[utoipa::path(
9 post,
10 path = "/api/v1/auth/session/logout",
11 tag = "auth",
12 operation_id = "auth_logout",
13 summary = "登出",
14 description = "销毁当前会话,并清掉该用户的 `/api/v1/auth/me` 缓存。匿名调用同样成功,此时 `user_id` 为 `null`。",
15 responses(
16 (status = 200, description = "会话已销毁", body = Envelope<LogoutResponse>, example = json!({"code":200,"message":"success","data":{"user_id":1024}})),
17 (status = 500, description = "服务端故障(`internal`、`database`、`cache`、`session`、`serialization`)", body = Envelope<NoData>, example = json!({"code": "internal", "message": "internal server error"}))
18 )
19)]
20pub async fn logout(
21 svc: web::Data<crate::AuthSvc>,
22 session: Session,
23 request_id: RequestId,
24) -> Result<Envelope<LogoutResponse>, AuthError> {
25 let response = svc.logout(session, request_id.0).await?;
26 Ok(Envelope::ok(response))
27}
Deleted lib/auth/src/handlers/identity/logout/mod.rs
@@ -1,5 +0,0 @@
1pub mod handler;
2pub mod service;
3pub mod types;
4
5pub use types::LogoutResponse;
Deleted lib/auth/src/handlers/identity/logout/types.rs
@@ -1,7 +0,0 @@
1use serde::{Deserialize, Serialize};
2use utoipa::ToSchema;
3
4#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
5pub struct LogoutResponse {
6 pub user_id: Option<i64>,
7}
Deleted lib/auth/src/handlers/identity/me/mod.rs
@@ -1 +0,0 @@
1pub mod handler;
Deleted lib/auth/src/handlers/identity/organization/mod.rs
@@ -1 +0,0 @@
1pub mod handler;
Deleted lib/auth/src/handlers/identity/password_reset/handler.rs
@@ -1,121 +0,0 @@
1use crate::session::{RequestId, Session};
2use actix_web::web;
3
4use crate::envelope::{Envelope, NoData};
5use crate::errors::AuthError;
6
7use crate::handlers::identity::password_reset::types::{
8 PasswordResetConfirmRequest, PasswordResetConfirmResponse, PasswordResetRequest,
9 PasswordResetResponse,
10};
11
12#[utoipa::path(
13 post,
14 path = "/api/v1/auth/password/reset",
15 tag = "auth",
16 operation_id = "auth_password_reset_request",
17 summary = "申请密码重置",
18 description = "校验工作量证明(payload = 邮箱的 UTF-8 字节)后,作废该用户其余未消费的重置令牌,签发新令牌(长度 `reset_token_bytes`,有效期 `reset_token_ttl_secs`),库内只存 `sha256_hex(token)` 且 `hash_key_id = \"sha256-v1\"`,同时把摘要写入缓存键 `auth:code:reset:{email}`,发信限流键 `auth:rate:reset:{email}`。",
19 request_body = PasswordResetRequest,
20 responses(
21 (
22 status = 200,
23 description = "申请已受理;是否真的发信不对外披露",
24 body = Envelope<PasswordResetResponse>,
25 example = json!({"code":200,"message":"success","data":{"expires_in_secs":1800}})
26 ),
27 (
28 status = 400,
29 description = "`invalid_email` / `expired` / `invalid_payload`(payload 与邮箱不一致)/ `invalid_proof`",
30 body = Envelope<NoData>,
31 example = json!({"code": "invalid_email", "message": "email is invalid"})
32 ),
33 (
34 status = 429,
35 description = "`rate_limited`(同一邮箱申请过于频繁)",
36 body = Envelope<NoData>,
37 example = json!({"code": "rate_limited", "message": "too many requests, retry after 60s"})
38 ),
39 (
40 status = 500,
41 description = "`email`(投递失败)/ `database` / `cache` / `session` / `serialization` / `internal`",
42 body = Envelope<NoData>,
43 example = json!({"code": "internal", "message": "internal server error"})
44 )
45 )
46)]
47pub async fn password_reset_request(
48 svc: web::Data<crate::AuthSvc>,
49 session: Session,
50 body: web::Json<PasswordResetRequest>,
51 request_id: RequestId,
52) -> Result<Envelope<PasswordResetResponse>, AuthError> {
53 let user_agent = session.user_agent()?;
54 let ip_addr = session.client_ip()?;
55 let response = svc
56 .request_password_reset(
57 session,
58 body.into_inner(),
59 user_agent,
60 ip_addr,
61 request_id.0,
62 )
63 .await?;
64 Ok(Envelope::ok(response))
65}
66
67/// 并与 `security_version` 绑定;成功后作废该用户其余未消费的重置令牌并递增 `security_version`。
68#[utoipa::path(
69 post,
70 path = "/api/v1/auth/password/reset/confirm",
71 tag = "auth",
72 operation_id = "auth_password_reset",
73 summary = "确认密码重置",
74 description = "校验工作量证明(payload = `token` 的 UTF-8 字节)与缓存键 `auth:code:reset:{email}`(尝试上限 `max_verify_attempts`,错误计入 `invalid_email_code`)后,条件消费数据库令牌行(`consumed_at` 单次占用),Argon2 重哈希新口令,更新 `password_hash`/`password_updated_at`/`password_must_change`/`security_version + 1`,作废其余令牌并记 `PasswordChange` 审计事件。",
75 request_body = PasswordResetConfirmRequest,
76 responses(
77 (
78 status = 200,
79 description = "口令已改写;旧口令与其余重置令牌失效",
80 body = Envelope<PasswordResetConfirmResponse>,
81 example = json!({"code":200,"message":"success","data":{"user_id":1234567}})
82 ),
83 (
84 status = 400,
85 description = "`invalid_reset_token`(令牌不存在/已消费/已作废/已过期/账号不可用或凭据代数不匹配)/ `invalid_email_code`(缓存内摘要不匹配或已过期)/ `invalid_password` / `expired` / `invalid_payload` / `invalid_proof`",
86 body = Envelope<NoData>,
87 example = json!({"code": "invalid_reset_token", "message": "password reset token is invalid"})
88 ),
89 (
90 status = 429,
91 description = "`email_code_attempts_exceeded`(令牌尝试次数用尽)",
92 body = Envelope<NoData>,
93 example = json!({"code": "email_code_attempts_exceeded", "message": "too many invalid email verification attempts"})
94 ),
95 (
96 status = 500,
97 description = "`password_hash`(口令重哈希失败)/ `database` / `cache` / `session` / `serialization` / `internal`",
98 body = Envelope<NoData>,
99 example = json!({"code": "internal", "message": "internal server error"})
100 )
101 )
102)]
103pub async fn password_reset(
104 svc: web::Data<crate::AuthSvc>,
105 session: Session,
106 body: web::Json<PasswordResetConfirmRequest>,
107 request_id: RequestId,
108) -> Result<Envelope<PasswordResetConfirmResponse>, AuthError> {
109 let user_agent = session.user_agent()?;
110 let ip_addr = session.client_ip()?;
111 let response = svc
112 .reset_password(
113 session,
114 body.into_inner(),
115 user_agent,
116 ip_addr,
117 request_id.0,
118 )
119 .await?;
120 Ok(Envelope::ok(response))
121}
Deleted lib/auth/src/handlers/identity/password_reset/mod.rs
@@ -1,8 +0,0 @@
1pub mod handler;
2pub mod service;
3pub mod types;
4
5pub use types::{
6 PasswordResetConfirmRequest, PasswordResetConfirmResponse, PasswordResetRequest,
7 PasswordResetResponse,
8};
Deleted lib/auth/src/handlers/identity/password_reset/types.rs
@@ -1,38 +0,0 @@
1use serde::{Deserialize, Serialize};
2use utoipa::ToSchema;
3
4use crate::handlers::identity::challenge::types::PowProofRequest;
5
6#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
7pub struct PasswordResetRequest {
8 pub email: String,
9 pub pow_payload: PowProofRequest,
10}
11
12#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
13pub struct PasswordResetResponse {
14 pub expires_in_secs: u64,
15}
16
17#[derive(Clone, Deserialize, Serialize, ToSchema)]
18pub struct PasswordResetConfirmRequest {
19 pub token: String,
20 pub password_hash: String,
21 pub pow_payload: PowProofRequest,
22}
23
24impl std::fmt::Debug for PasswordResetConfirmRequest {
25 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 formatter
27 .debug_struct("PasswordResetConfirmRequest")
28 .field("token", &"<redacted>")
29 .field("password_hash", &"<redacted>")
30 .field("pow_payload", &self.pow_payload)
31 .finish()
32 }
33}
34
35#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
36pub struct PasswordResetConfirmResponse {
37 pub user_id: i64,
38}
Deleted lib/auth/src/handlers/identity/register/age.rs
@@ -1,17 +0,0 @@
1use chrono::{Datelike, NaiveDate};
2
3use crate::errors::{AuthError, AuthResult};
4
5pub const EARLIEST_BIRTH_YEAR: i32 = 1900;
6
7pub fn parse_birth_date(raw: &str, min_age: u8) -> AuthResult<NaiveDate> {
8 let parsed =
9 NaiveDate::parse_from_str(raw, "%Y-%m-%d").map_err(|_| AuthError::InvalidBirthDate)?;
10 if parsed.format("%Y-%m-%d").to_string() != raw {
11 return Err(AuthError::InvalidBirthDate);
12 }
13 if parsed.year() < EARLIEST_BIRTH_YEAR {
14 return Err(AuthError::InvalidBirthDate);
15 }
16 crate::share::validate::validate_age(Some(parsed), min_age)
17}
Deleted lib/auth/src/handlers/identity/register/handler.rs
@@ -1,129 +0,0 @@
1use crate::session::{RequestId, Session};
2use actix_web::web;
3
4use crate::envelope::{Envelope, NoData};
5use crate::errors::AuthError;
6
7use crate::handlers::identity::register::types::{
8 RegisterCodeRequest, RegisterCodeResponse, UserRegisterRequest, UserRegisterResponse,
9};
10
11#[utoipa::path(
12 post,
13 path = "/api/v1/auth/register/code",
14 tag = "auth",
15 operation_id = "auth_register_code",
16 summary = "发送注册验证码",
17 description = "校验工作量证明(payload = 邮箱的 UTF-8 字节)与邮箱格式后,把 6 位验证码写入缓存并寄出。缓存键 `auth:code:register:{email}`,有效期由 `verify_code_ttl_secs` 决定,发码限流键 `auth:rate:register:{email}`。",
18 request_body = RegisterCodeRequest,
19 responses(
20 (
21 status = 200,
22 description = "验证码已写入缓存并寄出",
23 body = Envelope<RegisterCodeResponse>,
24 example = json!({"code":200,"message":"success","data":{"expires_in_secs":600}})
25 ),
26 (
27 status = 400,
28 description = "`invalid_email`(邮箱格式)/ `expired`(挑战过期)/ `invalid_payload`(payload 与邮箱不一致)/ `invalid_proof`(未满足难度)",
29 body = Envelope<NoData>,
30 example = json!({"code": "invalid_email", "message": "email is invalid"})
31 ),
32 (
33 status = 409,
34 description = "`email_taken`(邮箱已注册)",
35 body = Envelope<NoData>,
36 example = json!({"code": "email_taken", "message": "email is already taken"})
37 ),
38 (
39 status = 429,
40 description = "`rate_limited`(同一邮箱发码过于频繁)",
41 body = Envelope<NoData>,
42 example = json!({"code": "rate_limited", "message": "too many requests, retry after 60s"})
43 ),
44 (
45 status = 500,
46 description = "`email`(投递失败)/ `cache` / `session` / `serialization` / `internal`",
47 body = Envelope<NoData>,
48 example = json!({"code": "internal", "message": "internal server error"})
49 )
50 )
51)]
52pub async fn register_code(
53 svc: web::Data<crate::AuthSvc>,
54 session: Session,
55 body: web::Json<RegisterCodeRequest>,
56 request_id: RequestId,
57) -> Result<Envelope<RegisterCodeResponse>, AuthError> {
58 let response = svc
59 .send_register_code(session, body.into_inner(), request_id.0)
60 .await?;
61 Ok(Envelope::ok(response))
62}
63
64#[utoipa::path(
65 post,
66 path = "/api/v1/auth/register",
67 tag = "auth",
68 operation_id = "auth_register",
69 summary = "提交注册",
70 description = "校验工作量证明(payload = 用户名的 UTF-8 字节)、用户名/展示名/出生日期/邮箱、邮箱验证码(键 `auth:code:register:{email}`,尝试上限 `max_verify_attempts`)后,Argon2 重哈希口令并创建用户与初始档案。成功后写会话(user_id、security_version、renew)并记审计事件。",
71 request_body = UserRegisterRequest,
72 responses(
73 (
74 status = 200,
75 description = "注册成功且已登录",
76 body = Envelope<UserRegisterResponse>,
77 example = json!({"code":200,"message":"success","data":{"user_id":1234567,"username":"alice","displayname":"Alice"}})
78 ),
79 (
80 status = 400,
81 description = "`invalid_username` / `invalid_displayname` / `invalid_email` / `invalid_birth_date` / `invalid_password` / `invalid_email_code`(验证码错误或已过期)/ `expired` / `invalid_payload` / `invalid_proof`",
82 body = Envelope<NoData>,
83 example = json!({"code": "invalid_email_code", "message": "email verification code is invalid"})
84 ),
85 (
86 status = 403,
87 description = "`under_minimum_age`(未满最小年龄)",
88 body = Envelope<NoData>,
89 example = json!({"code": "under_minimum_age", "message": "you must be at least 13 years old"})
90 ),
91 (
92 status = 409,
93 description = "`username_taken` / `email_taken`",
94 body = Envelope<NoData>,
95 example = json!({"code": "username_taken", "message": "username is already taken"})
96 ),
97 (
98 status = 429,
99 description = "`email_code_attempts_exceeded`(验证码尝试次数用尽)",
100 body = Envelope<NoData>,
101 example = json!({"code": "email_code_attempts_exceeded", "message": "too many invalid email verification attempts"})
102 ),
103 (
104 status = 500,
105 description = "`password_hash`(口令重哈希失败)/ `database` / `cache` / `session` / `serialization` / `internal`",
106 body = Envelope<NoData>,
107 example = json!({"code": "internal", "message": "internal server error"})
108 )
109 )
110)]
111pub async fn register(
112 svc: web::Data<crate::AuthSvc>,
113 session: Session,
114 body: web::Json<UserRegisterRequest>,
115 request_id: RequestId,
116) -> Result<Envelope<UserRegisterResponse>, AuthError> {
117 let user_agent = session.user_agent()?;
118 let ip_addr = session.client_ip()?;
119 let response = svc
120 .register(
121 session,
122 body.into_inner(),
123 user_agent,
124 ip_addr,
125 request_id.0,
126 )
127 .await?;
128 Ok(Envelope::ok(response))
129}
Deleted lib/auth/src/handlers/identity/register/mod.rs
@@ -1,8 +0,0 @@
1pub mod age;
2pub mod handler;
3pub mod service;
4pub mod types;
5
6pub use types::{
7 RegisterCodeRequest, RegisterCodeResponse, UserRegisterRequest, UserRegisterResponse,
8};
Deleted lib/auth/src/handlers/identity/register/types.rs
@@ -1,33 +0,0 @@
1use serde::{Deserialize, Serialize};
2use utoipa::ToSchema;
3
4use crate::handlers::identity::challenge::types::PowProofRequest;
5
6#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
7pub struct RegisterCodeRequest {
8 pub email: String,
9 pub pow_payload: PowProofRequest,
10}
11
12#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
13pub struct RegisterCodeResponse {
14 pub expires_in_secs: u64,
15}
16
17#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
18pub struct UserRegisterRequest {
19 pub username: String,
20 pub displayname: Option<String>,
21 pub birth_date: String,
22 pub email: String,
23 pub password_hash: String,
24 pub email_code: String,
25 pub pow_payload: PowProofRequest,
26}
27
28#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
29pub struct UserRegisterResponse {
30 pub user_id: i64,
31 pub username: String,
32 pub displayname: Option<String>,
33}
Deleted lib/auth/src/handlers/identity/ssh_key/mod.rs
@@ -1 +0,0 @@
1pub mod handler;
Added lib/auth/src/handlers/identity/challenge.rs View lib/auth/src/handlers/identity/challenge.rs
@@ -0,0 +1,99 @@
1use crate::AuthService;
2use crate::envelope::{Envelope, NoData};
3use crate::errors::{AuthError, AuthResult};
4use crate::events::{Labels, label};
5use crate::session::{RequestId, Session};
6use actix_web::web;
7use serde::{Deserialize, Serialize};
8use utoipa::ToSchema;
9
10pub const SESSION_CHALLENGE: &str = "challenge";
11
12#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
13pub struct ChallengeResponse {
14 pub context: String,
15 pub difficulty: u32,
16 pub issued_at: u64,
17 pub expires_at: u64,
18}
19
20#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
21pub struct PowProofRequest {
22 pub nonce: u64,
23 pub payload: Vec<u8>,
24}
25
26impl AuthService {
27 #[tracing::instrument(skip_all, fields(request_id = %request_id, client_ip = ?session.client_ip().ok().flatten()))]
28 pub async fn challenge(
29 &self,
30 session: Session,
31 request_id: String,
32 ) -> AuthResult<ChallengeResponse> {
33 let response = crate::share::pow::store::issue(&session, self.pow()).await?;
34 self.emit_labeled(
35 label::AUTH_CHALLENGE_CREATE,
36 Labels::new()
37 .with_opt(label::USER_ID, session.user_id().ok().flatten())
38 .with(label::REQUEST_ID, &request_id),
39 &response,
40 Some(request_id.as_str()),
41 )
42 .await;
43 Ok(response)
44 }
45
46 #[tracing::instrument(skip_all, fields(request_id = %request_id, client_ip = ?session.client_ip().ok().flatten()))]
47 pub async fn challenge_verify_pow(
48 &self,
49 session: Session,
50 proof: &PowProofRequest,
51 expected_payload: &[u8],
52 request_id: String,
53 ) -> AuthResult<()> {
54 crate::share::pow::store::verify(&session, self.cache(), proof, expected_payload).await?;
55 self.emit_labeled(
56 label::AUTH_CHALLENGE_USED,
57 Labels::new()
58 .with_opt(label::USER_ID, session.user_id().ok().flatten())
59 .with(label::REQUEST_ID, &request_id),
60 serde_json::json!({
61 "user_id": session.user_id().ok().flatten(),
62 }),
63 Some(request_id.as_str()),
64 )
65 .await;
66 Ok(())
67 }
68}
69
70#[utoipa::path(
71 post,
72 path = "/api/v1/auth/challenge",
73 tag = "auth",
74 operation_id = "auth_challenge",
75 summary = "签发工作量证明挑战",
76 description = "把挑战写进当前会话并返回。客户端需要为 `context` 找出一枚满足 `difficulty`(前导零比特数)的 nonce;`pow.v1` 算法与前端求解器一致。",
77 responses(
78 (
79 status = 200,
80 description = "挑战已签发并写入会话",
81 body = Envelope<ChallengeResponse>,
82 example = json!({"code":200,"message":"success","data":{"context":"3xJk9Qp2mV7sLbT1rZa8nHc4wDf6YgU0","difficulty":16,"issued_at":1767139200,"expires_at":1767139500}})
83 ),
84 (
85 status = 500,
86 description = "`internal`(挑战无法写入会话)/ `cache` / `session` / `serialization`",
87 body = Envelope<NoData>,
88 example = json!({"code": "internal", "message": "internal server error"})
89 )
90 )
91)]
92pub async fn challenge(
93 svc: web::Data<crate::AuthService>,
94 session: Session,
95 request_id: RequestId,
96) -> Result<Envelope<ChallengeResponse>, AuthError> {
97 let challenge = svc.challenge(session, request_id.0).await?;
98 Ok(Envelope::ok(challenge))
99}
Renamed lib/auth/src/handlers/identity/login/service.rs → lib/auth/src/handlers/identity/login.rs View lib/auth/src/handlers/identity/login.rs
@@ -1,29 +1,92 @@
1use crate::AuthService;
2use crate::envelope::{Envelope, NoData};
3use crate::errors::{AuthError, AuthResult};
14use crate::events::{Labels, label};
5use crate::handlers::identity::challenge::PowProofRequest;
26use crate::models::users::user_events::UserEventType;
37use crate::models::users::user_mfa::{self, MfaStatus, MfaType};
48use crate::models::users::users;
5use crate::session::Session;
6use sea_orm::sea_query::{Expr, ExprTrait, Func};
7use sea_orm::{ColumnTrait, Condition, EntityTrait, QueryFilter};
8
9use crate::AuthSvc;
10use crate::errors::{AuthError, AuthResult};
11use crate::handlers::identity::login::types::{
12 LOGIN_ATTEMPTS_MAX, LOGIN_ATTEMPTS_WINDOW_SECS, LoginCodeRequest, LoginCodeResponse,
13 LoginCodeVerifyRequest, LoginMfaChallenge, LoginVerifyRequest, MFA_CHALLENGE_TTL_SECS,
14 SESSION_LOGIN_MFA, UserLoginRequest, UserLoginResponse,
15};
9use crate::session::{RequestId, Session};
1610use crate::share::codes::{consume_email_code, store_email_code};
1711use crate::share::crypto::{now_secs, random_alnum, random_numeric_code};
1812use crate::share::password::{self, PasswordCheck};
1913use crate::share::rate::{attempts_load, attempts_reset, attempts_track, rate_limit_once};
2014use crate::share::validate;
15use actix_web::web;
16use sea_orm::sea_query::{Expr, ExprTrait, Func};
17use sea_orm::{ColumnTrait, Condition, EntityTrait, QueryFilter};
18use serde::{Deserialize, Serialize};
19use utoipa::ToSchema;
20
21pub const SESSION_LOGIN_MFA: &str = "login_mfa";
22pub const HASH_KEY_SHA256_V1: &str = "sha256-v1";
23pub const LOGIN_ATTEMPTS_MAX: u64 = 5;
24pub const LOGIN_ATTEMPTS_WINDOW_SECS: u64 = 300;
25pub const MFA_CHALLENGE_TTL_SECS: u64 = 300;
26
27#[derive(Clone, Deserialize, Serialize, ToSchema)]
28pub struct UserLoginRequest {
29 pub identity: String,
30 pub password_hash: String,
31 pub pow_payload: PowProofRequest,
32}
33
34impl std::fmt::Debug for UserLoginRequest {
35 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 formatter
37 .debug_struct("UserLoginRequest")
38 .field("identity", &self.identity)
39 .field("password_hash", &"<redacted>")
40 .field("pow_payload", &self.pow_payload)
41 .finish()
42 }
43}
44
45#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
46pub struct UserLoginResponse {
47 pub user_id: i64,
48 pub username: String,
49 pub displayname: Option<String>,
50 pub mfa_required: bool,
51 pub challenge: Option<String>,
52}
53
54#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
55pub struct LoginCodeRequest {
56 pub email: String,
57 pub pow_payload: PowProofRequest,
58}
59
60#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
61pub struct LoginCodeResponse {
62 pub expires_in_secs: u64,
63}
2164
65#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
66pub struct LoginCodeVerifyRequest {
67 pub email: String,
68 pub email_code: String,
69 pub pow_payload: PowProofRequest,
70}
71
72#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
73pub struct LoginVerifyRequest {
74 pub challenge: String,
75 pub code: String,
76}
77
78#[derive(Clone, Debug, Deserialize, Serialize)]
79pub struct LoginMfaChallenge {
80 pub user_id: i64,
81 pub challenge: String,
82 pub expires_at: u64,
83}
84
2285pub(crate) fn login_code_key(user_id: i64) -> String {
2386 format!("auth:code:login:{user_id}")
2487}
2588
26impl AuthSvc {
89impl AuthService {
2790 #[tracing::instrument(skip_all, fields(request_id = %request_id, client_ip = ?session.client_ip().ok().flatten()))]
2891 pub async fn login(
2992 &self,
@@ -396,7 +459,7 @@
396459 .exec(self.db().write())
397460 .await?;
398461
399 crate::me::service::invalidate_me_cache(self.cache(), user.id).await;
462 crate::services::me::invalidate_me_cache(self.cache(), user.id).await;
400463 self.audit(
401464 session,
402465 user.id,
@@ -537,3 +600,117 @@
537600 .await
538601 }
539602}
603
604#[utoipa::path(
605 post,
606 path = "/api/v1/auth/login",
607 tag = "auth",
608 operation_id = "auth_login",
609 summary = "口令登录",
610 description = "用口令登录。`identity` 可以是用户名、邮箱(大小写不敏感)或用户编号,`password_hash` 是客户端先算好的口令摘要,工作量证明的载荷必须是 `identity`。账号启用了多因子时返回 `mfa_required: true` 与 `challenge`,此时会话尚未绑定,需再调 `/api/v1/auth/login/verify`。",
611 request_body = UserLoginRequest,
612 responses(
613 (status = 200, description = "登录成功;`mfa_required` 为真时还需二次验证", body = Envelope<UserLoginResponse>, example = json!({"code":200,"message":"success","data":{"user_id":1024,"username":"ada","displayname":"Ada","mfa_required":false,"challenge":null}})),
614 (status = 400, description = "工作量证明过期、载荷不匹配或请求不合法(`expired`、`invalid_proof`、`invalid_payload`、`malformed_request`)", body = Envelope<NoData>, example = json!({"code": "expired", "message": "challenge expired"})),
615 (status = 401, description = "身份或口令不正确(`invalid_credentials`)", body = Envelope<NoData>, example = json!({"code": "invalid_credentials", "message": "invalid credentials"})),
616 (status = 403, description = "账号被禁止登录,原因见 `message`(`login_forbidden`)", body = Envelope<NoData>, example = json!({"code": "login_forbidden", "message": "account is banned"})),
617 (status = 429, description = "失败次数超出配额(`rate_limited`)", body = Envelope<NoData>, example = json!({"code": "rate_limited", "message": "too many attempts"})),
618 (status = 500, description = "服务端故障(`internal`、`database`、`cache`、`session`、`serialization`)", body = Envelope<NoData>, example = json!({"code": "internal", "message": "internal server error"}))
619 )
620)]
621pub async fn login(
622 svc: web::Data<crate::AuthService>,
623 session: Session,
624 body: web::Json<UserLoginRequest>,
625 request_id: RequestId,
626) -> Result<Envelope<UserLoginResponse>, AuthError> {
627 let response = svc.login(session, body.into_inner(), request_id.0).await?;
628 Ok(Envelope::ok(response))
629}
630
631#[utoipa::path(
632 post,
633 path = "/api/v1/auth/login/code",
634 tag = "auth",
635 operation_id = "auth_login_code",
636 summary = "发送邮箱登录验证码",
637 description = "向账号邮箱发送 6 位登录验证码,`expires_in_secs` 为有效期。工作量证明的载荷必须是 `email`。同一账号的发码间隔受 `email_code_rate_limit_secs` 限制。",
638 request_body = LoginCodeRequest,
639 responses(
640 (status = 200, description = "验证码已发送", body = Envelope<LoginCodeResponse>, example = json!({"code":200,"message":"success","data":{"expires_in_secs":600}})),
641 (status = 400, description = "邮箱格式不合法或工作量证明过期、载荷不匹配(`invalid_email`、`expired`、`invalid_proof`、`invalid_payload`、`malformed_request`)", body = Envelope<NoData>, example = json!({"code": "invalid_email", "message": "invalid email address"})),
642 (status = 401, description = "邮箱未注册或账号不可用(`invalid_credentials`)", body = Envelope<NoData>, example = json!({"code": "invalid_credentials", "message": "invalid credentials"})),
643 (status = 403, description = "账号被禁止登录,原因见 `message`(`login_forbidden`)", body = Envelope<NoData>, example = json!({"code": "login_forbidden", "message": "account is banned"})),
644 (status = 429, description = "发码过于频繁(`rate_limited`)", body = Envelope<NoData>, example = json!({"code": "rate_limited", "message": "too many requests"})),
645 (status = 500, description = "服务端故障,含邮件发送失败(`internal`、`database`、`cache`、`session`、`email`、`serialization`)", body = Envelope<NoData>, example = json!({"code": "internal", "message": "internal server error"}))
646 )
647)]
648pub async fn login_code(
649 svc: web::Data<crate::AuthService>,
650 session: Session,
651 body: web::Json<LoginCodeRequest>,
652 request_id: RequestId,
653) -> Result<Envelope<LoginCodeResponse>, AuthError> {
654 let response = svc
655 .login_code(session, body.into_inner(), request_id.0)
656 .await?;
657 Ok(Envelope::ok(response))
658}
659
660#[utoipa::path(
661 post,
662 path = "/api/v1/auth/login/code/verify",
663 tag = "auth",
664 operation_id = "auth_login_code_verify",
665 summary = "邮箱验证码换会话",
666 description = "用邮箱登录验证码换会话。工作量证明的载荷必须是 `email`。验证码一旦核对通过即作废,连续输错 `max_verify_attempts` 次后需重新申请。账号启用了多因子时返回 `mfa_required: true` 与 `challenge`。",
667 request_body = LoginCodeVerifyRequest,
668 responses(
669 (status = 200, description = "登录成功;`mfa_required` 为真时还需二次验证", body = Envelope<UserLoginResponse>, example = json!({"code":200,"message":"success","data":{"user_id":1024,"username":"ada","displayname":"Ada","mfa_required":false,"challenge":null}})),
670 (status = 400, description = "验证码不正确或已过期,或邮箱格式、工作量证明不合法(`invalid_email_code`、`invalid_email`、`expired`、`invalid_proof`、`invalid_payload`、`malformed_request`)", body = Envelope<NoData>, example = json!({"code": "invalid_email_code", "message": "invalid email code"})),
671 (status = 401, description = "邮箱未注册或账号不可用(`invalid_credentials`)", body = Envelope<NoData>, example = json!({"code": "invalid_credentials", "message": "invalid credentials"})),
672 (status = 403, description = "账号被禁止登录,原因见 `message`(`login_forbidden`)", body = Envelope<NoData>, example = json!({"code": "login_forbidden", "message": "account is banned"})),
673 (status = 429, description = "验证码尝试次数超限或请求过频(`email_code_attempts_exceeded`、`rate_limited`)", body = Envelope<NoData>, example = json!({"code": "email_code_attempts_exceeded", "message": "too many attempts"})),
674 (status = 500, description = "服务端故障(`internal`、`database`、`cache`、`session`、`serialization`)", body = Envelope<NoData>, example = json!({"code": "internal", "message": "internal server error"}))
675 )
676)]
677pub async fn login_code_verify(
678 svc: web::Data<crate::AuthService>,
679 session: Session,
680 body: web::Json<LoginCodeVerifyRequest>,
681 request_id: RequestId,
682) -> Result<Envelope<UserLoginResponse>, AuthError> {
683 let response = svc
684 .login_code_verify(session, body.into_inner(), request_id.0)
685 .await?;
686 Ok(Envelope::ok(response))
687}
688
689#[utoipa::path(
690 post,
691 path = "/api/v1/auth/login/verify",
692 tag = "auth",
693 operation_id = "auth_login_verify",
694 summary = "二次验证换会话",
695 description = "用 6 位 TOTP 动态码或一枚未使用的恢复码完成二次验证并绑定会话。`challenge` 必须与 `/api/v1/auth/login` 或 `/api/v1/auth/login/code/verify` 返回的一致,挑战有效期 300 秒,同一动态码时间步不能重复使用,恢复码用掉即作废。",
696 request_body = LoginVerifyRequest,
697 responses(
698 (status = 200, description = "登录成功,会话已绑定", body = Envelope<UserLoginResponse>, example = json!({"code":200,"message":"success","data":{"user_id":1024,"username":"ada","displayname":"Ada","mfa_required":false,"challenge":null}})),
699 (status = 400, description = "挑战过期或对不上、动态码错误、动态码时间步已用过、恢复码无效(`expired`、`malformed_request`、`totp_code_invalid`、`totp_step_reused`、`recovery_code_invalid`)", body = Envelope<NoData>, example = json!({"code": "totp_code_invalid", "message": "invalid totp code"})),
700 (status = 401, description = "会话里没有待验证挑战、账号不可用或多因子记录已被撤销(`unauthenticated`、`invalid_credentials`、`mfa_required`)", body = Envelope<NoData>, example = json!({"code": "mfa_required", "message": "multi-factor authentication required"})),
701 (status = 403, description = "账号被禁止登录,原因见 `message`(`login_forbidden`)", body = Envelope<NoData>, example = json!({"code": "login_forbidden", "message": "account is banned"})),
702 (status = 429, description = "尝试次数超限或恢复码已用尽(`rate_limited`、`recovery_codes_exhausted`)", body = Envelope<NoData>, example = json!({"code": "rate_limited", "message": "too many attempts"})),
703 (status = 500, description = "服务端故障(`internal`、`database`、`cache`、`session`、`serialization`)", body = Envelope<NoData>, example = json!({"code": "internal", "message": "internal server error"}))
704 )
705)]
706pub async fn login_verify(
707 svc: web::Data<crate::AuthService>,
708 session: Session,
709 body: web::Json<LoginVerifyRequest>,
710 request_id: RequestId,
711) -> Result<Envelope<UserLoginResponse>, AuthError> {
712 let response = svc
713 .login_verify(session, body.into_inner(), request_id.0)
714 .await?;
715 Ok(Envelope::ok(response))
716}
Renamed lib/auth/src/handlers/identity/logout/service.rs → lib/auth/src/handlers/identity/logout.rs View lib/auth/src/handlers/identity/logout.rs
@@ -1,13 +1,20 @@
1use crate::AuthService;
2use crate::envelope::{Envelope, NoData};
3use crate::errors::{AuthError, AuthResult};
14use crate::events::{Labels, label};
25use crate::models::users::user_events::UserEventType;
3use crate::session::Session;
6use crate::session::{RequestId, Session};
7use actix_web::web;
48use sea_orm::Set;
9use serde::{Deserialize, Serialize};
10use utoipa::ToSchema;
511
6use crate::AuthSvc;
7use crate::errors::AuthResult;
8use crate::handlers::identity::logout::types::LogoutResponse;
12#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
13pub struct LogoutResponse {
14 pub user_id: Option<i64>,
15}
916
10impl AuthSvc {
17impl AuthService {
1118 #[tracing::instrument(skip_all, fields(request_id = %request_id, client_ip = ?session.client_ip().ok().flatten()))]
1219 pub async fn logout(&self, session: Session, request_id: String) -> AuthResult<LogoutResponse> {
1320 let user_id = match session.user_id() {
@@ -53,7 +60,7 @@
5360 }
5461
5562 if let Some(uid) = user_id {
56 crate::me::service::invalidate_me_cache(self.cache(), uid).await;
63 crate::services::me::invalidate_me_cache(self.cache(), uid).await;
5764 if let Err(error) = crate::share::audit::entry(
5865 self.db(),
5966 uid,
@@ -83,3 +90,24 @@
8390 Ok(response)
8491 }
8592}
93
94#[utoipa::path(
95 post,
96 path = "/api/v1/auth/session/logout",
97 tag = "auth",
98 operation_id = "auth_logout",
99 summary = "登出",
100 description = "销毁当前会话,并清掉该用户的 `/api/v1/auth/me` 缓存。匿名调用同样成功,此时 `user_id` 为 `null`。",
101 responses(
102 (status = 200, description = "会话已销毁", body = Envelope<LogoutResponse>, example = json!({"code":200,"message":"success","data":{"user_id":1024}})),
103 (status = 500, description = "服务端故障(`internal`、`database`、`cache`、`session`、`serialization`)", body = Envelope<NoData>, example = json!({"code": "internal", "message": "internal server error"}))
104 )
105)]
106pub async fn logout(
107 svc: web::Data<crate::AuthService>,
108 session: Session,
109 request_id: RequestId,
110) -> Result<Envelope<LogoutResponse>, AuthError> {
111 let response = svc.logout(session, request_id.0).await?;
112 Ok(Envelope::ok(response))
113}
Renamed lib/auth/src/handlers/identity/me/handler.rs → lib/auth/src/handlers/identity/me.rs View lib/auth/src/handlers/identity/me.rs
@@ -1,9 +1,8 @@
1use crate::session::{RequestId, Session};
2use actix_web::web;
3
41use crate::envelope::{Envelope, NoData};
52use crate::errors::AuthError;
6use crate::me::types::MeResponse;
3use crate::services::me::MeResponse;
4use crate::session::{RequestId, Session};
5use actix_web::web;
76
87#[utoipa::path(
98 get,
@@ -19,7 +18,7 @@
1918 )
2019)]
2120pub async fn me(
22 svc: web::Data<crate::AuthSvc>,
21 svc: web::Data<crate::AuthService>,
2322 session: Session,
2423 request_id: RequestId,
2524) -> Result<Envelope<MeResponse>, AuthError> {
Renamed lib/auth/src/handlers/identity/organization/handler.rs → lib/auth/src/handlers/identity/organization.rs View lib/auth/src/handlers/identity/organization.rs
@@ -1,15 +1,14 @@
1use actix_web::web;
2use uuid::Uuid;
3
41use crate::envelope::{Envelope, NoData};
52use crate::errors::AuthError;
6use crate::organization::types::{
3use crate::services::organization::types::{
74 AddMemberBody, BanMemberBody, CheckPermissionBody, CreateOrganizationBody, CreateRoleBody,
85 InviteSummary, MemberSummary, OrganizationSummary, PermissionCheck, PermissionGrant,
96 RoleSummary, SetMemberRoleBody, SetRolePermissionsBody, UpdateBanBody, UpdateMemberBody,
107 UpdateOrganizationBody,
118};
129use crate::session::{RequestId, Session};
10use actix_web::web;
11use uuid::Uuid;
1312
1413#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
1514pub struct OrgPath {
@@ -145,7 +144,7 @@
145144 )
146145)]
147146pub async fn create_organization(
148 svc: web::Data<crate::AuthSvc>,
147 svc: web::Data<crate::AuthService>,
149148 session: Session,
150149 request_id: RequestId,
151150 body: web::Json<CreateOrganizationBody>,
@@ -172,7 +171,7 @@
172171 )
173172)]
174173pub async fn list_organizations(
175 svc: web::Data<crate::AuthSvc>,
174 svc: web::Data<crate::AuthService>,
176175 session: Session,
177176 request_id: RequestId,
178177 query: web::Query<OrgListQuery>,
@@ -207,7 +206,7 @@
207206 )
208207)]
209208pub async fn get_organization(
210 svc: web::Data<crate::AuthSvc>,
209 svc: web::Data<crate::AuthService>,
211210 session: Session,
212211 request_id: RequestId,
213212 path: web::Path<OrgPath>,
@@ -242,7 +241,7 @@
242241 )
243242)]
244243pub async fn update_organization(
245 svc: web::Data<crate::AuthSvc>,
244 svc: web::Data<crate::AuthService>,
246245 session: Session,
247246 request_id: RequestId,
248247 path: web::Path<OrgPath>,
@@ -277,7 +276,7 @@
277276 )
278277)]
279278pub async fn archive_organization(
280 svc: web::Data<crate::AuthSvc>,
279 svc: web::Data<crate::AuthService>,
281280 session: Session,
282281 request_id: RequestId,
283282 path: web::Path<OrgPath>,
@@ -311,7 +310,7 @@
311310 )
312311)]
313312pub async fn restore_organization(
314 svc: web::Data<crate::AuthSvc>,
313 svc: web::Data<crate::AuthService>,
315314 session: Session,
316315 request_id: RequestId,
317316 path: web::Path<OrgPath>,
@@ -344,7 +343,7 @@
344343 )
345344)]
346345pub async fn delete_organization(
347 svc: web::Data<crate::AuthSvc>,
346 svc: web::Data<crate::AuthService>,
348347 session: Session,
349348 request_id: RequestId,
350349 path: web::Path<OrgPath>,
@@ -382,7 +381,7 @@
382381 )
383382)]
384383pub async fn transfer_ownership(
385 svc: web::Data<crate::AuthSvc>,
384 svc: web::Data<crate::AuthService>,
386385 session: Session,
387386 request_id: RequestId,
388387 path: web::Path<OrgPath>,
@@ -420,7 +419,7 @@
420419 )
421420)]
422421pub async fn list_members(
423 svc: web::Data<crate::AuthSvc>,
422 svc: web::Data<crate::AuthService>,
424423 session: Session,
425424 request_id: RequestId,
426425 path: web::Path<OrgPath>,
@@ -461,7 +460,7 @@
461460 )
462461)]
463462pub async fn get_member(
464 svc: web::Data<crate::AuthSvc>,
463 svc: web::Data<crate::AuthService>,
465464 session: Session,
466465 request_id: RequestId,
467466 path: web::Path<OrgMemberPath>,
@@ -496,7 +495,7 @@
496495 )
497496)]
498497pub async fn update_self_member(
499 svc: web::Data<crate::AuthSvc>,
498 svc: web::Data<crate::AuthService>,
500499 session: Session,
501500 request_id: RequestId,
502501 path: web::Path<OrgPath>,
@@ -535,7 +534,7 @@
535534 )
536535)]
537536pub async fn update_member(
538 svc: web::Data<crate::AuthSvc>,
537 svc: web::Data<crate::AuthService>,
539538 session: Session,
540539 request_id: RequestId,
541540 path: web::Path<OrgMemberPath>,
@@ -572,7 +571,7 @@
572571 )
573572)]
574573pub async fn add_member(
575 svc: web::Data<crate::AuthSvc>,
574 svc: web::Data<crate::AuthService>,
576575 session: Session,
577576 request_id: RequestId,
578577 path: web::Path<OrgPath>,
@@ -610,7 +609,7 @@
610609 )
611610)]
612611pub async fn remove_member(
613 svc: web::Data<crate::AuthSvc>,
612 svc: web::Data<crate::AuthService>,
614613 session: Session,
615614 request_id: RequestId,
616615 path: web::Path<OrgMemberPath>,
@@ -646,7 +645,7 @@
646645 )
647646)]
648647pub async fn leave_organization(
649 svc: web::Data<crate::AuthSvc>,
648 svc: web::Data<crate::AuthService>,
650649 session: Session,
651650 request_id: RequestId,
652651 path: web::Path<OrgPath>,
@@ -679,7 +678,7 @@
679678 )
680679)]
681680pub async fn set_member_role(
682 svc: web::Data<crate::AuthSvc>,
681 svc: web::Data<crate::AuthService>,
683682 session: Session,
684683 request_id: RequestId,
685684 path: web::Path<OrgMemberPath>,
@@ -718,7 +717,7 @@
718717 )
719718)]
720719pub async fn ban_member(
721 svc: web::Data<crate::AuthSvc>,
720 svc: web::Data<crate::AuthService>,
722721 session: Session,
723722 request_id: RequestId,
724723 path: web::Path<OrgMemberPath>,
@@ -756,7 +755,7 @@
756755 )
757756)]
758757pub async fn unban_member(
759 svc: web::Data<crate::AuthSvc>,
758 svc: web::Data<crate::AuthService>,
760759 session: Session,
761760 request_id: RequestId,
762761 path: web::Path<OrgMemberPath>,
@@ -793,7 +792,7 @@
793792 )
794793)]
795794pub async fn list_bans(
796 svc: web::Data<crate::AuthSvc>,
795 svc: web::Data<crate::AuthService>,
797796 session: Session,
798797 request_id: RequestId,
799798 path: web::Path<OrgPath>,
@@ -831,7 +830,7 @@
831830 )
832831)]
833832pub async fn get_ban(
834 svc: web::Data<crate::AuthSvc>,
833 svc: web::Data<crate::AuthService>,
835834 session: Session,
836835 request_id: RequestId,
837836 path: web::Path<OrgBanPath>,
@@ -868,7 +867,7 @@
868867 )
869868)]
870869pub async fn update_ban(
871 svc: web::Data<crate::AuthSvc>,
870 svc: web::Data<crate::AuthService>,
872871 session: Session,
873872 request_id: RequestId,
874873 path: web::Path<OrgBanPath>,
@@ -906,7 +905,7 @@
906905 )
907906)]
908907pub async fn unban_by_user(
909 svc: web::Data<crate::AuthSvc>,
908 svc: web::Data<crate::AuthService>,
910909 session: Session,
911910 request_id: RequestId,
912911 path: web::Path<OrgBanPath>,
@@ -951,7 +950,7 @@
951950 )
952951)]
953952pub async fn set_member_permissions(
954 svc: web::Data<crate::AuthSvc>,
953 svc: web::Data<crate::AuthService>,
955954 session: Session,
956955 request_id: RequestId,
957956 path: web::Path<OrgMemberPath>,
@@ -987,7 +986,7 @@
987986 )
988987)]
989988pub async fn list_roles(
990 svc: web::Data<crate::AuthSvc>,
989 svc: web::Data<crate::AuthService>,
991990 session: Session,
992991 request_id: RequestId,
993992 path: web::Path<OrgPath>,
@@ -1018,7 +1017,7 @@
10181017 )
10191018)]
10201019pub async fn get_role(
1021 svc: web::Data<crate::AuthSvc>,
1020 svc: web::Data<crate::AuthService>,
10221021 session: Session,
10231022 request_id: RequestId,
10241023 path: web::Path<OrgRolePath>,
@@ -1052,7 +1051,7 @@
10521051 )
10531052)]
10541053pub async fn create_role(
1055 svc: web::Data<crate::AuthSvc>,
1054 svc: web::Data<crate::AuthService>,
10561055 session: Session,
10571056 request_id: RequestId,
10581057 path: web::Path<OrgPath>,
@@ -1090,7 +1089,7 @@
10901089 )
10911090)]
10921091pub async fn update_role(
1093 svc: web::Data<crate::AuthSvc>,
1092 svc: web::Data<crate::AuthService>,
10941093 session: Session,
10951094 request_id: RequestId,
10961095 path: web::Path<OrgRolePath>,
@@ -1134,7 +1133,7 @@
11341133 )
11351134)]
11361135pub async fn delete_role(
1137 svc: web::Data<crate::AuthSvc>,
1136 svc: web::Data<crate::AuthService>,
11381137 session: Session,
11391138 request_id: RequestId,
11401139 path: web::Path<OrgRolePath>,
@@ -1171,7 +1170,7 @@
11711170 )
11721171)]
11731172pub async fn set_role_permissions(
1174 svc: web::Data<crate::AuthSvc>,
1173 svc: web::Data<crate::AuthService>,
11751174 session: Session,
11761175 request_id: RequestId,
11771176 path: web::Path<OrgRolePath>,
@@ -1213,7 +1212,7 @@
12131212 )
12141213)]
12151214pub async fn assign_role(
1216 svc: web::Data<crate::AuthSvc>,
1215 svc: web::Data<crate::AuthService>,
12171216 session: Session,
12181217 request_id: RequestId,
12191218 path: web::Path<OrgPath>,
@@ -1249,7 +1248,7 @@
12491248 )
12501249)]
12511250pub async fn unassign_role(
1252 svc: web::Data<crate::AuthSvc>,
1251 svc: web::Data<crate::AuthService>,
12531252 session: Session,
12541253 request_id: RequestId,
12551254 path: web::Path<OrgPath>,
@@ -1287,7 +1286,7 @@
12871286 )
12881287)]
12891288pub async fn join_role(
1290 svc: web::Data<crate::AuthSvc>,
1289 svc: web::Data<crate::AuthService>,
12911290 session: Session,
12921291 request_id: RequestId,
12931292 path: web::Path<OrgRolePath>,
@@ -1323,7 +1322,7 @@
13231322 )
13241323)]
13251324pub async fn leave_role(
1326 svc: web::Data<crate::AuthSvc>,
1325 svc: web::Data<crate::AuthService>,
13271326 session: Session,
13281327 request_id: RequestId,
13291328 path: web::Path<OrgRolePath>,
@@ -1357,7 +1356,7 @@
13571356 )
13581357)]
13591358pub async fn check_permission(
1360 svc: web::Data<crate::AuthSvc>,
1359 svc: web::Data<crate::AuthService>,
13611360 session: Session,
13621361 request_id: RequestId,
13631362 path: web::Path<OrgPath>,
@@ -1394,7 +1393,7 @@
13941393 )
13951394)]
13961395pub async fn create_invite(
1397 svc: web::Data<crate::AuthSvc>,
1396 svc: web::Data<crate::AuthService>,
13981397 session: Session,
13991398 request_id: RequestId,
14001399 path: web::Path<OrgPath>,
@@ -1435,7 +1434,7 @@
14351434 )
14361435)]
14371436pub async fn list_invites(
1438 svc: web::Data<crate::AuthSvc>,
1437 svc: web::Data<crate::AuthService>,
14391438 session: Session,
14401439 request_id: RequestId,
14411440 path: web::Path<OrgPath>,
@@ -1475,7 +1474,7 @@
14751474 )
14761475)]
14771476pub async fn revoke_invite(
1478 svc: web::Data<crate::AuthSvc>,
1477 svc: web::Data<crate::AuthService>,
14791478 session: Session,
14801479 request_id: RequestId,
14811480 path: web::Path<OrgInvitePath>,
@@ -1510,7 +1509,7 @@
15101509 )
15111510)]
15121511pub async fn redeem_invite(
1513 svc: web::Data<crate::AuthSvc>,
1512 svc: web::Data<crate::AuthService>,
15141513 session: Session,
15151514 request_id: RequestId,
15161515 body: web::Json<RedeemInviteBody>,
Renamed lib/auth/src/handlers/identity/password_reset/service.rs → lib/auth/src/handlers/identity/password_reset.rs View lib/auth/src/handlers/identity/password_reset.rs
@@ -1,30 +1,64 @@
1use crate::AuthService;
2use crate::envelope::{Envelope, NoData};
3use crate::errors::{AuthError, AuthResult};
14use crate::events::{Labels, label};
2use crate::session::Session;
3use chrono::{DateTime, Duration, Utc};
4use sea_orm::entity::Set;
5use sea_orm::sea_query::{Expr, ExprTrait, Func};
6use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter};
7
8use crate::AuthSvc;
9use crate::errors::{AuthError, AuthResult};
10use crate::handlers::identity::password_reset::types::{
11 PasswordResetConfirmRequest, PasswordResetConfirmResponse, PasswordResetRequest,
12 PasswordResetResponse,
13};
5use crate::handlers::identity::challenge::PowProofRequest;
146use crate::models::users::user_events::UserEventType;
157use crate::models::users::user_verification_tokens::{self, VerificationPurpose};
168use crate::models::users::users;
9use crate::session::{RequestId, Session};
1710use crate::share::codes::{consume_email_code, store_email_code};
1811use crate::share::crypto::{random_b64url, sha256_hex};
1912use crate::share::password::{hash as hash_password, validate_new_password};
2013use crate::share::{rate, validate};
14use actix_web::web;
15use chrono::{DateTime, Duration, Utc};
16use sea_orm::entity::Set;
17use sea_orm::sea_query::{Expr, ExprTrait, Func};
18use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter};
19use serde::{Deserialize, Serialize};
20use utoipa::ToSchema;
21
22#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
23pub struct PasswordResetRequest {
24 pub email: String,
25 pub pow_payload: PowProofRequest,
26}
27
28#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
29pub struct PasswordResetResponse {
30 pub expires_in_secs: u64,
31}
32
33#[derive(Clone, Deserialize, Serialize, ToSchema)]
34pub struct PasswordResetConfirmRequest {
35 pub token: String,
36 pub password_hash: String,
37 pub pow_payload: PowProofRequest,
38}
39
40impl std::fmt::Debug for PasswordResetConfirmRequest {
41 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 formatter
43 .debug_struct("PasswordResetConfirmRequest")
44 .field("token", &"<redacted>")
45 .field("password_hash", &"<redacted>")
46 .field("pow_payload", &self.pow_payload)
47 .finish()
48 }
49}
50
51#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
52pub struct PasswordResetConfirmResponse {
53 pub user_id: i64,
54}
2155
2256const RESET_CODE_KEY_PREFIX: &str = "auth:code:reset:";
2357const RESET_RATE_KEY_PREFIX: &str = "auth:rate:reset:";
2458const RESET_HASH_KEY_ID: &str = "sha256-v1";
2559const PASSWORD_DIGEST_MAX_LEN: usize = 255;
2660
27impl AuthSvc {
61impl AuthService {
2862 #[tracing::instrument(skip_all, fields(request_id = %request_id, client_ip = ?session.client_ip().ok().flatten()))]
2963 pub async fn request_password_reset(
3064 &self,
@@ -271,3 +305,114 @@
271305 Ok(())
272306 }
273307}
308
309#[utoipa::path(
310 post,
311 path = "/api/v1/auth/password/reset",
312 tag = "auth",
313 operation_id = "auth_password_reset_request",
314 summary = "申请密码重置",
315 description = "校验工作量证明(payload = 邮箱的 UTF-8 字节)后,作废该用户其余未消费的重置令牌,签发新令牌(长度 `reset_token_bytes`,有效期 `reset_token_ttl_secs`),库内只存 `sha256_hex(token)` 且 `hash_key_id = \"sha256-v1\"`,同时把摘要写入缓存键 `auth:code:reset:{email}`,发信限流键 `auth:rate:reset:{email}`。",
316 request_body = PasswordResetRequest,
317 responses(
318 (
319 status = 200,
320 description = "申请已受理;是否真的发信不对外披露",
321 body = Envelope<PasswordResetResponse>,
322 example = json!({"code":200,"message":"success","data":{"expires_in_secs":1800}})
323 ),
324 (
325 status = 400,
326 description = "`invalid_email` / `expired` / `invalid_payload`(payload 与邮箱不一致)/ `invalid_proof`",
327 body = Envelope<NoData>,
328 example = json!({"code": "invalid_email", "message": "email is invalid"})
329 ),
330 (
331 status = 429,
332 description = "`rate_limited`(同一邮箱申请过于频繁)",
333 body = Envelope<NoData>,
334 example = json!({"code": "rate_limited", "message": "too many requests, retry after 60s"})
335 ),
336 (
337 status = 500,
338 description = "`email`(投递失败)/ `database` / `cache` / `session` / `serialization` / `internal`",
339 body = Envelope<NoData>,
340 example = json!({"code": "internal", "message": "internal server error"})
341 )
342 )
343)]
344pub async fn password_reset_request(
345 svc: web::Data<crate::AuthService>,
346 session: Session,
347 body: web::Json<PasswordResetRequest>,
348 request_id: RequestId,
349) -> Result<Envelope<PasswordResetResponse>, AuthError> {
350 let user_agent = session.user_agent()?;
351 let ip_addr = session.client_ip()?;
352 let response = svc
353 .request_password_reset(
354 session,
355 body.into_inner(),
356 user_agent,
357 ip_addr,
358 request_id.0,
359 )
360 .await?;
361 Ok(Envelope::ok(response))
362}
363
364/// 并与 `security_version` 绑定;成功后作废该用户其余未消费的重置令牌并递增 `security_version`。
365#[utoipa::path(
366 post,
367 path = "/api/v1/auth/password/reset/confirm",
368 tag = "auth",
369 operation_id = "auth_password_reset",
370 summary = "确认密码重置",
371 description = "校验工作量证明(payload = `token` 的 UTF-8 字节)与缓存键 `auth:code:reset:{email}`(尝试上限 `max_verify_attempts`,错误计入 `invalid_email_code`)后,条件消费数据库令牌行(`consumed_at` 单次占用),Argon2 重哈希新口令,更新 `password_hash`/`password_updated_at`/`password_must_change`/`security_version + 1`,作废其余令牌并记 `PasswordChange` 审计事件。",
372 request_body = PasswordResetConfirmRequest,
373 responses(
374 (
375 status = 200,
376 description = "口令已改写;旧口令与其余重置令牌失效",
377 body = Envelope<PasswordResetConfirmResponse>,
378 example = json!({"code":200,"message":"success","data":{"user_id":1234567}})
379 ),
380 (
381 status = 400,
382 description = "`invalid_reset_token`(令牌不存在/已消费/已作废/已过期/账号不可用或凭据代数不匹配)/ `invalid_email_code`(缓存内摘要不匹配或已过期)/ `invalid_password` / `expired` / `invalid_payload` / `invalid_proof`",
383 body = Envelope<NoData>,
384 example = json!({"code": "invalid_reset_token", "message": "password reset token is invalid"})
385 ),
386 (
387 status = 429,
388 description = "`email_code_attempts_exceeded`(令牌尝试次数用尽)",
389 body = Envelope<NoData>,
390 example = json!({"code": "email_code_attempts_exceeded", "message": "too many invalid email verification attempts"})
391 ),
392 (
393 status = 500,
394 description = "`password_hash`(口令重哈希失败)/ `database` / `cache` / `session` / `serialization` / `internal`",
395 body = Envelope<NoData>,
396 example = json!({"code": "internal", "message": "internal server error"})
397 )
398 )
399)]
400pub async fn password_reset(
401 svc: web::Data<crate::AuthService>,
402 session: Session,
403 body: web::Json<PasswordResetConfirmRequest>,
404 request_id: RequestId,
405) -> Result<Envelope<PasswordResetConfirmResponse>, AuthError> {
406 let user_agent = session.user_agent()?;
407 let ip_addr = session.client_ip()?;
408 let response = svc
409 .reset_password(
410 session,
411 body.into_inner(),
412 user_agent,
413 ip_addr,
414 request_id.0,
415 )
416 .await?;
417 Ok(Envelope::ok(response))
418}
Renamed lib/auth/src/handlers/identity/register/service.rs → lib/auth/src/handlers/identity/register.rs View lib/auth/src/handlers/identity/register.rs
@@ -1,31 +1,80 @@
1use crate::AuthService;
2use crate::envelope::{Envelope, NoData};
3use crate::errors::{AuthError, AuthResult};
14use crate::events::{Labels, label};
2use crate::session::Session;
5use crate::handlers::identity::challenge::PowProofRequest;
6use crate::models::users::user_events::UserEventType;
7use crate::models::users::users;
8use crate::session::{RequestId, Session};
9use crate::share::codes::{consume_email_code, store_email_code};
10use crate::share::password::{hash as hash_password, validate_new_password};
11use crate::share::validate::{validate_display_name, validate_email, validate_username};
12use crate::share::{crypto, rate};
13use actix_web::web;
314use chrono::Utc;
415use rand::RngExt;
516use sea_orm::entity::Set;
617use sea_orm::sea_query::{Expr, ExprTrait, Func};
718use sea_orm::{ActiveModelTrait, ColumnTrait, Condition, EntityTrait, QueryFilter, SqlErr};
19use serde::{Deserialize, Serialize};
20use utoipa::ToSchema;
21
22#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
23pub struct RegisterCodeRequest {
24 pub email: String,
25 pub pow_payload: PowProofRequest,
26}
27
28#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
29pub struct RegisterCodeResponse {
30 pub expires_in_secs: u64,
31}
832
9use crate::AuthSvc;
10use crate::errors::{AuthError, AuthResult};
11use crate::handlers::identity::register::age;
12use crate::handlers::identity::register::types::{
13 RegisterCodeRequest, RegisterCodeResponse, UserRegisterRequest, UserRegisterResponse,
14};
15use crate::models::users::user_events::UserEventType;
16use crate::models::users::users;
17use crate::share::codes::{consume_email_code, store_email_code};
18use crate::share::password::{hash as hash_password, validate_new_password};
19use crate::share::validate::{validate_display_name, validate_email, validate_username};
20use crate::share::{crypto, rate};
33#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
34pub struct UserRegisterRequest {
35 pub username: String,
36 pub displayname: Option<String>,
37 pub birth_date: String,
38 pub email: String,
39 pub password_hash: String,
40 pub email_code: String,
41 pub pow_payload: PowProofRequest,
42}
2143
44#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
45pub struct UserRegisterResponse {
46 pub user_id: i64,
47 pub username: String,
48 pub displayname: Option<String>,
49}
50
51pub mod age {
52 use chrono::{Datelike, NaiveDate};
53
54 use crate::errors::{AuthError, AuthResult};
55
56 pub const EARLIEST_BIRTH_YEAR: i32 = 1900;
57
58 pub fn parse_birth_date(raw: &str, min_age: u8) -> AuthResult<NaiveDate> {
59 let parsed =
60 NaiveDate::parse_from_str(raw, "%Y-%m-%d").map_err(|_| AuthError::InvalidBirthDate)?;
61 if parsed.format("%Y-%m-%d").to_string() != raw {
62 return Err(AuthError::InvalidBirthDate);
63 }
64 if parsed.year() < EARLIEST_BIRTH_YEAR {
65 return Err(AuthError::InvalidBirthDate);
66 }
67 crate::share::validate::validate_age(Some(parsed), min_age)
68 }
69}
70
2271const REGISTER_CODE_KEY_PREFIX: &str = "auth:code:register:";
2372const REGISTER_RATE_KEY_PREFIX: &str = "auth:rate:register:";
2473const USERNAME_MAX_LEN: usize = 32;
2574const PASSWORD_DIGEST_MAX_LEN: usize = 255;
2675const ID_INSERT_ATTEMPTS: u32 = 8;
2776
28impl AuthSvc {
77impl AuthService {
2978 #[tracing::instrument(skip_all, fields(request_id = %request_id, client_ip = ?session.client_ip().ok().flatten()))]
3079 pub async fn send_register_code(
3180 &self,
@@ -245,3 +294,123 @@
245294 let upper = 10i64.pow(digits);
246295 rng.random_range((upper / 10)..upper)
247296}
297
298#[utoipa::path(
299 post,
300 path = "/api/v1/auth/register/code",
301 tag = "auth",
302 operation_id = "auth_register_code",
303 summary = "发送注册验证码",
304 description = "校验工作量证明(payload = 邮箱的 UTF-8 字节)与邮箱格式后,把 6 位验证码写入缓存并寄出。缓存键 `auth:code:register:{email}`,有效期由 `verify_code_ttl_secs` 决定,发码限流键 `auth:rate:register:{email}`。",
305 request_body = RegisterCodeRequest,
306 responses(
307 (
308 status = 200,
309 description = "验证码已写入缓存并寄出",
310 body = Envelope<RegisterCodeResponse>,
311 example = json!({"code":200,"message":"success","data":{"expires_in_secs":600}})
312 ),
313 (
314 status = 400,
315 description = "`invalid_email`(邮箱格式)/ `expired`(挑战过期)/ `invalid_payload`(payload 与邮箱不一致)/ `invalid_proof`(未满足难度)",
316 body = Envelope<NoData>,
317 example = json!({"code": "invalid_email", "message": "email is invalid"})
318 ),
319 (
320 status = 409,
321 description = "`email_taken`(邮箱已注册)",
322 body = Envelope<NoData>,
323 example = json!({"code": "email_taken", "message": "email is already taken"})
324 ),
325 (
326 status = 429,
327 description = "`rate_limited`(同一邮箱发码过于频繁)",
328 body = Envelope<NoData>,
329 example = json!({"code": "rate_limited", "message": "too many requests, retry after 60s"})
330 ),
331 (
332 status = 500,
333 description = "`email`(投递失败)/ `cache` / `session` / `serialization` / `internal`",
334 body = Envelope<NoData>,
335 example = json!({"code": "internal", "message": "internal server error"})
336 )
337 )
338)]
339pub async fn register_code(
340 svc: web::Data<crate::AuthService>,
341 session: Session,
342 body: web::Json<RegisterCodeRequest>,
343 request_id: RequestId,
344) -> Result<Envelope<RegisterCodeResponse>, AuthError> {
345 let response = svc
346 .send_register_code(session, body.into_inner(), request_id.0)
347 .await?;
348 Ok(Envelope::ok(response))
349}
350
351#[utoipa::path(
352 post,
353 path = "/api/v1/auth/register",
354 tag = "auth",
355 operation_id = "auth_register",
356 summary = "提交注册",
357 description = "校验工作量证明(payload = 用户名的 UTF-8 字节)、用户名/展示名/出生日期/邮箱、邮箱验证码(键 `auth:code:register:{email}`,尝试上限 `max_verify_attempts`)后,Argon2 重哈希口令并创建用户与初始档案。成功后写会话(user_id、security_version、renew)并记审计事件。",
358 request_body = UserRegisterRequest,
359 responses(
360 (
361 status = 200,
362 description = "注册成功且已登录",
363 body = Envelope<UserRegisterResponse>,
364 example = json!({"code":200,"message":"success","data":{"user_id":1234567,"username":"alice","displayname":"Alice"}})
365 ),
366 (
367 status = 400,
368 description = "`invalid_username` / `invalid_displayname` / `invalid_email` / `invalid_birth_date` / `invalid_password` / `invalid_email_code`(验证码错误或已过期)/ `expired` / `invalid_payload` / `invalid_proof`",
369 body = Envelope<NoData>,
370 example = json!({"code": "invalid_email_code", "message": "email verification code is invalid"})
371 ),
372 (
373 status = 403,
374 description = "`under_minimum_age`(未满最小年龄)",
375 body = Envelope<NoData>,
376 example = json!({"code": "under_minimum_age", "message": "you must be at least 13 years old"})
377 ),
378 (
379 status = 409,
380 description = "`username_taken` / `email_taken`",
381 body = Envelope<NoData>,
382 example = json!({"code": "username_taken", "message": "username is already taken"})
383 ),
384 (
385 status = 429,
386 description = "`email_code_attempts_exceeded`(验证码尝试次数用尽)",
387 body = Envelope<NoData>,
388 example = json!({"code": "email_code_attempts_exceeded", "message": "too many invalid email verification attempts"})
389 ),
390 (
391 status = 500,
392 description = "`password_hash`(口令重哈希失败)/ `database` / `cache` / `session` / `serialization` / `internal`",
393 body = Envelope<NoData>,
394 example = json!({"code": "internal", "message": "internal server error"})
395 )
396 )
397)]
398pub async fn register(
399 svc: web::Data<crate::AuthService>,
400 session: Session,
401 body: web::Json<UserRegisterRequest>,
402 request_id: RequestId,
403) -> Result<Envelope<UserRegisterResponse>, AuthError> {
404 let user_agent = session.user_agent()?;
405 let ip_addr = session.client_ip()?;
406 let response = svc
407 .register(
408 session,
409 body.into_inner(),
410 user_agent,
411 ip_addr,
412 request_id.0,
413 )
414 .await?;
415 Ok(Envelope::ok(response))
416}
Renamed lib/auth/src/handlers/identity/ssh_key/handler.rs → lib/auth/src/handlers/identity/ssh_key.rs View lib/auth/src/handlers/identity/ssh_key.rs
@@ -1,11 +1,10 @@
1use crate::session::{RequestId, Session};
2use actix_web::web;
3use uuid::Uuid;
4
51use crate::envelope::{Envelope, NoData};
62use crate::errors::AuthError;
3use crate::services::ssh_key::types::{SshKeyAddRequest, SshKeySummary};
4use crate::session::{RequestId, Session};
75use crate::share::types::ListQuery;
8use crate::ssh_key::types::{SshKeyAddRequest, SshKeySummary};
6use actix_web::web;
7use uuid::Uuid;
98
109#[utoipa::path(
1110 get,
@@ -40,7 +39,7 @@
4039 )
4140)]
4241pub async fn ssh_key_list(
43 svc: web::Data<crate::AuthSvc>,
42 svc: web::Data<crate::AuthService>,
4443 session: Session,
4544 query: web::Query<ListQuery>,
4645 request_id: RequestId,
@@ -93,7 +92,7 @@
9392 )
9493)]
9594pub async fn ssh_key_add(
96 svc: web::Data<crate::AuthSvc>,
95 svc: web::Data<crate::AuthService>,
9796 session: Session,
9897 body: web::Json<SshKeyAddRequest>,
9998 request_id: RequestId,
@@ -151,7 +150,7 @@
151150 )
152151)]
153152pub async fn ssh_key_delete(
154 svc: web::Data<crate::AuthSvc>,
153 svc: web::Data<crate::AuthService>,
155154 session: Session,
156155 key_id: web::Path<Uuid>,
157156 request_id: RequestId,
Deleted lib/auth/src/internal/bridge/agreement.rs
@@ -1,32 +0,0 @@
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
3use tonic::{Request, Response, Status};
4
5use crate::internal::agreement::AgreementApi;
6use crate::protos::auth::v1::agreement_service_server::AgreementService;
7
8pub struct AgreementClient {
9 api: AgreementApi,
10}
11
12impl AgreementClient {
13 pub fn new(service: AuthSvc) -> Self {
14 Self {
15 api: AgreementApi::new(service),
16 }
17 }
18 pub async fn list_agreements(
19 &mut self,
20 mut request: Request<crate::protos::auth::v1::ListAgreementsRequest>,
21 ) -> Result<Response<crate::protos::auth::v1::ListAgreementsResponse>, Status> {
22 request.extensions_mut().insert(RequestMeta::internal());
23 self.api.list_agreements(request).await
24 }
25 pub async fn check_agreements_accepted(
26 &mut self,
27 mut request: Request<crate::protos::auth::v1::CheckAgreementsAcceptedRequest>,
28 ) -> Result<Response<crate::protos::auth::v1::CheckAgreementsAcceptedResponse>, Status> {
29 request.extensions_mut().insert(RequestMeta::internal());
30 self.api.check_agreements_accepted(request).await
31 }
32}
Deleted lib/auth/src/internal/bridge/authority.rs
@@ -1,90 +0,0 @@
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
3use tonic::{Request, Response, Status};
4
5use crate::internal::authority::AuthorityApi;
6use crate::protos::auth::internal::v1::authority_service_server::AuthorityService;
7
8pub struct AuthorityClient {
9 api: AuthorityApi,
10}
11
12impl AuthorityClient {
13 pub fn new(service: AuthSvc) -> Self {
14 Self {
15 api: AuthorityApi::new(service),
16 }
17 }
18 pub async fn redeem_socket_ticket(
19 &mut self,
20 mut request: Request<crate::protos::auth::internal::v1::RedeemSocketTicketRequest>,
21 ) -> Result<Response<crate::protos::auth::internal::v1::RedeemSocketTicketResponse>, Status>
22 {
23 request.extensions_mut().insert(RequestMeta::internal());
24 self.api.redeem_socket_ticket(request).await
25 }
26 pub async fn validate(
27 &mut self,
28 mut request: Request<crate::protos::auth::internal::v1::ValidateRequest>,
29 ) -> Result<Response<crate::protos::auth::internal::v1::ValidateResponse>, Status> {
30 request.extensions_mut().insert(RequestMeta::internal());
31 self.api.validate(request).await
32 }
33 pub async fn get_user(
34 &mut self,
35 mut request: Request<crate::protos::auth::internal::v1::GetUserRequest>,
36 ) -> Result<Response<crate::protos::auth::internal::v1::GetUserResponse>, Status> {
37 request.extensions_mut().insert(RequestMeta::internal());
38 self.api.get_user(request).await
39 }
40 pub async fn batch_get_user(
41 &mut self,
42 mut request: Request<crate::protos::auth::internal::v1::BatchGetUserRequest>,
43 ) -> Result<Response<crate::protos::auth::internal::v1::BatchGetUserResponse>, Status> {
44 request.extensions_mut().insert(RequestMeta::internal());
45 self.api.batch_get_user(request).await
46 }
47 pub async fn get_session(
48 &mut self,
49 mut request: Request<crate::protos::auth::internal::v1::GetSessionRequest>,
50 ) -> Result<Response<crate::protos::auth::internal::v1::GetSessionResponse>, Status> {
51 request.extensions_mut().insert(RequestMeta::internal());
52 self.api.get_session(request).await
53 }
54 pub async fn update_user(
55 &mut self,
56 mut request: Request<crate::protos::auth::internal::v1::UpdateUserRequest>,
57 ) -> Result<Response<crate::protos::auth::internal::v1::UpdateUserResponse>, Status> {
58 request.extensions_mut().insert(RequestMeta::internal());
59 self.api.update_user(request).await
60 }
61 pub async fn delete_account(
62 &mut self,
63 mut request: Request<crate::protos::auth::internal::v1::DeleteAccountRequest>,
64 ) -> Result<Response<crate::protos::auth::internal::v1::DeleteAccountResponse>, Status> {
65 request.extensions_mut().insert(RequestMeta::internal());
66 self.api.delete_account(request).await
67 }
68 pub async fn cancel_account_deletion(
69 &mut self,
70 mut request: Request<crate::protos::auth::internal::v1::CancelAccountDeletionRequest>,
71 ) -> Result<Response<crate::protos::auth::internal::v1::CancelAccountDeletionResponse>, Status>
72 {
73 request.extensions_mut().insert(RequestMeta::internal());
74 self.api.cancel_account_deletion(request).await
75 }
76 pub async fn purge_account(
77 &mut self,
78 mut request: Request<crate::protos::auth::internal::v1::PurgeAccountRequest>,
79 ) -> Result<Response<crate::protos::auth::internal::v1::PurgeAccountResponse>, Status> {
80 request.extensions_mut().insert(RequestMeta::internal());
81 self.api.purge_account(request).await
82 }
83 pub async fn list_users(
84 &mut self,
85 mut request: Request<crate::protos::auth::internal::v1::ListUsersRequest>,
86 ) -> Result<Response<crate::protos::auth::internal::v1::ListUsersResponse>, Status> {
87 request.extensions_mut().insert(RequestMeta::internal());
88 self.api.list_users(request).await
89 }
90}
Deleted lib/auth/src/internal/bridge/authorization.rs
@@ -1,46 +0,0 @@
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
3use tonic::{Request, Response, Status};
4
5use crate::internal::authorization::AuthorizationApi;
6use crate::protos::auth::v1::authorization_service_server::AuthorizationService;
7
8pub struct AuthorizationClient {
9 api: AuthorizationApi,
10}
11
12impl AuthorizationClient {
13 pub fn new(service: AuthSvc) -> Self {
14 Self {
15 api: AuthorizationApi::new(service),
16 }
17 }
18 pub async fn authorize(
19 &mut self,
20 mut request: Request<crate::protos::auth::v1::AuthorizeRequest>,
21 ) -> Result<Response<crate::protos::auth::v1::AuthorizeResponse>, Status> {
22 request.extensions_mut().insert(RequestMeta::internal());
23 self.api.authorize(request).await
24 }
25 pub async fn batch_authorize(
26 &mut self,
27 mut request: Request<crate::protos::auth::v1::BatchAuthorizeRequest>,
28 ) -> Result<Response<crate::protos::auth::v1::BatchAuthorizeResponse>, Status> {
29 request.extensions_mut().insert(RequestMeta::internal());
30 self.api.batch_authorize(request).await
31 }
32 pub async fn is_platform_admin(
33 &mut self,
34 mut request: Request<crate::protos::auth::v1::IsPlatformAdminRequest>,
35 ) -> Result<Response<crate::protos::auth::v1::IsPlatformAdminResponse>, Status> {
36 request.extensions_mut().insert(RequestMeta::internal());
37 self.api.is_platform_admin(request).await
38 }
39 pub async fn get_capabilities(
40 &mut self,
41 mut request: Request<crate::protos::auth::v1::GetCapabilitiesRequest>,
42 ) -> Result<Response<crate::protos::auth::v1::GetCapabilitiesResponse>, Status> {
43 request.extensions_mut().insert(RequestMeta::internal());
44 self.api.get_capabilities(request).await
45 }
46}
Deleted lib/auth/src/internal/bridge/identity.rs
@@ -1,67 +0,0 @@
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
3use tonic::{Request, Response, Status};
4
5use crate::internal::identity::IdentityApi;
6use crate::protos::auth::v1::identity_service_server::IdentityService;
7
8pub struct IdentityClient {
9 api: IdentityApi,
10}
11
12impl IdentityClient {
13 pub fn new(service: AuthSvc) -> Self {
14 Self {
15 api: IdentityApi::new(service),
16 }
17 }
18 pub async fn authenticate(
19 &mut self,
20 mut request: Request<crate::protos::auth::v1::AuthenticateRequest>,
21 ) -> Result<Response<crate::protos::auth::v1::AuthenticateResponse>, Status> {
22 request.extensions_mut().insert(RequestMeta::internal());
23 self.api.authenticate(request).await
24 }
25 pub async fn verify_credential(
26 &mut self,
27 mut request: Request<crate::protos::auth::v1::VerifyCredentialRequest>,
28 ) -> Result<Response<crate::protos::auth::v1::VerifyCredentialResponse>, Status> {
29 request.extensions_mut().insert(RequestMeta::internal());
30 self.api.verify_credential(request).await
31 }
32 pub async fn get_user_public(
33 &mut self,
34 mut request: Request<crate::protos::auth::v1::GetUserPublicRequest>,
35 ) -> Result<Response<crate::protos::auth::v1::GetUserPublicResponse>, Status> {
36 request.extensions_mut().insert(RequestMeta::internal());
37 self.api.get_user_public(request).await
38 }
39 pub async fn batch_get_user_public(
40 &mut self,
41 mut request: Request<crate::protos::auth::v1::BatchGetUserPublicRequest>,
42 ) -> Result<Response<crate::protos::auth::v1::BatchGetUserPublicResponse>, Status> {
43 request.extensions_mut().insert(RequestMeta::internal());
44 self.api.batch_get_user_public(request).await
45 }
46 pub async fn get_user_status(
47 &mut self,
48 mut request: Request<crate::protos::auth::v1::GetUserStatusRequest>,
49 ) -> Result<Response<crate::protos::auth::v1::GetUserStatusResponse>, Status> {
50 request.extensions_mut().insert(RequestMeta::internal());
51 self.api.get_user_status(request).await
52 }
53 pub async fn ensure_bot_user(
54 &mut self,
55 mut request: Request<crate::protos::auth::v1::EnsureBotUserRequest>,
56 ) -> Result<Response<crate::protos::auth::v1::EnsureBotUserResponse>, Status> {
57 request.extensions_mut().insert(RequestMeta::internal());
58 self.api.ensure_bot_user(request).await
59 }
60 pub async fn change_password(
61 &mut self,
62 mut request: Request<crate::protos::auth::v1::ChangePasswordRequest>,
63 ) -> Result<Response<crate::protos::auth::v1::ChangePasswordResponse>, Status> {
64 request.extensions_mut().insert(RequestMeta::internal());
65 self.api.change_password(request).await
66 }
67}
Deleted lib/auth/src/internal/bridge/mfa.rs
@@ -1,67 +0,0 @@
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
3use tonic::{Request, Response, Status};
4
5use crate::internal::mfa::MfaApi;
6use crate::protos::auth::v1::mfa_service_server::MfaService;
7
8pub struct MfaClient {
9 api: MfaApi,
10}
11
12impl MfaClient {
13 pub fn new(service: AuthSvc) -> Self {
14 Self {
15 api: MfaApi::new(service),
16 }
17 }
18 pub async fn get_mfa_status(
19 &mut self,
20 mut request: Request<crate::protos::auth::v1::GetMfaStatusRequest>,
21 ) -> Result<Response<crate::protos::auth::v1::GetMfaStatusResponse>, Status> {
22 request.extensions_mut().insert(RequestMeta::internal());
23 self.api.get_mfa_status(request).await
24 }
25 pub async fn verify_totp(
26 &mut self,
27 mut request: Request<crate::protos::auth::v1::VerifyTotpRequest>,
28 ) -> Result<Response<crate::protos::auth::v1::VerifyTotpResponse>, Status> {
29 request.extensions_mut().insert(RequestMeta::internal());
30 self.api.verify_totp(request).await
31 }
32 pub async fn redeem_recovery_code(
33 &mut self,
34 mut request: Request<crate::protos::auth::v1::RedeemRecoveryCodeRequest>,
35 ) -> Result<Response<crate::protos::auth::v1::RedeemRecoveryCodeResponse>, Status> {
36 request.extensions_mut().insert(RequestMeta::internal());
37 self.api.redeem_recovery_code(request).await
38 }
39 pub async fn configure_totp(
40 &mut self,
41 mut request: Request<crate::protos::auth::v1::ConfigureTotpRequest>,
42 ) -> Result<Response<crate::protos::auth::v1::ConfigureTotpResponse>, Status> {
43 request.extensions_mut().insert(RequestMeta::internal());
44 self.api.configure_totp(request).await
45 }
46 pub async fn enable_totp(
47 &mut self,
48 mut request: Request<crate::protos::auth::v1::EnableTotpRequest>,
49 ) -> Result<Response<crate::protos::auth::v1::EnableTotpResponse>, Status> {
50 request.extensions_mut().insert(RequestMeta::internal());
51 self.api.enable_totp(request).await
52 }
53 pub async fn disable_totp(
54 &mut self,
55 mut request: Request<crate::protos::auth::v1::DisableTotpRequest>,
56 ) -> Result<Response<crate::protos::auth::v1::DisableTotpResponse>, Status> {
57 request.extensions_mut().insert(RequestMeta::internal());
58 self.api.disable_totp(request).await
59 }
60 pub async fn regenerate_recovery_codes(
61 &mut self,
62 mut request: Request<crate::protos::auth::v1::RegenerateRecoveryCodesRequest>,
63 ) -> Result<Response<crate::protos::auth::v1::RegenerateRecoveryCodesResponse>, Status> {
64 request.extensions_mut().insert(RequestMeta::internal());
65 self.api.regenerate_recovery_codes(request).await
66 }
67}
Deleted lib/auth/src/internal/bridge/mod.rs
@@ -1,17 +0,0 @@
1mod agreement;
2mod authority;
3mod authorization;
4mod identity;
5mod mfa;
6mod organization;
7mod session;
8mod ssh_key;
9
10pub use agreement::AgreementClient;
11pub use authority::AuthorityClient;
12pub use authorization::AuthorizationClient;
13pub use identity::IdentityClient;
14pub use mfa::MfaClient;
15pub use organization::OrganizationClient;
16pub use session::SessionClient;
17pub use ssh_key::SshKeyClient;
Deleted lib/auth/src/internal/bridge/organization.rs
@@ -1,361 +0,0 @@
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
3use tonic::{Request, Response, Status};
4
5use crate::internal::organization::OrganizationApi;
6use crate::protos::auth::v1::organization_service_server::OrganizationService;
7
8pub struct OrganizationClient {
9 api: OrganizationApi,
10}
11
12impl OrganizationClient {
13 pub fn new(service: AuthSvc) -> Self {
14 Self {
15 api: OrganizationApi::new(service),
16 }
17 }
18 pub async fn create_organization(
19 &mut self,
20 mut request: Request<crate::protos::auth::v1::CreateOrganizationRequest>,
21 ) -> Result<Response<crate::protos::auth::v1::CreateOrganizationResponse>, Status> {
22 request.extensions_mut().insert(RequestMeta::internal());
23 self.api.create_organization(request).await
24 }
25 pub async fn get_organization(
26 &mut self,
27 mut request: Request<crate::protos::auth::v1::GetOrganizationRequest>,
28 ) -> Result<Response<crate::protos::auth::v1::GetOrganizationResponse>, Status> {
29 request.extensions_mut().insert(RequestMeta::internal());
30 self.api.get_organization(request).await
31 }
32 pub async fn list_organizations(
33 &mut self,
34 mut request: Request<crate::protos::auth::v1::ListOrganizationsRequest>,
35 ) -> Result<Response<crate::protos::auth::v1::ListOrganizationsResponse>, Status> {
36 request.extensions_mut().insert(RequestMeta::internal());
37 self.api.list_organizations(request).await
38 }
39 pub async fn list_public_organizations(
40 &mut self,
41 mut request: Request<crate::protos::auth::v1::ListPublicOrganizationsRequest>,
42 ) -> Result<Response<crate::protos::auth::v1::ListPublicOrganizationsResponse>, Status> {
43 request.extensions_mut().insert(RequestMeta::internal());
44 self.api.list_public_organizations(request).await
45 }
46 pub async fn update_organization(
47 &mut self,
48 mut request: Request<crate::protos::auth::v1::UpdateOrganizationRequest>,
49 ) -> Result<Response<crate::protos::auth::v1::UpdateOrganizationResponse>, Status> {
50 request.extensions_mut().insert(RequestMeta::internal());
51 self.api.update_organization(request).await
52 }
53 pub async fn archive_organization(
54 &mut self,
55 mut request: Request<crate::protos::auth::v1::ArchiveOrganizationRequest>,
56 ) -> Result<Response<crate::protos::auth::v1::ArchiveOrganizationResponse>, Status> {
57 request.extensions_mut().insert(RequestMeta::internal());
58 self.api.archive_organization(request).await
59 }
60 pub async fn restore_organization(
61 &mut self,
62 mut request: Request<crate::protos::auth::v1::RestoreOrganizationRequest>,
63 ) -> Result<Response<crate::protos::auth::v1::RestoreOrganizationResponse>, Status> {
64 request.extensions_mut().insert(RequestMeta::internal());
65 self.api.restore_organization(request).await
66 }
67 pub async fn delete_organization(
68 &mut self,
69 mut request: Request<crate::protos::auth::v1::DeleteOrganizationRequest>,
70 ) -> Result<Response<crate::protos::auth::v1::DeleteOrganizationResponse>, Status> {
71 request.extensions_mut().insert(RequestMeta::internal());
72 self.api.delete_organization(request).await
73 }
74 pub async fn transfer_ownership(
75 &mut self,
76 mut request: Request<crate::protos::auth::v1::TransferOwnershipRequest>,
77 ) -> Result<Response<crate::protos::auth::v1::TransferOwnershipResponse>, Status> {
78 request.extensions_mut().insert(RequestMeta::internal());
79 self.api.transfer_ownership(request).await
80 }
81 pub async fn add_member(
82 &mut self,
83 mut request: Request<crate::protos::auth::v1::AddMemberRequest>,
84 ) -> Result<Response<crate::protos::auth::v1::AddMemberResponse>, Status> {
85 request.extensions_mut().insert(RequestMeta::internal());
86 self.api.add_member(request).await
87 }
88 pub async fn remove_member(
89 &mut self,
90 mut request: Request<crate::protos::auth::v1::RemoveMemberRequest>,
91 ) -> Result<Response<crate::protos::auth::v1::RemoveMemberResponse>, Status> {
92 request.extensions_mut().insert(RequestMeta::internal());
93 self.api.remove_member(request).await
94 }
95 pub async fn leave_organization(
96 &mut self,
97 mut request: Request<crate::protos::auth::v1::LeaveOrganizationRequest>,
98 ) -> Result<Response<crate::protos::auth::v1::LeaveOrganizationResponse>, Status> {
99 request.extensions_mut().insert(RequestMeta::internal());
100 self.api.leave_organization(request).await
101 }
102 pub async fn list_members(
103 &mut self,
104 mut request: Request<crate::protos::auth::v1::ListMembersRequest>,
105 ) -> Result<Response<crate::protos::auth::v1::ListMembersResponse>, Status> {
106 request.extensions_mut().insert(RequestMeta::internal());
107 self.api.list_members(request).await
108 }
109 pub async fn set_member_role(
110 &mut self,
111 mut request: Request<crate::protos::auth::v1::SetMemberRoleRequest>,
112 ) -> Result<Response<crate::protos::auth::v1::SetMemberRoleResponse>, Status> {
113 request.extensions_mut().insert(RequestMeta::internal());
114 self.api.set_member_role(request).await
115 }
116 pub async fn ban_member(
117 &mut self,
118 mut request: Request<crate::protos::auth::v1::BanMemberRequest>,
119 ) -> Result<Response<crate::protos::auth::v1::BanMemberResponse>, Status> {
120 request.extensions_mut().insert(RequestMeta::internal());
121 self.api.ban_member(request).await
122 }
123 pub async fn unban_member(
124 &mut self,
125 mut request: Request<crate::protos::auth::v1::UnbanMemberRequest>,
126 ) -> Result<Response<crate::protos::auth::v1::UnbanMemberResponse>, Status> {
127 request.extensions_mut().insert(RequestMeta::internal());
128 self.api.unban_member(request).await
129 }
130 pub async fn get_member(
131 &mut self,
132 mut request: Request<crate::protos::auth::v1::GetMemberRequest>,
133 ) -> Result<Response<crate::protos::auth::v1::GetMemberResponse>, Status> {
134 request.extensions_mut().insert(RequestMeta::internal());
135 self.api.get_member(request).await
136 }
137 pub async fn update_member(
138 &mut self,
139 mut request: Request<crate::protos::auth::v1::UpdateMemberRequest>,
140 ) -> Result<Response<crate::protos::auth::v1::UpdateMemberResponse>, Status> {
141 request.extensions_mut().insert(RequestMeta::internal());
142 self.api.update_member(request).await
143 }
144 pub async fn update_self_member(
145 &mut self,
146 mut request: Request<crate::protos::auth::v1::UpdateMemberRequest>,
147 ) -> Result<Response<crate::protos::auth::v1::UpdateMemberResponse>, Status> {
148 request.extensions_mut().insert(RequestMeta::internal());
149 self.api.update_self_member(request).await
150 }
151 pub async fn list_bans(
152 &mut self,
153 mut request: Request<crate::protos::auth::v1::ListBansRequest>,
154 ) -> Result<Response<crate::protos::auth::v1::ListBansResponse>, Status> {
155 request.extensions_mut().insert(RequestMeta::internal());
156 self.api.list_bans(request).await
157 }
158 pub async fn get_ban(
159 &mut self,
160 mut request: Request<crate::protos::auth::v1::GetBanRequest>,
161 ) -> Result<Response<crate::protos::auth::v1::GetBanResponse>, Status> {
162 request.extensions_mut().insert(RequestMeta::internal());
163 self.api.get_ban(request).await
164 }
165 pub async fn update_ban(
166 &mut self,
167 mut request: Request<crate::protos::auth::v1::UpdateBanRequest>,
168 ) -> Result<Response<crate::protos::auth::v1::UpdateBanResponse>, Status> {
169 request.extensions_mut().insert(RequestMeta::internal());
170 self.api.update_ban(request).await
171 }
172 pub async fn get_role(
173 &mut self,
174 mut request: Request<crate::protos::auth::v1::GetRoleRequest>,
175 ) -> Result<Response<crate::protos::auth::v1::GetRoleResponse>, Status> {
176 request.extensions_mut().insert(RequestMeta::internal());
177 self.api.get_role(request).await
178 }
179 pub async fn join_role(
180 &mut self,
181 mut request: Request<crate::protos::auth::v1::JoinRoleRequest>,
182 ) -> Result<Response<crate::protos::auth::v1::JoinRoleResponse>, Status> {
183 request.extensions_mut().insert(RequestMeta::internal());
184 self.api.join_role(request).await
185 }
186 pub async fn leave_role(
187 &mut self,
188 mut request: Request<crate::protos::auth::v1::LeaveRoleRequest>,
189 ) -> Result<Response<crate::protos::auth::v1::LeaveRoleResponse>, Status> {
190 request.extensions_mut().insert(RequestMeta::internal());
191 self.api.leave_role(request).await
192 }
193 pub async fn create_role(
194 &mut self,
195 mut request: Request<crate::protos::auth::v1::CreateRoleRequest>,
196 ) -> Result<Response<crate::protos::auth::v1::CreateRoleResponse>, Status> {
197 request.extensions_mut().insert(RequestMeta::internal());
198 self.api.create_role(request).await
199 }
200 pub async fn update_role(
201 &mut self,
202 mut request: Request<crate::protos::auth::v1::UpdateRoleRequest>,
203 ) -> Result<Response<crate::protos::auth::v1::UpdateRoleResponse>, Status> {
204 request.extensions_mut().insert(RequestMeta::internal());
205 self.api.update_role(request).await
206 }
207 pub async fn delete_role(
208 &mut self,
209 mut request: Request<crate::protos::auth::v1::DeleteRoleRequest>,
210 ) -> Result<Response<crate::protos::auth::v1::DeleteRoleResponse>, Status> {
211 request.extensions_mut().insert(RequestMeta::internal());
212 self.api.delete_role(request).await
213 }
214 pub async fn list_roles(
215 &mut self,
216 mut request: Request<crate::protos::auth::v1::ListRolesRequest>,
217 ) -> Result<Response<crate::protos::auth::v1::ListRolesResponse>, Status> {
218 request.extensions_mut().insert(RequestMeta::internal());
219 self.api.list_roles(request).await
220 }
221 pub async fn set_role_permissions(
222 &mut self,
223 mut request: Request<crate::protos::auth::v1::SetRolePermissionsRequest>,
224 ) -> Result<Response<crate::protos::auth::v1::SetRolePermissionsResponse>, Status> {
225 request.extensions_mut().insert(RequestMeta::internal());
226 self.api.set_role_permissions(request).await
227 }
228 pub async fn assign_role(
229 &mut self,
230 mut request: Request<crate::protos::auth::v1::AssignRoleRequest>,
231 ) -> Result<Response<crate::protos::auth::v1::AssignRoleResponse>, Status> {
232 request.extensions_mut().insert(RequestMeta::internal());
233 self.api.assign_role(request).await
234 }
235 pub async fn unassign_role(
236 &mut self,
237 mut request: Request<crate::protos::auth::v1::UnassignRoleRequest>,
238 ) -> Result<Response<crate::protos::auth::v1::UnassignRoleResponse>, Status> {
239 request.extensions_mut().insert(RequestMeta::internal());
240 self.api.unassign_role(request).await
241 }
242 pub async fn set_member_permissions(
243 &mut self,
244 mut request: Request<crate::protos::auth::v1::SetMemberPermissionsRequest>,
245 ) -> Result<Response<crate::protos::auth::v1::SetMemberPermissionsResponse>, Status> {
246 request.extensions_mut().insert(RequestMeta::internal());
247 self.api.set_member_permissions(request).await
248 }
249 pub async fn check_permission(
250 &mut self,
251 mut request: Request<crate::protos::auth::v1::CheckPermissionRequest>,
252 ) -> Result<Response<crate::protos::auth::v1::CheckPermissionResponse>, Status> {
253 request.extensions_mut().insert(RequestMeta::internal());
254 self.api.check_permission(request).await
255 }
256 pub async fn check_permissions(
257 &mut self,
258 mut request: Request<crate::protos::auth::v1::CheckPermissionsRequest>,
259 ) -> Result<Response<crate::protos::auth::v1::CheckPermissionsResponse>, Status> {
260 request.extensions_mut().insert(RequestMeta::internal());
261 self.api.check_permissions(request).await
262 }
263 pub async fn create_invite(
264 &mut self,
265 mut request: Request<crate::protos::auth::v1::CreateInviteRequest>,
266 ) -> Result<Response<crate::protos::auth::v1::CreateInviteResponse>, Status> {
267 request.extensions_mut().insert(RequestMeta::internal());
268 self.api.create_invite(request).await
269 }
270 pub async fn revoke_invite(
271 &mut self,
272 mut request: Request<crate::protos::auth::v1::RevokeInviteRequest>,
273 ) -> Result<Response<crate::protos::auth::v1::RevokeInviteResponse>, Status> {
274 request.extensions_mut().insert(RequestMeta::internal());
275 self.api.revoke_invite(request).await
276 }
277 pub async fn list_invites(
278 &mut self,
279 mut request: Request<crate::protos::auth::v1::ListInvitesRequest>,
280 ) -> Result<Response<crate::protos::auth::v1::ListInvitesResponse>, Status> {
281 request.extensions_mut().insert(RequestMeta::internal());
282 self.api.list_invites(request).await
283 }
284 pub async fn redeem_invite(
285 &mut self,
286 mut request: Request<crate::protos::auth::v1::RedeemInviteRequest>,
287 ) -> Result<Response<crate::protos::auth::v1::RedeemInviteResponse>, Status> {
288 request.extensions_mut().insert(RequestMeta::internal());
289 self.api.redeem_invite(request).await
290 }
291 pub async fn create_join_rule(
292 &mut self,
293 mut request: Request<crate::protos::auth::v1::CreateJoinRuleRequest>,
294 ) -> Result<Response<crate::protos::auth::v1::CreateJoinRuleResponse>, Status> {
295 request.extensions_mut().insert(RequestMeta::internal());
296 self.api.create_join_rule(request).await
297 }
298 pub async fn update_join_rule(
299 &mut self,
300 mut request: Request<crate::protos::auth::v1::UpdateJoinRuleRequest>,
301 ) -> Result<Response<crate::protos::auth::v1::UpdateJoinRuleResponse>, Status> {
302 request.extensions_mut().insert(RequestMeta::internal());
303 self.api.update_join_rule(request).await
304 }
305 pub async fn delete_join_rule(
306 &mut self,
307 mut request: Request<crate::protos::auth::v1::DeleteJoinRuleRequest>,
308 ) -> Result<Response<crate::protos::auth::v1::DeleteJoinRuleResponse>, Status> {
309 request.extensions_mut().insert(RequestMeta::internal());
310 self.api.delete_join_rule(request).await
311 }
312 pub async fn list_join_rules(
313 &mut self,
314 mut request: Request<crate::protos::auth::v1::ListJoinRulesRequest>,
315 ) -> Result<Response<crate::protos::auth::v1::ListJoinRulesResponse>, Status> {
316 request.extensions_mut().insert(RequestMeta::internal());
317 self.api.list_join_rules(request).await
318 }
319 pub async fn create_join_request(
320 &mut self,
321 mut request: Request<crate::protos::auth::v1::CreateJoinRequestRequest>,
322 ) -> Result<Response<crate::protos::auth::v1::CreateJoinRequestResponse>, Status> {
323 request.extensions_mut().insert(RequestMeta::internal());
324 self.api.create_join_request(request).await
325 }
326 pub async fn list_join_requests(
327 &mut self,
328 mut request: Request<crate::protos::auth::v1::ListJoinRequestsRequest>,
329 ) -> Result<Response<crate::protos::auth::v1::ListJoinRequestsResponse>, Status> {
330 request.extensions_mut().insert(RequestMeta::internal());
331 self.api.list_join_requests(request).await
332 }
333 pub async fn withdraw_join_request(
334 &mut self,
335 mut request: Request<crate::protos::auth::v1::WithdrawJoinRequestRequest>,
336 ) -> Result<Response<crate::protos::auth::v1::WithdrawJoinRequestResponse>, Status> {
337 request.extensions_mut().insert(RequestMeta::internal());
338 self.api.withdraw_join_request(request).await
339 }
340 pub async fn approve_join_request(
341 &mut self,
342 mut request: Request<crate::protos::auth::v1::ApproveJoinRequestRequest>,
343 ) -> Result<Response<crate::protos::auth::v1::ApproveJoinRequestResponse>, Status> {
344 request.extensions_mut().insert(RequestMeta::internal());
345 self.api.approve_join_request(request).await
346 }
347 pub async fn reject_join_request(
348 &mut self,
349 mut request: Request<crate::protos::auth::v1::RejectJoinRequestRequest>,
350 ) -> Result<Response<crate::protos::auth::v1::RejectJoinRequestResponse>, Status> {
351 request.extensions_mut().insert(RequestMeta::internal());
352 self.api.reject_join_request(request).await
353 }
354 pub async fn ensure_bot_member(
355 &mut self,
356 mut request: Request<crate::protos::auth::v1::EnsureBotMemberRequest>,
357 ) -> Result<Response<crate::protos::auth::v1::EnsureBotMemberResponse>, Status> {
358 request.extensions_mut().insert(RequestMeta::internal());
359 self.api.ensure_bot_member(request).await
360 }
361}
Deleted lib/auth/src/internal/bridge/session.rs
@@ -1,46 +0,0 @@
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
3use tonic::{Request, Response, Status};
4
5use crate::internal::session::SessionApi;
6use crate::protos::auth::v1::session_service_server::SessionService;
7
8pub struct SessionClient {
9 api: SessionApi,
10}
11
12impl SessionClient {
13 pub fn new(service: AuthSvc) -> Self {
14 Self {
15 api: SessionApi::new(service),
16 }
17 }
18 pub async fn list_user_sessions(
19 &mut self,
20 mut request: Request<crate::protos::auth::v1::ListUserSessionsRequest>,
21 ) -> Result<Response<crate::protos::auth::v1::ListUserSessionsResponse>, Status> {
22 request.extensions_mut().insert(RequestMeta::internal());
23 self.api.list_user_sessions(request).await
24 }
25 pub async fn revoke_session(
26 &mut self,
27 mut request: Request<crate::protos::auth::v1::RevokeSessionRequest>,
28 ) -> Result<Response<crate::protos::auth::v1::RevokeSessionResponse>, Status> {
29 request.extensions_mut().insert(RequestMeta::internal());
30 self.api.revoke_session(request).await
31 }
32 pub async fn revoke_user_sessions(
33 &mut self,
34 mut request: Request<crate::protos::auth::v1::RevokeUserSessionsRequest>,
35 ) -> Result<Response<crate::protos::auth::v1::RevokeUserSessionsResponse>, Status> {
36 request.extensions_mut().insert(RequestMeta::internal());
37 self.api.revoke_user_sessions(request).await
38 }
39 pub async fn bump_security_version(
40 &mut self,
41 mut request: Request<crate::protos::auth::v1::BumpSecurityVersionRequest>,
42 ) -> Result<Response<crate::protos::auth::v1::BumpSecurityVersionResponse>, Status> {
43 request.extensions_mut().insert(RequestMeta::internal());
44 self.api.bump_security_version(request).await
45 }
46}
Deleted lib/auth/src/internal/bridge/ssh_key.rs
@@ -1,46 +0,0 @@
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
3use tonic::{Request, Response, Status};
4
5use crate::internal::ssh_key::SshKeyApi;
6use crate::protos::auth::v1::ssh_key_service_server::SshKeyService;
7
8pub struct SshKeyClient {
9 api: SshKeyApi,
10}
11
12impl SshKeyClient {
13 pub fn new(service: AuthSvc) -> Self {
14 Self {
15 api: SshKeyApi::new(service),
16 }
17 }
18 pub async fn lookup_ssh_key(
19 &mut self,
20 mut request: Request<crate::protos::auth::v1::LookupSshKeyRequest>,
21 ) -> Result<Response<crate::protos::auth::v1::LookupSshKeyResponse>, Status> {
22 request.extensions_mut().insert(RequestMeta::internal());
23 self.api.lookup_ssh_key(request).await
24 }
25 pub async fn add_ssh_key(
26 &mut self,
27 mut request: Request<crate::protos::auth::v1::AddSshKeyRequest>,
28 ) -> Result<Response<crate::protos::auth::v1::AddSshKeyResponse>, Status> {
29 request.extensions_mut().insert(RequestMeta::internal());
30 self.api.add_ssh_key(request).await
31 }
32 pub async fn list_user_ssh_keys(
33 &mut self,
34 mut request: Request<crate::protos::auth::v1::ListUserSshKeysRequest>,
35 ) -> Result<Response<crate::protos::auth::v1::ListUserSshKeysResponse>, Status> {
36 request.extensions_mut().insert(RequestMeta::internal());
37 self.api.list_user_ssh_keys(request).await
38 }
39 pub async fn revoke_ssh_key(
40 &mut self,
41 mut request: Request<crate::protos::auth::v1::RevokeSshKeyRequest>,
42 ) -> Result<Response<crate::protos::auth::v1::RevokeSshKeyResponse>, Status> {
43 request.extensions_mut().insert(RequestMeta::internal());
44 self.api.revoke_ssh_key(request).await
45 }
46}
Deleted lib/auth/src/internal/agreement.rs
@@ -1,263 +0,0 @@
1use crate::protos::auth::v1::agreement_service_server::{AgreementService, AgreementServiceServer};
2use crate::protos::auth::v1::check_agreements_accepted_response::Record;
3use crate::protos::auth::v1::list_agreements_response::AgreementDoc;
4use crate::protos::auth::v1::{
5 CheckAgreementsAcceptedRequest, CheckAgreementsAcceptedResponse, ListAgreementsRequest,
6 ListAgreementsResponse,
7};
8use serde::{Deserialize, Serialize};
9use tonic::{Request, Response, Status};
10
11use crate::AuthSvc;
12use crate::errors::{AuthError, AuthResult};
13use crate::internal::{context, convert, error, store};
14
15pub const DEFAULT_LOCALE: &str = "zh-CN";
16pub const SUMMARY_MAX_CHARS: usize = 160;
17pub const MAX_CHECK_KEYS: usize = 32;
18pub const ACCEPTANCE_PREFIX: &str = "auth:agreement:";
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct Acceptance {
22 pub version: String,
23 pub accepted_at: u64,
24}
25
26pub fn acceptance_key(user_id: i64, key: &str) -> String {
27 format!("{ACCEPTANCE_PREFIX}{user_id}:{key}")
28}
29
30pub async fn acceptance_of(
31 cache: &crate::cache::Cache,
32 user_id: i64,
33 key: &str,
34) -> AuthResult<Option<Acceptance>> {
35 Ok(cache.get_json(&acceptance_key(user_id, key)).await?)
36}
37
38pub async fn record_acceptance(
39 cache: &crate::cache::Cache,
40 user_id: i64,
41 key: &str,
42 version: &str,
43) -> AuthResult<()> {
44 let record = Acceptance {
45 version: version.to_owned(),
46 accepted_at: crate::share::crypto::now_secs(),
47 };
48 cache
49 .set_json(&acceptance_key(user_id, key), &record)
50 .await?;
51 Ok(())
52}
53
54pub async fn pending_keys(svc: &AuthSvc, user_id: i64) -> AuthResult<Vec<String>> {
55 let cache = svc.cache();
56 let mut pending = Vec::new();
57 for doc in &svc.auth().agreements {
58 let stored = acceptance_of(cache, user_id, &doc.key).await?;
59 let up_to_date = stored
60 .as_ref()
61 .is_some_and(|record| record.version == doc.version);
62 if !up_to_date {
63 pending.push(doc.key.clone());
64 }
65 }
66 Ok(pending)
67}
68
69#[derive(Clone)]
70pub struct AgreementApi {
71 svc: AuthSvc,
72}
73
74impl AgreementApi {
75 pub fn new(svc: AuthSvc) -> Self {
76 Self { svc }
77 }
78
79 pub fn server(
80 self,
81 ) -> tonic::service::interceptor::InterceptedService<
82 AgreementServiceServer<Self>,
83 context::Interceptor,
84 > {
85 let gate = context::Interceptor::new(self.svc.auth().rpc_key.clone());
86 AgreementServiceServer::with_interceptor(self, gate)
87 }
88}
89
90#[tonic::async_trait]
91impl AgreementService for AgreementApi {
92 async fn list_agreements(
93 &self,
94 request: Request<ListAgreementsRequest>,
95 ) -> Result<Response<ListAgreementsResponse>, Status> {
96 // 认证与鉴权统一走 meta():即使只读公开数据,也要经过拦截器与服务作用域校验。
97 let meta = context::meta(&request).await?;
98 let timer = crate::oltp::metrics::ActiveRpcGuard::new("auth.v1.AgreementService");
99 let result = self
100 .list_agreements_inner(meta.request_id(), request.into_inner())
101 .await;
102 timer.finish("ListAgreements", &result);
103 result
104 }
105
106 async fn check_agreements_accepted(
107 &self,
108 request: Request<CheckAgreementsAcceptedRequest>,
109 ) -> Result<Response<CheckAgreementsAcceptedResponse>, Status> {
110 let meta = context::meta(&request).await?;
111 let timer = crate::oltp::metrics::ActiveRpcGuard::new("auth.v1.AgreementService");
112 let result = self
113 .check_agreements_accepted_inner(meta.request_id(), request.into_inner())
114 .await;
115 timer.finish("CheckAgreementsAccepted", &result);
116 result
117 }
118}
119
120impl AgreementApi {
121 #[tracing::instrument(skip_all, fields(request_id = %_request_id.unwrap_or_default()))]
122 async fn list_agreements_inner(
123 &self,
124 _request_id: Option<&str>,
125 payload: ListAgreementsRequest,
126 ) -> Result<Response<ListAgreementsResponse>, Status> {
127 let requested_locale = payload.locale.trim();
128 let locale = if requested_locale.is_empty() {
129 DEFAULT_LOCALE
130 } else {
131 requested_locale
132 };
133 let include_content = payload.include_content;
134 let agreements = self
135 .svc
136 .auth()
137 .agreements
138 .iter()
139 .map(|doc| AgreementDoc {
140 key: doc.key.clone(),
141 version: doc.version.clone(),
142 title: doc.title.clone(),
143 summary: summary_of(&doc.content_md),
144 content_md: if include_content {
145 doc.content_md.clone()
146 } else {
147 String::new()
148 },
149 locale: locale.to_owned(),
150 effective_at: version_date(&doc.version).and_then(convert::date_timestamp),
151 })
152 .collect();
153 Ok(Response::new(ListAgreementsResponse { agreements }))
154 }
155
156 #[tracing::instrument(skip_all, fields(request_id = %request_id.unwrap_or_default()))]
157 async fn check_agreements_accepted_inner(
158 &self,
159 request_id: Option<&str>,
160 payload: CheckAgreementsAcceptedRequest,
161 ) -> Result<Response<CheckAgreementsAcceptedResponse>, Status> {
162 if payload.user_id <= 0 {
163 return Err(error::invalid_argument(
164 "user_id must be positive",
165 request_id,
166 ));
167 }
168 let keys = &payload.keys;
169 if keys.len() > MAX_CHECK_KEYS {
170 return Err(error::invalid_argument(
171 format!("at most {MAX_CHECK_KEYS} agreement keys can be checked at once"),
172 request_id,
173 ));
174 }
175 let configured = self.svc.auth().agreements.clone();
176 if let Some(unknown) = keys
177 .iter()
178 .find(|key| !configured.iter().any(|doc| doc.key == **key))
179 {
180 return Err(error::status(
181 &AuthError::AgreementNotFound {
182 key: unknown.clone(),
183 },
184 request_id,
185 ));
186 }
187 store::load_active_user(self.svc.db(), payload.user_id)
188 .await
189 .map_err(|failure| error::status(&failure, request_id))?;
190 let selected = if keys.is_empty() {
191 configured.iter().collect::<Vec<_>>()
192 } else {
193 keys.iter()
194 .filter_map(|key| configured.iter().find(|doc| doc.key == *key))
195 .collect::<Vec<_>>()
196 };
197 let accept_any_version = payload.accept_any_version;
198 let cache = self.svc.cache();
199 let mut missing_keys = Vec::new();
200 let mut outdated_keys = Vec::new();
201 let mut records = Vec::with_capacity(selected.len());
202 for doc in selected {
203 let stored = acceptance_of(cache, payload.user_id, &doc.key)
204 .await
205 .map_err(|failure| error::status(&failure, request_id))?;
206 let up_to_date = match &stored {
207 Some(record) if accept_any_version => true,
208 Some(record) => record.version == doc.version,
209 None => false,
210 };
211 match &stored {
212 None => missing_keys.push(doc.key.clone()),
213 Some(_) if !up_to_date => outdated_keys.push(doc.key.clone()),
214 Some(_) => {}
215 }
216 records.push(Record {
217 key: doc.key.clone(),
218 accepted_version: stored
219 .as_ref()
220 .map(|record| record.version.clone())
221 .unwrap_or_default(),
222 current_version: doc.version.clone(),
223 up_to_date,
224 accepted_at: stored.as_ref().and_then(|record| {
225 chrono::DateTime::from_timestamp(record.accepted_at as i64, 0)
226 .map(convert::timestamp_from_datetime)
227 }),
228 });
229 }
230 Ok(Response::new(CheckAgreementsAcceptedResponse {
231 accepted: missing_keys.is_empty() && outdated_keys.is_empty(),
232 missing_keys,
233 outdated_keys,
234 records,
235 }))
236 }
237}
238
239pub fn summary_of(content_md: &str) -> String {
240 let mut summary = String::new();
241 for line in content_md.lines() {
242 let line = line.trim().trim_start_matches('#').trim();
243 if line.is_empty() {
244 continue;
245 }
246 if !summary.is_empty() {
247 summary.push(' ');
248 }
249 summary.push_str(line);
250 if summary.chars().count() >= SUMMARY_MAX_CHARS {
251 break;
252 }
253 }
254 let mut truncated: String = summary.chars().take(SUMMARY_MAX_CHARS).collect();
255 if summary.chars().count() > SUMMARY_MAX_CHARS {
256 truncated.push('…');
257 }
258 truncated
259}
260
261pub fn version_date(version: &str) -> Option<chrono::NaiveDate> {
262 chrono::NaiveDate::parse_from_str(version.trim(), "%Y-%m-%d").ok()
263}
Deleted lib/auth/src/internal/audit.rs
@@ -1,447 +0,0 @@
1use crate::models::users::user_events::{self, UserEventType};
2use crate::protos::auth::internal::v1::audit_service_server::{AuditService, AuditServiceServer};
3use crate::protos::auth::internal::v1::*;
4use chrono::{Duration, Utc};
5use prost_types::Timestamp;
6use sea_orm::sea_query::Condition;
7use sea_orm::{
8 ColumnTrait, EntityTrait, ExprTrait, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect,
9};
10use tonic::{Request, Response, Status};
11
12use crate::AuthSvc;
13use crate::errors::AuthError;
14use crate::internal::{context, convert, error, store};
15
16const DEFAULT_EXPORT_BATCH: u32 = 500;
17const MAX_EXPORT_BATCH: u32 = 5_000;
18const MAX_EXPORT_RECORDS: u64 = 1_000_000;
19const EXPORT_CHANNEL_CAPACITY: usize = 256;
20const PURGE_BATCH: u32 = 5_000;
21const MAX_RETENTION_DAYS: u64 = 3_650;
22const MAX_PURGE_RECORDS: u64 = 1_000_000;
23
24pub struct EventStream {
25 rx: tokio::sync::mpsc::Receiver<Result<AuthEventRecord, Status>>,
26}
27
28impl tonic::codegen::tokio_stream::Stream for EventStream {
29 type Item = Result<AuthEventRecord, Status>;
30
31 fn poll_next(
32 mut self: std::pin::Pin<&mut Self>,
33 cx: &mut std::task::Context<'_>,
34 ) -> std::task::Poll<Option<Self::Item>> {
35 self.rx.poll_recv(cx)
36 }
37}
38
39#[derive(Clone)]
40pub struct AuditApi {
41 svc: AuthSvc,
42}
43
44fn kind_name(kind: &UserEventType) -> &'static str {
45 match kind {
46 UserEventType::Register => "register",
47 UserEventType::Login => "login",
48 UserEventType::Logout => "logout",
49 UserEventType::PasswordChange => "password_change",
50 UserEventType::ProfileUpdate => "profile_update",
51 UserEventType::TwoFactorEnabled => "two_factor_enabled",
52 UserEventType::TwoFactorDisabled => "two_factor_disabled",
53 UserEventType::FailedLoginAttempt => "failed_login_attempt",
54 UserEventType::AccountLocked => "account_locked",
55 UserEventType::AccountUnlocked => "account_unlocked",
56 UserEventType::EmailChange => "email_change",
57 UserEventType::EmailVerified => "email_verified",
58 UserEventType::RoleChanged => "role_changed",
59 UserEventType::PasswordResetRequested => "password_reset_requested",
60 UserEventType::PublicKeyAdded => "public_key_added",
61 UserEventType::PublicKeyRemoved => "public_key_removed",
62 UserEventType::AccessKeyAdded => "access_key_added",
63 UserEventType::AccessKeyRemoved => "access_key_removed",
64 UserEventType::Follow => "follow",
65 UserEventType::Unfollow => "unfollow",
66 UserEventType::Block => "block",
67 UserEventType::Unblock => "unblock",
68 }
69}
70
71fn parse_kind(name: &str) -> Option<UserEventType> {
72 let value = name.trim().to_ascii_lowercase();
73 Some(match value.as_str() {
74 "register" => UserEventType::Register,
75 "login" => UserEventType::Login,
76 "logout" => UserEventType::Logout,
77 "password_change" => UserEventType::PasswordChange,
78 "profile_update" => UserEventType::ProfileUpdate,
79 "two_factor_enabled" => UserEventType::TwoFactorEnabled,
80 "two_factor_disabled" => UserEventType::TwoFactorDisabled,
81 "failed_login_attempt" => UserEventType::FailedLoginAttempt,
82 "account_locked" => UserEventType::AccountLocked,
83 "account_unlocked" => UserEventType::AccountUnlocked,
84 "email_change" => UserEventType::EmailChange,
85 "email_verified" => UserEventType::EmailVerified,
86 "role_changed" => UserEventType::RoleChanged,
87 "password_reset_requested" => UserEventType::PasswordResetRequested,
88 "public_key_added" => UserEventType::PublicKeyAdded,
89 "public_key_removed" => UserEventType::PublicKeyRemoved,
90 "access_key_added" => UserEventType::AccessKeyAdded,
91 "access_key_removed" => UserEventType::AccessKeyRemoved,
92 "follow" => UserEventType::Follow,
93 "unfollow" => UserEventType::Unfollow,
94 "block" => UserEventType::Block,
95 "unblock" => UserEventType::Unblock,
96 _ => return None,
97 })
98}
99
100fn kinds_of(raw: &[String], request_id: Option<&str>) -> Result<Vec<UserEventType>, Status> {
101 let mut kinds = Vec::with_capacity(raw.len());
102 for name in raw {
103 match parse_kind(name) {
104 Some(kind) => kinds.push(kind),
105 None => {
106 return Err(error::invalid_argument(
107 format!("unknown event kind: {}", name.trim()),
108 request_id,
109 ));
110 }
111 }
112 }
113 Ok(kinds)
114}
115
116fn record(row: &user_events::Model) -> AuthEventRecord {
117 AuthEventRecord {
118 event_id: row.id.to_string(),
119 kind: kind_name(&row.event_type).to_owned(),
120 subject_user_id: row.user_id,
121 actor_user_id: row.actor_user_id.unwrap_or(0),
122 occurred_at: convert::datetime_opt(Some(row.occurred_at)),
123 client_ip: row.ip_address.clone().unwrap_or_default(),
124 user_agent: row.user_agent.clone().unwrap_or_default(),
125 trace_id: row.trace_id.clone().unwrap_or_default(),
126 details_json: row
127 .details
128 .as_ref()
129 .map(|details| details.to_string())
130 .unwrap_or_default(),
131 credential_fingerprint: String::new(),
132 }
133}
134
135#[allow(clippy::too_many_arguments)]
136fn base_query<Q: QueryFilter>(
137 query: Q,
138 user_id: i64,
139 actor_user_id: i64,
140 kinds: &[UserEventType],
141 from: Option<&Timestamp>,
142 to: Option<&Timestamp>,
143 request_id: Option<&str>,
144) -> Result<Q, Status> {
145 let mut query = query;
146 if user_id > 0 {
147 query = query.filter(user_events::Column::UserId.eq(user_id));
148 }
149 if actor_user_id > 0 {
150 query = query.filter(user_events::Column::ActorUserId.eq(actor_user_id));
151 }
152 if !kinds.is_empty() {
153 query = query.filter(user_events::Column::EventType.is_in(kinds.to_vec()));
154 }
155 if let Some(from) = from {
156 let value = convert::datetime(from)
157 .ok_or_else(|| error::invalid_argument("from is malformed", request_id))?;
158 query = query.filter(user_events::Column::OccurredAt.gte(value));
159 }
160 if let Some(to) = to {
161 let value = convert::datetime(to)
162 .ok_or_else(|| error::invalid_argument("to is malformed", request_id))?;
163 query = query.filter(user_events::Column::OccurredAt.lte(value));
164 }
165 Ok(query)
166}
167
168impl AuditApi {
169 pub fn new(svc: AuthSvc) -> Self {
170 Self { svc }
171 }
172
173 pub fn server(
174 self,
175 ) -> tonic::service::interceptor::InterceptedService<
176 AuditServiceServer<Self>,
177 context::Interceptor,
178 > {
179 let gate = context::Interceptor::new(self.svc.auth().rpc_key.clone());
180 AuditServiceServer::with_interceptor(self, gate)
181 }
182}
183
184#[tonic::async_trait]
185impl AuditService for AuditApi {
186 type ExportAuthEventsStream = EventStream;
187
188 async fn list_auth_events(
189 &self,
190 request: Request<ListAuthEventsRequest>,
191 ) -> Result<Response<ListAuthEventsResponse>, Status> {
192 let meta = context::meta(&request).await?;
193 let timer = crate::oltp::metrics::ActiveRpcGuard::new("auth.v1.AuditService");
194 let result = self
195 .list_auth_events_inner(meta.request_id(), request.into_inner())
196 .await;
197 timer.finish("ListAuthEvents", &result);
198 result
199 }
200
201
202
203 async fn export_auth_events(
204 &self,
205 request: Request<ExportAuthEventsRequest>,
206 ) -> Result<Response<<AuditApi as crate::protos::auth::internal::v1::audit_service_server::AuditService>::ExportAuthEventsStream>, Status>{
207 let meta = context::meta(&request).await?;
208 let timer = crate::oltp::metrics::ActiveRpcGuard::new("auth.v1.AuditService");
209 let result = self
210 .export_auth_events_inner(meta.request_id(), request.into_inner())
211 .await;
212 timer.finish("ExportAuthEvents", &result);
213 result
214 }
215
216 async fn purge_auth_events(
217 &self,
218 request: Request<PurgeAuthEventsRequest>,
219 ) -> Result<Response<PurgeAuthEventsResponse>, Status> {
220 let meta = context::meta(&request).await?;
221 let timer = crate::oltp::metrics::ActiveRpcGuard::new("auth.v1.AuditService");
222 let result = self
223 .purge_auth_events_inner(meta.request_id(), request.into_inner())
224 .await;
225 timer.finish("PurgeAuthEvents", &result);
226 result
227 }
228}
229impl AuditApi {
230 #[tracing::instrument(skip_all, fields(request_id = %request_id.unwrap_or_default()))]
231 async fn list_auth_events_inner(
232 &self,
233 request_id: Option<&str>,
234 request: ListAuthEventsRequest,
235 ) -> Result<Response<ListAuthEventsResponse>, Status> {
236 let kinds = kinds_of(&request.kinds, request_id)?;
237 let page = request.page.unwrap_or_default();
238 let offset = store::parse_page_token(&page.page_token)
239 .ok_or_else(|| error::invalid_argument("page_token is malformed", request_id))?;
240 let limit = convert::page_size(page.page_size) as u64;
241 let query = base_query(
242 user_events::Entity::find(),
243 request.user_id,
244 request.actor_user_id,
245 &kinds,
246 request.from.as_ref(),
247 request.to.as_ref(),
248 request_id,
249 )?;
250 let rows = query
251 .order_by_desc(user_events::Column::OccurredAt)
252 .limit(limit)
253 .offset(offset)
254 .all(self.svc.db().read())
255 .await
256 .map_err(|err| error::status(&AuthError::from(err), request_id))?;
257 let next_page_token = if limit > 0 && rows.len() as u64 == limit {
258 store::page_token(offset + rows.len() as u64)
259 } else {
260 String::new()
261 };
262 let events = rows.iter().map(record).collect();
263 Ok(Response::new(ListAuthEventsResponse {
264 events,
265 page: Some(crate::protos::auth::common::v1::PageResponse { next_page_token }),
266 }))
267 }
268 #[tracing::instrument(skip_all, fields(request_id = %request_id.unwrap_or_default()))]
269 async fn export_auth_events_inner(
270 &self,
271 request_id: Option<&str>,
272 request: ExportAuthEventsRequest,
273 ) -> Result<Response<<AuditApi as crate::protos::auth::internal::v1::audit_service_server::AuditService>::ExportAuthEventsStream>, Status>{
274 let kinds = kinds_of(&request.kinds, request_id)?;
275 let batch_size = if request.batch_size == 0 {
276 DEFAULT_EXPORT_BATCH
277 } else {
278 Ord::min(request.batch_size, MAX_EXPORT_BATCH)
279 } as u64;
280 let base = base_query(
281 user_events::Entity::find(),
282 0,
283 0,
284 &kinds,
285 request.from.as_ref(),
286 request.to.as_ref(),
287 request_id,
288 )?;
289 let db = self.svc.db().clone();
290 let request_id = request_id.map(str::to_owned);
291 let (tx, rx) = tokio::sync::mpsc::channel(EXPORT_CHANNEL_CAPACITY);
292 // keyset 分页:以上一页最后一行的 (occurred_at, id) 为游标,避免深 offset O(n²)。
293 tokio::spawn(async move {
294 let mut cursor: Option<(chrono::DateTime<chrono::Utc>, uuid::Uuid)> = None;
295 let mut sent = 0u64;
296 while sent < MAX_EXPORT_RECORDS {
297 let limit = Ord::min(batch_size, MAX_EXPORT_RECORDS - sent);
298 let mut query = base
299 .clone()
300 .order_by_asc(user_events::Column::OccurredAt)
301 .order_by_asc(user_events::Column::Id)
302 .limit(limit);
303 if let Some((occurred_at, id)) = cursor {
304 query = query.filter(
305 user_events::Column::OccurredAt.gt(occurred_at).or(
306 user_events::Column::OccurredAt
307 .eq(occurred_at)
308 .and(user_events::Column::Id.gt(id)),
309 ),
310 );
311 }
312 let rows = query.all(db.read()).await;
313 match rows {
314 Ok(rows) => {
315 let fetched = rows.len() as u64;
316 if let Some(last) = rows.last() {
317 cursor = Some((last.occurred_at, last.id));
318 }
319 for row in &rows {
320 sent += 1;
321 if tx.send(Ok(record(row))).await.is_err() {
322 return;
323 }
324 }
325 if fetched < limit {
326 return;
327 }
328 }
329 Err(err) => {
330 let _ = tx
331 .send(Err(error::status(
332 &AuthError::from(err),
333 request_id.as_deref(),
334 )))
335 .await;
336 return;
337 }
338 }
339 }
340 });
341 Ok(Response::new(EventStream { rx }))
342 }
343 #[tracing::instrument(skip_all, fields(request_id = %request_id.unwrap_or_default()))]
344 async fn purge_auth_events_inner(
345 &self,
346 request_id: Option<&str>,
347 request: PurgeAuthEventsRequest,
348 ) -> Result<Response<PurgeAuthEventsResponse>, Status> {
349 let older_than =
350 match request.older_than.as_ref() {
351 Some(value) => Some(convert::datetime(value).ok_or_else(|| {
352 error::invalid_argument("older_than is malformed", request_id)
353 })?),
354 None => None,
355 };
356 let retention_cutoff = if request.retention_days > 0 {
357 if request.retention_days > MAX_RETENTION_DAYS {
358 return Err(error::invalid_argument(
359 format!("retention_days exceeds the maximum of {MAX_RETENTION_DAYS}"),
360 request_id,
361 ));
362 }
363 let days = Duration::try_days(request.retention_days as i64).ok_or_else(|| {
364 error::invalid_argument("retention_days is out of range", request_id)
365 })?;
366 Some(Utc::now().checked_sub_signed(days).ok_or_else(|| {
367 error::invalid_argument("retention_days is out of range", request_id)
368 })?)
369 } else {
370 None
371 };
372 let cutoff = match (older_than, retention_cutoff) {
373 (Some(older_than), Some(retention)) => Ord::min(older_than, retention),
374 (Some(older_than), None) => older_than,
375 (None, Some(retention)) => retention,
376 (None, None) => {
377 return Err(error::invalid_argument(
378 "older_than or retention_days is required",
379 request_id,
380 ));
381 }
382 };
383 let kinds = kinds_of(&request.kinds, request_id)?;
384 let mut condition = Condition::all().add(user_events::Column::OccurredAt.lt(cutoff));
385 if !kinds.is_empty() {
386 condition = condition.add(user_events::Column::EventType.is_in(kinds));
387 }
388 if request.dry_run {
389 let matched = user_events::Entity::find()
390 .filter(condition)
391 .count(self.svc.db().read())
392 .await
393 .map_err(|err| error::status(&AuthError::from(err), request_id))?;
394 return Ok(Response::new(PurgeAuthEventsResponse {
395 matched_records: matched,
396 purged_records: 0,
397 dry_run: true,
398 }));
399 }
400 // 删除前先数一次:单次调用不得清空超过上限的事件,避免错误 cutoff 造成不可逆损失。
401 let matched = user_events::Entity::find()
402 .filter(condition.clone())
403 .count(self.svc.db().read())
404 .await
405 .map_err(|err| error::status(&AuthError::from(err), request_id))?;
406 if matched > MAX_PURGE_RECORDS {
407 return Err(error::invalid_argument(
408 format!(
409 "matched {matched} records, which exceeds the single purge limit of {MAX_PURGE_RECORDS}; narrow the range or run with dry_run"
410 ),
411 request_id,
412 ));
413 }
414 // 分批删除:单条大 DELETE 会长时间锁表并撑大 WAL,按批次循环清。
415 let mut purged = 0u64;
416 let mut matched_records = 0u64;
417 loop {
418 let ids: Vec<uuid::Uuid> = user_events::Entity::find()
419 .filter(condition.clone())
420 .limit(PURGE_BATCH as u64)
421 .all(self.svc.db().read())
422 .await
423 .map_err(|err| error::status(&AuthError::from(err), request_id))?
424 .into_iter()
425 .map(|row| row.id)
426 .collect();
427 if ids.is_empty() {
428 break;
429 }
430 matched_records += ids.len() as u64;
431 let batch = user_events::Entity::delete_many()
432 .filter(user_events::Column::Id.is_in(ids))
433 .exec(self.svc.db().write())
434 .await
435 .map_err(|err| error::status(&AuthError::from(err), request_id))?;
436 purged += batch.rows_affected;
437 if batch.rows_affected < PURGE_BATCH as u64 {
438 break;
439 }
440 }
441 Ok(Response::new(PurgeAuthEventsResponse {
442 matched_records,
443 purged_records: purged,
444 dry_run: false,
445 }))
446 }
447}
Deleted lib/auth/src/internal/authorization.rs
@@ -1,305 +0,0 @@
1use std::time::Duration;
2
3use crate::models::users::users;
4use crate::protos::auth::common::v1::Code;
5use crate::protos::auth::v1::authorization_service_server::{
6 AuthorizationService, AuthorizationServiceServer,
7};
8use crate::protos::auth::v1::*;
9use chrono::Utc;
10use sea_orm::EntityTrait;
11use tonic::{Request, Response, Status};
12
13use crate::AuthSvc;
14use crate::errors::{AuthError, AuthResult};
15use crate::internal::store::ALLOWED_APP_SCOPES;
16use crate::internal::{agreement, context, error, store};
17
18const MAX_BATCH_CAPABILITIES: usize = 32;
19const ADMIN_CACHE_TTL_SECS: u64 = 300;
20
21fn requires_admin(capability: PlatformCapability) -> bool {
22 matches!(
23 capability,
24 PlatformCapability::AdminPanel
25 | PlatformCapability::ManageUsers
26 | PlatformCapability::ManagePlatform
27 )
28}
29
30#[derive(Clone)]
31pub struct AuthorizationApi {
32 svc: AuthSvc,
33}
34
35impl AuthorizationApi {
36 pub fn new(svc: AuthSvc) -> Self {
37 Self { svc }
38 }
39
40 pub fn server(
41 self,
42 ) -> tonic::service::interceptor::InterceptedService<
43 AuthorizationServiceServer<Self>,
44 context::Interceptor,
45 > {
46 let gate = context::Interceptor::new(self.svc.auth().rpc_key.clone());
47 AuthorizationServiceServer::with_interceptor(self, gate)
48 }
49
50 async fn decide(
51 &self,
52 user_id: i64,
53 capability: PlatformCapability,
54 require_mfa: bool,
55 ) -> AuthResult<(bool, Code)> {
56 let user = users::Entity::find_by_id(user_id)
57 .one(self.svc.db().read())
58 .await?;
59 let Some(user) = user else {
60 return Ok((false, Code::UserDisabled));
61 };
62 if user.deleted_at.is_some() || !user.allow_use {
63 return Ok((false, Code::UserDisabled));
64 }
65 if user.forbid_login
66 && user
67 .endtime_forbid_login_at
68 .is_none_or(|until| until > Utc::now())
69 {
70 return Ok((false, Code::LoginForbidden));
71 }
72 if require_mfa {
73 return Ok((false, Code::MfaRequired));
74 }
75 if requires_admin(capability) && !user.is_admin {
76 return Ok((false, Code::Forbidden));
77 }
78 Ok((true, Code::Unspecified))
79 }
80
81 async fn agreements_pending(&self, user_id: i64) -> AuthResult<bool> {
82 Ok(!agreement::pending_keys(&self.svc, user_id)
83 .await?
84 .is_empty())
85 }
86}
87
88#[tonic::async_trait]
89impl AuthorizationService for AuthorizationApi {
90 async fn authorize(
91 &self,
92 request: Request<AuthorizeRequest>,
93 ) -> Result<Response<AuthorizeResponse>, Status> {
94 let meta = context::meta(&request).await?;
95 let timer = crate::oltp::metrics::ActiveRpcGuard::new("auth.v1.AuthorizationService");
96 let result = self
97 .authorize_inner(meta.request_id(), request.into_inner())
98 .await;
99 timer.finish("Authorize", &result);
100 result
101 }
102
103 async fn batch_authorize(
104 &self,
105 request: Request<BatchAuthorizeRequest>,
106 ) -> Result<Response<BatchAuthorizeResponse>, Status> {
107 let meta = context::meta(&request).await?;
108 let timer = crate::oltp::metrics::ActiveRpcGuard::new("auth.v1.AuthorizationService");
109 let result = self
110 .batch_authorize_inner(meta.request_id(), request.into_inner())
111 .await;
112 timer.finish("BatchAuthorize", &result);
113 result
114 }
115
116 async fn is_platform_admin(
117 &self,
118 request: Request<IsPlatformAdminRequest>,
119 ) -> Result<Response<IsPlatformAdminResponse>, Status> {
120 let meta = context::meta(&request).await?;
121 let timer = crate::oltp::metrics::ActiveRpcGuard::new("auth.v1.AuthorizationService");
122 let result = self
123 .is_platform_admin_inner(meta.request_id(), request.into_inner())
124 .await;
125 timer.finish("IsPlatformAdmin", &result);
126 result
127 }
128
129 async fn get_capabilities(
130 &self,
131 request: Request<GetCapabilitiesRequest>,
132 ) -> Result<Response<GetCapabilitiesResponse>, Status> {
133 let meta = context::meta(&request).await?;
134 let timer = crate::oltp::metrics::ActiveRpcGuard::new("auth.v1.AuthorizationService");
135 let result = self
136 .get_capabilities_inner(meta.request_id(), request.into_inner())
137 .await;
138 timer.finish("GetCapabilities", &result);
139 result
140 }
141}
142impl AuthorizationApi {
143 #[tracing::instrument(skip_all, fields(request_id = %request_id.unwrap_or_default()))]
144 async fn authorize_inner(
145 &self,
146 request_id: Option<&str>,
147 request: AuthorizeRequest,
148 ) -> Result<Response<AuthorizeResponse>, Status> {
149 let inner = &request;
150 let capability = PlatformCapability::try_from(inner.capability)
151 .unwrap_or(PlatformCapability::Unspecified);
152 if capability == PlatformCapability::Unspecified {
153 return Err(error::invalid_argument(
154 "capability must be specified",
155 request_id,
156 ));
157 }
158 let (mut allowed, mut reason) = self
159 .decide(inner.user_id, capability, inner.require_mfa)
160 .await
161 .map_err(|error_value| error::status(&error_value, request_id))?;
162 if allowed
163 && self
164 .agreements_pending(inner.user_id)
165 .await
166 .map_err(|error_value| error::status(&error_value, request_id))?
167 {
168 allowed = false;
169 reason = Code::AgreementNotAccepted;
170 }
171 Ok(Response::new(AuthorizeResponse {
172 allowed,
173 reason: reason as i32,
174 cached: false,
175 }))
176 }
177 #[tracing::instrument(skip_all, fields(request_id = %request_id.unwrap_or_default()))]
178 async fn batch_authorize_inner(
179 &self,
180 request_id: Option<&str>,
181 request: BatchAuthorizeRequest,
182 ) -> Result<Response<BatchAuthorizeResponse>, Status> {
183 let inner = &request;
184 if inner.capabilities.len() > MAX_BATCH_CAPABILITIES {
185 return Err(error::invalid_argument(
186 format!("at most {MAX_BATCH_CAPABILITIES} capabilities can be checked at once"),
187 request_id,
188 ));
189 }
190 let agreements_pending = self
191 .agreements_pending(inner.user_id)
192 .await
193 .map_err(|error_value| error::status(&error_value, request_id))?;
194 let mut decisions = Vec::with_capacity(inner.capabilities.len());
195 for raw in &inner.capabilities {
196 let capability =
197 PlatformCapability::try_from(*raw).unwrap_or(PlatformCapability::Unspecified);
198 let (mut allowed, mut reason) = if capability == PlatformCapability::Unspecified {
199 (false, Code::InvalidArgument)
200 } else {
201 self.decide(inner.user_id, capability, inner.require_mfa)
202 .await
203 .map_err(|error_value| error::status(&error_value, request_id))?
204 };
205 if allowed && agreements_pending {
206 allowed = false;
207 reason = Code::AgreementNotAccepted;
208 }
209 decisions.push(batch_authorize_response::Decision {
210 capability: *raw,
211 allowed,
212 reason: reason as i32,
213 });
214 }
215 Ok(Response::new(BatchAuthorizeResponse { decisions }))
216 }
217 #[tracing::instrument(skip_all, fields(request_id = %request_id.unwrap_or_default()))]
218 async fn is_platform_admin_inner(
219 &self,
220 request_id: Option<&str>,
221 request: IsPlatformAdminRequest,
222 ) -> Result<Response<IsPlatformAdminResponse>, Status> {
223 let user_id = request.user_id;
224 let cache_key = format!("auth:admin:{user_id}");
225 if let Some((is_admin, security_version)) = self
226 .svc
227 .cache()
228 .get_json::<(bool, i32)>(&cache_key)
229 .await
230 .map_err(|error_value| error::status(&AuthError::from(error_value), request_id))?
231 {
232 return Ok(Response::new(IsPlatformAdminResponse {
233 is_admin,
234 security_version,
235 }));
236 }
237 let user = users::Entity::find_by_id(user_id)
238 .one(self.svc.db().read())
239 .await
240 .map_err(|error_value| error::status(&AuthError::from(error_value), request_id))?;
241 let Some(user) = user else {
242 return Ok(Response::new(IsPlatformAdminResponse {
243 is_admin: false,
244 security_version: 0,
245 }));
246 };
247 let _ = self
248 .svc
249 .cache()
250 .set_json_ex(
251 &cache_key,
252 &(user.is_admin, user.security_version),
253 Duration::from_secs(ADMIN_CACHE_TTL_SECS),
254 )
255 .await;
256 Ok(Response::new(IsPlatformAdminResponse {
257 is_admin: user.is_admin,
258 security_version: user.security_version,
259 }))
260 }
261 #[tracing::instrument(skip_all, fields(request_id = %request_id.unwrap_or_default()))]
262 async fn get_capabilities_inner(
263 &self,
264 request_id: Option<&str>,
265 request: GetCapabilitiesRequest,
266 ) -> Result<Response<GetCapabilitiesResponse>, Status> {
267 let user_id = request.user_id;
268 let user = users::Entity::find_by_id(user_id)
269 .one(self.svc.db().read())
270 .await
271 .map_err(|error_value| error::status(&AuthError::from(error_value), request_id))?
272 .ok_or_else(|| error::status(&AuthError::UserNotFound, request_id))?;
273 let mfa_enabled = store::has_enabled_mfa(self.svc.db(), user_id)
274 .await
275 .map_err(|error_value| error::status(&error_value, request_id))?;
276 // RPC 面已由共享密钥信任,调用方看到的是账号的全部可用 scope。
277 let scopes: Vec<i32> = (1..=ALLOWED_APP_SCOPES.len() as i32).collect();
278 let mut capabilities = Vec::new();
279 for raw in 1..=9i32 {
280 let Ok(capability) = PlatformCapability::try_from(raw) else {
281 continue;
282 };
283 let (allowed, _) = self
284 .decide(user_id, capability, false)
285 .await
286 .map_err(|error_value| error::status(&error_value, request_id))?;
287 if allowed {
288 capabilities.push(raw);
289 }
290 }
291 Ok(Response::new(GetCapabilitiesResponse {
292 allow_use: user.allow_use,
293 forbid_login: user.forbid_login,
294 email_verified: user.email_verified_at.is_some(),
295 mfa_enabled,
296 is_admin: user.is_admin,
297 security_version: user.security_version,
298 scopes,
299 capabilities,
300 pending_agreements: agreement::pending_keys(&self.svc, user_id)
301 .await
302 .map_err(|error_value| error::status(&error_value, request_id))?,
303 }))
304 }
305}
Deleted lib/auth/src/internal/cache_maintenance.rs
@@ -1,244 +0,0 @@
1use std::time::Duration;
2
3use futures_util::StreamExt;
4
5use crate::protos::auth::internal::v1::cache_maintenance_service_server::{
6 CacheMaintenanceService, CacheMaintenanceServiceServer,
7};
8use crate::protos::auth::internal::v1::*;
9use tonic::{Request, Response, Status};
10
11use crate::AuthSvc;
12use crate::errors::AuthError;
13use crate::internal::{context, error, store};
14use crate::me::types::MeResponse;
15
16const USER_SCOPED_PREFIXES: [&str; 3] = ["auth:me:", "auth:admin:", "auth:rate:mfa:"];
17
18const REBUILD_USER_LIMIT: usize = 1000;
19const REBUILD_ME_TTL_SECS: u64 = 300;
20const REBUILD_CONCURRENCY: usize = 8;
21const REBUILD_TOTAL_TIMEOUT_SECS: u64 = 30;
22
23fn scope_prefixes(scope: CacheScope, user_id: i64) -> Vec<String> {
24 let prefixes: Vec<&str> = match scope {
25 CacheScope::All => vec![
26 "auth:me:",
27 "auth:admin:",
28 "auth:svc-token:",
29 "auth:rate:mfa:",
30 "auth:code:",
31 "auth:rate:",
32 "auth:pow:used:",
33 ],
34 CacheScope::Sessions => vec!["auth:me:", "auth:admin:"],
35 CacheScope::Introspection => vec!["auth:svc-token:"],
36 CacheScope::Authorization => vec!["auth:admin:", "auth:me:"],
37 CacheScope::Mfa => vec!["auth:rate:mfa:"],
38 CacheScope::Codes => vec!["auth:code:", "auth:rate:"],
39 CacheScope::Pow => vec!["auth:pow:used:"],
40 CacheScope::Unspecified => Vec::new(),
41 };
42 let mut keys = Vec::with_capacity(prefixes.len());
43 for prefix in prefixes {
44 if user_id > 0 && USER_SCOPED_PREFIXES.contains(&prefix) {
45 keys.push(format!("{prefix}{user_id}"));
46 } else {
47 keys.push(prefix.to_owned());
48 }
49 }
50 keys
51}
52
53#[derive(Clone)]
54pub struct CacheMaintenanceApi {
55 svc: AuthSvc,
56}
57
58impl CacheMaintenanceApi {
59 pub fn new(svc: AuthSvc) -> Self {
60 Self { svc }
61 }
62
63 pub fn server(
64 self,
65 ) -> tonic::service::interceptor::InterceptedService<
66 CacheMaintenanceServiceServer<Self>,
67 context::Interceptor,
68 > {
69 let gate = context::Interceptor::new(self.svc.auth().rpc_key.clone());
70 CacheMaintenanceServiceServer::with_interceptor(self, gate)
71 }
72}
73
74#[tonic::async_trait]
75impl CacheMaintenanceService for CacheMaintenanceApi {
76 async fn flush_auth_cache(
77 &self,
78 request: Request<FlushAuthCacheRequest>,
79 ) -> Result<Response<FlushAuthCacheResponse>, Status> {
80 let meta = context::meta(&request).await?;
81 let timer = crate::oltp::metrics::ActiveRpcGuard::new("auth.v1.CacheMaintenanceService");
82 let result = self
83 .flush_auth_cache_inner(meta.request_id(), request.into_inner())
84 .await;
85 timer.finish("FlushAuthCache", &result);
86 result
87 }
88
89 async fn rebuild_identity_cache(
90 &self,
91 request: Request<RebuildIdentityCacheRequest>,
92 ) -> Result<Response<RebuildIdentityCacheResponse>, Status> {
93 let meta = context::meta(&request).await?;
94 let timer = crate::oltp::metrics::ActiveRpcGuard::new("auth.v1.CacheMaintenanceService");
95 let result = self
96 .rebuild_identity_cache_inner(meta.request_id(), request.into_inner())
97 .await;
98 timer.finish("RebuildIdentityCache", &result);
99 result
100 }
101}
102impl CacheMaintenanceApi {
103 #[tracing::instrument(skip_all, fields(request_id = %request_id.unwrap_or_default()))]
104 async fn flush_auth_cache_inner(
105 &self,
106 request_id: Option<&str>,
107 request: FlushAuthCacheRequest,
108 ) -> Result<Response<FlushAuthCacheResponse>, Status> {
109 let request_id = request_id;
110 if request.scope == CacheScope::Unspecified as i32 {
111 return Err(error::invalid_argument("scope is required", request_id));
112 }
113 let scope = match CacheScope::try_from(request.scope) {
114 Ok(scope) => scope,
115 Err(_) => return Err(error::invalid_argument("scope is invalid", request_id)),
116 };
117 let prefixes = if request.key_prefix.is_empty() {
118 scope_prefixes(scope, request.user_id)
119 } else {
120 vec![request.key_prefix.clone()]
121 };
122 let cache = self.svc.cache();
123 let mut matched_keys = 0u64;
124 let mut deleted_keys = 0u64;
125 if request.user_id > 0 {
126 for prefix in &prefixes {
127 if request.dry_run {
128 let exists = cache
129 .exists(prefix)
130 .await
131 .map_err(|err| error::status(&AuthError::from(err), request_id))?;
132 if exists {
133 matched_keys += 1;
134 }
135 } else {
136 let deleted = cache
137 .delete(prefix)
138 .await
139 .map_err(|err| error::status(&AuthError::from(err), request_id))?;
140 if deleted {
141 matched_keys += 1;
142 deleted_keys += 1;
143 }
144 }
145 }
146 } else {
147 if cache.is_cluster() {
148 return Err(error::unavailable(
149 "global cache flush requires a non-cluster cache",
150 request_id,
151 ));
152 }
153 for prefix in &prefixes {
154 let count = cache
155 .count_prefix(prefix)
156 .await
157 .map_err(|err| error::status(&AuthError::from(err), request_id))?
158 as u64;
159 matched_keys += count;
160 if !request.dry_run {
161 deleted_keys += cache
162 .delete_prefix(prefix)
163 .await
164 .map_err(|err| error::status(&AuthError::from(err), request_id))?
165 as u64;
166 }
167 }
168 }
169 Ok(Response::new(FlushAuthCacheResponse {
170 matched_keys,
171 deleted_keys,
172 dry_run: request.dry_run,
173 }))
174 }
175 #[tracing::instrument(skip_all, fields(request_id = %request_id.unwrap_or_default()))]
176 async fn rebuild_identity_cache_inner(
177 &self,
178 request_id: Option<&str>,
179 request: RebuildIdentityCacheRequest,
180 ) -> Result<Response<RebuildIdentityCacheResponse>, Status> {
181 let request_id = request_id;
182 if request.user_ids.len() > REBUILD_USER_LIMIT {
183 return Err(error::invalid_argument(
184 "user_ids exceeds the limit of 1000",
185 request_id,
186 ));
187 }
188 let queued_users = request.user_ids.len() as u32;
189 if request.dry_run {
190 return Ok(Response::new(RebuildIdentityCacheResponse {
191 queued_users,
192 rebuilt_users: 0,
193 skipped_users: 0,
194 }));
195 }
196 let svc = self.svc.clone();
197 let rebuild = async {
198 let results = futures_util::stream::iter(request.user_ids)
199 .map(|user_id| {
200 let svc = svc.clone();
201 async move {
202 let user = match store::load_user(svc.db(), user_id).await {
203 Ok(user) if user.deleted_at.is_none() => user,
204 _ => return (user_id, false),
205 };
206 let me = MeResponse::from(user);
207 match svc
208 .cache()
209 .set_json_ex(
210 &format!("auth:me:{user_id}"),
211 &me,
212 Duration::from_secs(REBUILD_ME_TTL_SECS),
213 )
214 .await
215 {
216 Ok(()) => (user_id, true),
217 Err(err) => {
218 tracing::warn!(user_id, %err, "identity cache rebuild failed");
219 (user_id, false)
220 }
221 }
222 }
223 })
224 .buffer_unordered(REBUILD_CONCURRENCY)
225 .collect::<Vec<(i64, bool)>>()
226 .await;
227 let rebuilt_users = results.iter().filter(|(_, ok)| *ok).count() as u32;
228 let skipped_users = results.len() as u32 - rebuilt_users;
229 Ok(Response::new(RebuildIdentityCacheResponse {
230 queued_users,
231 rebuilt_users,
232 skipped_users,
233 }))
234 };
235 match tokio::time::timeout(Duration::from_secs(REBUILD_TOTAL_TIMEOUT_SECS), rebuild).await {
236 Ok(response) => response,
237 Err(_) => Err(error::status_of(
238 crate::protos::pb::auth::common::v1::Code::Unavailable,
239 "identity cache rebuild did not finish in time",
240 request_id,
241 )),
242 }
243 }
244}
Deleted lib/auth/src/internal/mod.rs
@@ -1,15 +0,0 @@
1pub mod agreement;
2pub mod audit;
3pub mod authority;
4pub mod authorization;
5pub mod bridge;
6pub mod cache_maintenance;
7pub mod context;
8pub mod convert;
9pub mod error;
10pub mod identity;
11pub mod mfa;
12pub mod organization;
13pub mod session;
14pub mod ssh_key;
15pub mod store;
Deleted lib/auth/src/me/mod.rs
@@ -1,5 +0,0 @@
1pub mod service;
2pub mod types;
3
4pub use service::{invalidate_me_cache, me_cache_keys};
5pub use types::MeResponse;
Deleted lib/auth/src/me/types.rs
@@ -1,33 +0,0 @@
1use crate::models::users::users;
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use utoipa::ToSchema;
5
6#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, ToSchema)]
7pub struct MeResponse {
8 pub user_id: i64,
9 pub username: String,
10 pub displayname: Option<String>,
11 pub email: String,
12 pub is_admin: bool,
13 pub avatar_url: Option<String>,
14 pub email_verified_at: Option<DateTime<Utc>>,
15 pub created_at: DateTime<Utc>,
16 pub version: i32,
17}
18
19impl From<users::Model> for MeResponse {
20 fn from(user: users::Model) -> Self {
21 Self {
22 user_id: user.id,
23 username: user.username,
24 displayname: user.displayname,
25 email: user.email,
26 is_admin: user.is_admin,
27 avatar_url: None,
28 email_verified_at: user.email_verified_at,
29 created_at: user.created_at,
30 version: user.version,
31 }
32 }
33}
Renamed lib/auth/src/templates/common.rs → lib/auth/src/pages/common.rs View lib/auth/src/pages/common.rs

No content changes.

Renamed lib/auth/src/templates/mod.rs → lib/auth/src/pages/mod.rs View lib/auth/src/pages/mod.rs

No content changes.

Renamed lib/auth/src/templates/page.rs → lib/auth/src/pages/page.rs View lib/auth/src/pages/page.rs

No content changes.

Renamed lib/auth/src/organization/error.rs → lib/auth/src/services/organization/error.rs View lib/auth/src/services/organization/error.rs

No content changes.

Renamed lib/auth/src/organization/invite.rs → lib/auth/src/services/organization/invite.rs View lib/auth/src/services/organization/invite.rs
@@ -7,21 +7,21 @@
77};
88use uuid::Uuid;
99
10use crate::AuthSvc;
10use crate::AuthService;
1111use crate::errors::{AuthError, AuthResult};
1212use crate::events::label;
1313use crate::models::space::{
1414 OrgJoinMethod, OrgMemberRole, OrganizationStatus, organization_invite_redemptions,
1515 organization_invites, organization_roles,
1616};
17use crate::organization::member;
18use crate::organization::service::{bump_version, normalize_reason};
19use crate::organization::types::{
17use crate::services::organization::member;
18use crate::services::organization::service::{bump_version, normalize_reason};
19use crate::services::organization::types::{
2020 DEFAULT_INVITE_TTL_SECS, InviteSummary, SYSTEM_ROLE_MEMBER, SYSTEM_ROLE_OWNER,
2121};
2222use crate::share::crypto;
2323
24impl AuthSvc {
24impl AuthService {
2525 #[allow(clippy::too_many_arguments)]
2626 #[tracing::instrument(skip_all, fields(actor_user_id = actor_user_id, organization_id = %organization_id, request_id = %request_id.unwrap_or_default()))]
2727 pub async fn create_invite(
@@ -354,7 +354,13 @@
354354 if let Some(role) = role
355355 && role.key != SYSTEM_ROLE_OWNER
356356 {
357 crate::organization::role::bind_role(&txn, row.id, role.id, invite.created_by).await?;
357 crate::services::organization::role::bind_role(
358 &txn,
359 row.id,
360 role.id,
361 invite.created_by,
362 )
363 .await?;
358364 }
359365 if let Some(extra) = invite.extra_role_ids.as_deref() {
360366 for raw in extra.split(',') {
@@ -371,8 +377,13 @@
371377 if role.key == SYSTEM_ROLE_OWNER || role.key == SYSTEM_ROLE_MEMBER {
372378 continue;
373379 }
374 crate::organization::role::bind_role(&txn, row.id, role.id, invite.created_by)
375 .await?;
380 crate::services::organization::role::bind_role(
381 &txn,
382 row.id,
383 role.id,
384 invite.created_by,
385 )
386 .await?;
376387 }
377388 }
378389 let redeemed = already.is_none();
Renamed lib/auth/src/organization/join.rs → lib/auth/src/services/organization/join.rs View lib/auth/src/services/organization/join.rs
@@ -11,11 +11,11 @@
1111 organization_join_rules, organization_members, organization_roles,
1212};
1313use crate::models::users::users;
14use crate::organization::types::{
14use crate::services::organization::types::{
1515 AddMemberBody, JoinRequestSummary, JoinRuleSummary, SYSTEM_ROLE_MEMBER, SYSTEM_ROLE_OWNER,
1616};
17use crate::organization::{member, service};
18use crate::{AuthError, AuthResult, AuthSvc};
17use crate::services::organization::{member, service};
18use crate::{AuthError, AuthResult, AuthService};
1919
2020fn rule_summary(row: &organization_join_rules::Model) -> JoinRuleSummary {
2121 JoinRuleSummary {
@@ -30,7 +30,7 @@
3030 }
3131}
3232
33impl AuthSvc {
33impl AuthService {
3434 async fn join_request_summary(
3535 &self,
3636 row: &organization_join_requests::Model,
@@ -389,7 +389,7 @@
389389 role_keys: Vec<String>,
390390 role_ids: Vec<String>,
391391 request_id: Option<&str>,
392 ) -> AuthResult<(crate::organization::types::MemberSummary, bool)> {
392 ) -> AuthResult<(crate::services::organization::types::MemberSummary, bool)> {
393393 let txn = self.db().write().begin().await?;
394394 // 机器人成员的角色上限:OWNER 只能由人类走成员面转移,不能通过内部接口直接塞入。
395395 let user = crate::models::users::users::Entity::find_by_id(user_id)
Renamed lib/auth/src/organization/member.rs → lib/auth/src/services/organization/member.rs View lib/auth/src/services/organization/member.rs
@@ -7,16 +7,16 @@
77};
88use uuid::Uuid;
99
10use crate::AuthSvc;
10use crate::AuthService;
1111use crate::errors::{AuthError, AuthResult};
1212use crate::events::label;
1313use crate::models::space::{
1414 OrgJoinMethod, OrgMemberRole, organization_member_permissions, organization_members,
1515};
1616use crate::models::users::users;
17use crate::organization::role;
18use crate::organization::service::{bump_version, normalize_reason};
19use crate::organization::types::{
17use crate::services::organization::role;
18use crate::services::organization::service::{bump_version, normalize_reason};
19use crate::services::organization::types::{
2020 AddMemberBody, MAX_ALIAS_AVATAR_URL_LEN, MAX_ALIAS_NAME_LEN, MAX_REASON_LEN, MemberSummary,
2121 SetMemberRoleBody, UpdateBanBody, UpdateMemberBody, normalize_alias,
2222};
@@ -125,7 +125,7 @@
125125 }
126126}
127127
128impl AuthSvc {
128impl AuthService {
129129 // 分页与过滤开关直接对应查询参数,拆结构体没有收益。
130130 #[allow(clippy::too_many_arguments)]
131131 #[tracing::instrument(skip_all, fields(actor_user_id = actor_user_id, organization_id = %organization_id, request_id = %request_id.unwrap_or_default()))]
Renamed lib/auth/src/organization/mod.rs → lib/auth/src/services/organization/mod.rs View lib/auth/src/services/organization/mod.rs

No content changes.

Renamed lib/auth/src/organization/permission.rs → lib/auth/src/services/organization/permission.rs View lib/auth/src/services/organization/permission.rs
@@ -4,17 +4,17 @@
44use serde::{Deserialize, Serialize};
55use uuid::Uuid;
66
7use crate::AuthSvc;
7use crate::AuthService;
88use crate::errors::{AuthError, AuthResult};
99use crate::models::space::{
1010 OrgMemberRole, PermissionEffect, organization_member_permissions, organization_member_roles,
1111 organization_members, organization_role_permissions,
1212};
13use crate::organization::types::{
13use crate::services::organization::types::{
1414 PERMISSION_CACHE_TTL_SECS, PERMISSIONS, PermissionCheck, Resolution, SYSTEM_ROLE_ADMIN,
1515 SYSTEM_ROLE_OWNER, is_admin_permission, is_known_permission,
1616};
17use crate::organization::{member, role, service};
17use crate::services::organization::{member, role, service};
1818
1919/// 缓存进 Redis 的判定核心。版本号相同的组织共享同一份核心,
2020/// 任何写操作推进 `organizations.version` 都会让整份核心失效。
@@ -132,7 +132,7 @@
132132}
133133
134134async fn cached_kernel(
135 svc: &AuthSvc,
135 svc: &AuthService,
136136 organization_id: Uuid,
137137 user_id: i64,
138138 version: i32,
@@ -201,7 +201,7 @@
201201}
202202
203203async fn gate(
204 svc: &AuthSvc,
204 svc: &AuthService,
205205 organization_id: Uuid,
206206 user_id: i64,
207207 _request_id: Option<&str>,
@@ -248,7 +248,7 @@
248248/// → 角色 Allow/Deny → 成员直挂 Allow/Deny。
249249#[tracing::instrument(skip_all, fields(organization_id = %organization_id, user_id = user_id, permission = %permission, request_id = %request_id.unwrap_or_default()))]
250250pub(crate) async fn resolve(
251 svc: &AuthSvc,
251 svc: &AuthService,
252252 organization_id: Uuid,
253253 user_id: i64,
254254 permission: &str,
@@ -258,7 +258,7 @@
258258 Ok(gate.resolution(permission))
259259}
260260
261impl AuthSvc {
261impl AuthService {
262262 /// 平台管理员越过会籍判定,但仍受组织状态约束。
263263 #[tracing::instrument(skip_all, fields(actor_user_id = actor_user_id, organization_id = %organization_id, user_id = user_id, request_id = %request_id.unwrap_or_default()))]
264264 pub async fn check_permission(
Renamed lib/auth/src/organization/role.rs → lib/auth/src/services/organization/role.rs View lib/auth/src/services/organization/role.rs
@@ -6,19 +6,21 @@
66};
77use uuid::Uuid;
88
9use crate::AuthSvc;
9use crate::AuthService;
1010use crate::errors::{AuthError, AuthResult};
1111use crate::events::label;
1212use crate::models::space::{
1313 PermissionEffect, organization_member_roles, organization_role_permissions, organization_roles,
1414};
15use crate::organization::service::{bump_version, normalize_role_key, normalize_role_name};
16use crate::organization::types::{
15use crate::services::organization::service::{
16 bump_version, normalize_role_key, normalize_role_name,
17};
18use crate::services::organization::types::{
1719 CreateRoleBody, MemberSummary, PermissionGrant, PermissionGrant as Grant, RoleSummary,
1820 SYSTEM_ROLE_ADMIN, SYSTEM_ROLE_MEMBER, SYSTEM_ROLE_OWNER, SetRolePermissionsBody,
1921 is_known_permission,
2022};
21use crate::organization::{error, member};
23use crate::services::organization::{error, member};
2224
2325/// 内置角色的固有授权。OWNER 不需要显式授权(解析链直接短路),
2426/// ADMIN 的固有集写在 permissions.rs 的白名单里,这里只落 MEMBER 的默认值。
@@ -239,7 +241,7 @@
239241 Ok(())
240242}
241243
242impl AuthSvc {
244impl AuthService {
243245 #[tracing::instrument(skip_all, fields(actor_user_id = actor_user_id, organization_id = %organization_id, request_id = %request_id.unwrap_or_default()))]
244246 pub async fn list_roles(
245247 &self,
@@ -536,9 +538,12 @@
536538 role_key: &str,
537539 request_id: Option<&str>,
538540 ) -> AuthResult<Vec<String>> {
539 let member =
540 crate::organization::member::load_by_id(self.db().read(), organization_id, member_id)
541 .await?;
541 let member = crate::services::organization::member::load_by_id(
542 self.db().read(),
543 organization_id,
544 member_id,
545 )
546 .await?;
542547 if member.banned_at.is_some() || member.left_at.is_some() {
543548 return Err(AuthError::OrganizationMemberNotFound);
544549 }
@@ -602,9 +607,12 @@
602607 if role.key == SYSTEM_ROLE_OWNER {
603608 return Err(AuthError::OrganizationRoleSystem);
604609 }
605 let member =
606 crate::organization::member::load_by_id(self.db().read(), organization_id, member_id)
607 .await?;
610 let member = crate::services::organization::member::load_by_id(
611 self.db().read(),
612 organization_id,
613 member_id,
614 )
615 .await?;
608616 if actor_user_id == member.user_id {
609617 if !role.self_assignable {
610618 return Err(AuthError::Forbidden);
@@ -661,9 +669,12 @@
661669 request_id,
662670 )
663671 .await?;
664 let member =
665 crate::organization::member::load_by_id(self.db().read(), organization_id, member_id)
666 .await?;
672 let member = crate::services::organization::member::load_by_id(
673 self.db().read(),
674 organization_id,
675 member_id,
676 )
677 .await?;
667678 if member.role == crate::models::space::OrgMemberRole::Owner {
668679 return Err(AuthError::OrganizationOwnerRequired);
669680 }
Renamed lib/auth/src/organization/service.rs → lib/auth/src/services/organization/service.rs View lib/auth/src/services/organization/service.rs
@@ -7,7 +7,7 @@
77};
88use uuid::Uuid;
99
10use crate::AuthSvc;
10use crate::AuthService;
1111use crate::errors::{AuthError, AuthResult};
1212use crate::events::{Labels, label};
1313use crate::models::space::{
@@ -16,19 +16,19 @@
1616 organization_members, organization_role_permissions, organization_roles, organizations,
1717};
1818use crate::models::users::users;
19use crate::organization::types::{
19use crate::services::organization::types::{
2020 CreateOrganizationBody, MAX_ORG_DESCRIPTION_LEN, MAX_ORG_NAME_LEN, MAX_PERMISSION_LEN,
2121 MAX_ROLE_KEY_LEN, MAX_ROLE_NAME_LEN, MAX_SLUG_LEN, MemberSummary, OrganizationSummary,
2222 PermissionGrant, RoleSummary, SYSTEM_ROLE_ADMIN, SYSTEM_ROLE_OWNER, UpdateOrganizationBody,
2323 is_known_permission,
2424};
25use crate::organization::{error, member, permission, role};
25use crate::services::organization::{error, member, permission, role};
2626
2727pub(crate) fn perm_cache_prefix(organization_id: Uuid, user_id: i64) -> String {
2828 format!("auth:org:perm:{organization_id}:{user_id}")
2929}
3030
31impl AuthSvc {
31impl AuthService {
3232 /// 组织写操作的统一收尾:推进 version,然后清掉本组织全部派生缓存。
3333 /// version 同时是权限缓存的版本键与乐观锁基准,必须与写操作同事务推进。
3434 pub(crate) async fn invalidate_org(&self, organization_id: Uuid) -> AuthResult<()> {
Renamed lib/auth/src/organization/types.rs → lib/auth/src/services/organization/types.rs View lib/auth/src/services/organization/types.rs

No content changes.

Renamed lib/auth/src/ssh_key/mod.rs → lib/auth/src/services/ssh_key/mod.rs View lib/auth/src/services/ssh_key/mod.rs

No content changes.

Renamed lib/auth/src/ssh_key/service.rs → lib/auth/src/services/ssh_key/service.rs View lib/auth/src/services/ssh_key/service.rs
@@ -14,14 +14,14 @@
1414};
1515use uuid::Uuid;
1616
17use crate::AuthSvc;
17use crate::AuthService;
1818use crate::errors::{AuthError, AuthResult};
19use crate::share::crypto;
20use crate::share::types::ListQuery;
21use crate::ssh_key::types::{
19use crate::services::ssh_key::types::{
2220 MAX_SSH_KEY_DESCRIPTION_LEN, MAX_SSH_KEY_NAME_LEN, MAX_SSH_PUBLIC_KEY_LEN,
2321 MIN_SSH_PUBLIC_KEY_LEN, SshKeyAddRequest, SshKeyAlgorithm, SshKeySummary,
2422};
23use crate::share::crypto;
24use crate::share::types::ListQuery;
2525
2626pub(crate) fn parse_public_key(raw: &str) -> AuthResult<(PublicKeyType, String)> {
2727 let key_line = raw.trim();
@@ -77,7 +77,7 @@
7777 }
7878}
7979
80impl AuthSvc {
80impl AuthService {
8181 #[tracing::instrument(skip_all, fields(request_id = %request_id, client_ip = ?session.client_ip().ok().flatten()))]
8282 pub async fn add_ssh_key(
8383 &self,
Renamed lib/auth/src/ssh_key/types.rs → lib/auth/src/services/ssh_key/types.rs View lib/auth/src/services/ssh_key/types.rs

No content changes.

Modified lib/auth/src/services/agreements.rs View lib/auth/src/services/agreements.rs
@@ -4,8 +4,8 @@
44use time::OffsetDateTime;
55use uuid::Uuid;
66
7use crate::api_error::AuthBusinessError;
8use crate::service::AuthService;
7use crate::AuthService;
8use crate::errors::AuthBusinessError;
99use crate::share::account::types::AgreementDoc;
1010
1111#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
Renamed lib/auth/src/authority.rs → lib/auth/src/services/authority.rs View lib/auth/src/services/authority.rs
@@ -5,7 +5,7 @@
55use uuid::Uuid;
66
77use crate::share::{crypto, user as store};
8use crate::{AuthError, AuthResult, AuthSvc};
8use crate::{AuthError, AuthResult, AuthService};
99
1010/// 会话凭据摘要的算法标识,落在 `user_sessions.hash_key_id`。
1111pub const SESSION_HASH_KEY_ID: &str = "logical-session-v1";
@@ -25,7 +25,7 @@
2525 .is_none_or(|until| until > Utc::now()))
2626}
2727
28impl AuthSvc {
28impl AuthService {
2929 #[tracing::instrument(skip_all, fields(user_id = user.id, mfa = mfa, request_id = %request_id.unwrap_or_default()))]
3030 pub async fn bind_auth_session(
3131 &self,
@@ -212,7 +212,7 @@
212212 .ok_or(AuthError::VersionMismatch {
213213 current: user.version,
214214 })?;
215 crate::me::service::invalidate_me_cache(self.cache(), user_id).await;
215 crate::services::me::invalidate_me_cache(self.cache(), user_id).await;
216216 Ok(updated)
217217 }
218218}
Renamed lib/auth/src/account_lifecycle.rs → lib/auth/src/services/lifecycle.rs View lib/auth/src/services/lifecycle.rs
@@ -4,12 +4,12 @@
44 ActiveModelTrait, ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, Set, TransactionTrait,
55};
66
7use crate::AuthSvc;
7use crate::AuthService;
88use crate::errors::{AuthError, AuthResult};
99use crate::models::space::{OrganizationStatus, organization_members, organizations};
1010use crate::models::users::{user_mfa, user_mfa_backup_codes, user_sessions, user_ssh_keys, users};
1111
12impl AuthSvc {
12impl AuthService {
1313 /// Soft-delete the account and revoke every credential. The row (and its
1414 /// numeric id) stays so historical references remain resolvable; a later
1515 /// `purge_account` anonymizes the PII.
@@ -118,7 +118,7 @@
118118 active.update(&txn).await?;
119119 txn.commit().await?;
120120 if let Err(error) =
121 crate::internal::store::drop_user_session_states(self.cache(), user_id, None).await
121 crate::api::store::drop_user_session_states(self.cache(), user_id, None).await
122122 {
123123 tracing::warn!(%error, %user_id, "failed to drop purged account sessions");
124124 }
Renamed lib/auth/src/me/service.rs → lib/auth/src/services/me.rs View lib/auth/src/services/me.rs
@@ -1,13 +1,44 @@
11use std::time::Duration;
22
3use crate::models::users::users;
4use crate::session::Session;
3use chrono::{DateTime, Utc};
54use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
5use serde::{Deserialize, Serialize};
6use utoipa::ToSchema;
67
7use crate::AuthSvc;
8use crate::AuthService;
89use crate::errors::{AuthError, AuthResult};
9use crate::me::types::MeResponse;
10use crate::models::users::users;
11use crate::session::Session;
1012
13#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, ToSchema)]
14pub struct MeResponse {
15 pub user_id: i64,
16 pub username: String,
17 pub displayname: Option<String>,
18 pub email: String,
19 pub is_admin: bool,
20 pub avatar_url: Option<String>,
21 pub email_verified_at: Option<DateTime<Utc>>,
22 pub created_at: DateTime<Utc>,
23 pub version: i32,
24}
25
26impl From<users::Model> for MeResponse {
27 fn from(user: users::Model) -> Self {
28 Self {
29 user_id: user.id,
30 username: user.username,
31 displayname: user.displayname,
32 email: user.email,
33 is_admin: user.is_admin,
34 avatar_url: None,
35 email_verified_at: user.email_verified_at,
36 created_at: user.created_at,
37 version: user.version,
38 }
39 }
40}
41
1142pub const ME_CACHE_TTL_SECS: u64 = 300;
1243const ME_COUNT_TTL_SECS: u64 = 600;
1344const ME_REFRESH_EVERY: i64 = 5;
@@ -32,7 +63,7 @@
3263 }
3364}
3465
35impl AuthSvc {
66impl AuthService {
3667 #[tracing::instrument(skip_all, fields(request_id = %_request_id, client_ip = ?session.client_ip().ok().flatten()))]
3768 pub async fn me(&self, session: Session, _request_id: String) -> AuthResult<MeResponse> {
3869 let user_id = self
Modified lib/auth/src/services/mod.rs View lib/auth/src/services/mod.rs
@@ -1,7 +1,13 @@
11pub mod agreements;
2pub mod authority;
3pub mod lifecycle;
4pub mod me;
25pub mod notifications;
6pub mod organization;
37pub mod preferences;
48pub mod profile;
59pub mod reports;
10pub mod service;
11pub mod ssh_key;
612pub mod suspensions;
713pub mod users;
Modified lib/auth/src/services/notifications.rs View lib/auth/src/services/notifications.rs
@@ -9,8 +9,8 @@
99use time::OffsetDateTime;
1010use uuid::Uuid;
1111
12use crate::api_error::AuthBusinessError;
13use crate::service::AuthService;
12use crate::AuthService;
13use crate::errors::AuthBusinessError;
1414
1515#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
1616pub struct NotificationDto {
Modified lib/auth/src/services/preferences.rs View lib/auth/src/services/preferences.rs
@@ -4,8 +4,8 @@
44use serde::{Deserialize, Serialize};
55use time::OffsetDateTime;
66
7use crate::api_error::AuthBusinessError;
8use crate::service::AuthService;
7use crate::AuthService;
8use crate::errors::AuthBusinessError;
99
1010#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
1111pub struct UserPreferencesDto {
Modified lib/auth/src/services/profile.rs View lib/auth/src/services/profile.rs
@@ -4,8 +4,8 @@
44use serde::{Deserialize, Serialize};
55use time::{Date, OffsetDateTime};
66
7use crate::api_error::AuthBusinessError;
8use crate::service::AuthService;
7use crate::AuthService;
8use crate::errors::AuthBusinessError;
99use crate::share::account::types::UserPublic;
1010use crate::share::account::validate;
1111
@@ -65,7 +65,10 @@
6565}
6666
6767impl AuthService {
68 pub async fn me(&self, uid: i64) -> Result<(UserMeDto, Option<ProfileDto>), AuthBusinessError> {
68 pub async fn account_me(
69 &self,
70 uid: i64,
71 ) -> Result<(UserMeDto, Option<ProfileDto>), AuthBusinessError> {
6972 export::users::ensure_local_user(&self.state, uid).await?;
7073 let db = self.db();
7174 let Some(user) = user::Entity::find_by_id(uid).one(db.read()).await? else {
@@ -193,7 +196,7 @@
193196 }
194197 user_active.updated_at = Set(now);
195198
196 TransactionTrait::transaction(db.primary(), |txn| {
199 TransactionTrait::transaction(self.state.database.primary(), |txn| {
197200 Box::pin(async move {
198201 user_active.update(txn).await?;
199202
@@ -320,8 +323,8 @@
320323 }
321324}
322325
323fn map_kenauth(error: crate::auth_api_error::KenAuthError) -> AuthBusinessError {
324 use crate::auth_api_error::KenAuthError;
326fn map_kenauth(error: crate::errors::KenAuthError) -> AuthBusinessError {
327 use crate::errors::KenAuthError;
325328 match error {
326329 KenAuthError::NotFound => AuthBusinessError::NotFound,
327330 KenAuthError::Unauthenticated => AuthBusinessError::Unauthorized,
Modified lib/auth/src/services/reports.rs View lib/auth/src/services/reports.rs
@@ -5,8 +5,8 @@
55use time::OffsetDateTime;
66use uuid::Uuid;
77
8use crate::api_error::AuthBusinessError;
9use crate::service::AuthService;
8use crate::AuthService;
9use crate::errors::AuthBusinessError;
1010
1111#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
1212pub struct CreateReportCmd {
Renamed lib/auth/src/auth_svc.rs → lib/auth/src/services/service.rs View lib/auth/src/services/service.rs
@@ -1,16 +1,18 @@
1use std::sync::Arc;
1use std::time::Duration;
2
3use config::AppConfig;
4use config::auth::AuthConfig;
5use config::pow::PowConfig;
6use export::{AppRedis, AppState};
7use serde::Serialize;
28
39use crate::cache::Cache;
4use crate::config::auth::AuthConfig;
5use crate::config::pow::PowConfig;
610use crate::db::DbConn;
711use crate::email::MailPublisher;
12use crate::errors::{AuthError, AuthResult};
813use crate::events::{EventHandler, Labels, label};
9use serde::Serialize;
10
11pub use super::errors::{AuthError, AuthResult};
1214
13const EVENT_PUBLISH_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
15const EVENT_PUBLISH_TIMEOUT: Duration = Duration::from_secs(5);
1416
1517async fn actor_details(
1618 conn: &impl sea_orm::ConnectionTrait,
@@ -34,81 +36,74 @@
3436}
3537
3638#[derive(Clone)]
37pub struct AuthSvc {
39pub struct AuthService {
40 pub state: AppState,
3841 db: DbConn,
39 cache: Cache,
40 mail: Option<MailPublisher>,
41 events: Option<EventHandler>,
42 auth: Arc<AuthConfig>,
43 pow: Arc<PowConfig>,
44 session_max_age_secs: u64,
42 pub(crate) read_timeout: Duration,
43 pub(crate) write_timeout: Duration,
4544}
4645
47impl AuthSvc {
48 #[allow(clippy::too_many_arguments)]
49 pub fn new(
50 db: DbConn,
51 cache: Cache,
52 mail: Option<MailPublisher>,
53 events: Option<EventHandler>,
54 auth: AuthConfig,
55 pow: PowConfig,
56 session_max_age_secs: u64,
57 ) -> Self {
58 Self {
59 db,
60 cache,
61 mail,
62 events,
63 auth: Arc::new(auth),
64 pow: Arc::new(pow),
65 session_max_age_secs: session_max_age_secs.max(1),
66 }
67 }
68
69 pub fn from_state(state: &export::AppState) -> Self {
46impl AuthService {
47 pub fn new(state: AppState) -> Self {
7048 let db = DbConn::new(
7149 state.database.primary().clone(),
7250 state.database.replicas().to_vec(),
7351 );
74 Self::new(
52 Self {
53 state,
7554 db,
76 state.cache.clone(),
77 state.mail.clone(),
78 state.events.clone(),
79 state.config.auth.clone(),
80 state.config.pow.clone(),
81 state.config.session.max_age.max(1) as u64,
82 )
55 read_timeout: Duration::from_millis(300),
56 write_timeout: Duration::from_millis(2_000),
57 }
8358 }
8459
60 pub fn config(&self) -> &AppConfig {
61 &self.state.config
62 }
63
8564 pub fn db(&self) -> &DbConn {
8665 &self.db
8766 }
8867
68 pub fn redis(&self) -> &AppRedis {
69 &self.state.redis
70 }
71
8972 pub fn cache(&self) -> &Cache {
90 &self.cache
73 &self.state.cache
9174 }
9275
9376 pub fn mail(&self) -> Option<&MailPublisher> {
94 self.mail.as_ref()
77 self.state.mail.as_ref()
78 }
79
80 pub fn events(&self) -> Option<&EventHandler> {
81 self.state.events.as_ref()
9582 }
9683
9784 pub fn auth(&self) -> &AuthConfig {
98 &self.auth
85 &self.state.config.auth
9986 }
10087
10188 pub fn pow(&self) -> &PowConfig {
102 &self.pow
89 &self.state.config.pow
10390 }
10491
105 pub fn events(&self) -> Option<&EventHandler> {
106 self.events.as_ref()
92 pub fn session_max_age_secs(&self) -> u64 {
93 self.state.config.session.max_age.max(1) as u64
10794 }
10895
109 /// 逻辑会话有效期(秒):启动时从配置读取一次,热路径避免重读配置文件。
110 pub fn session_max_age_secs(&self) -> u64 {
111 self.session_max_age_secs
96 pub async fn mail_locale(
97 &self,
98 uid: Option<i64>,
99 accept_language: Option<&str>,
100 ) -> i18n::UiLocale {
101 let fallback = i18n::UiLocale::parse(self.config().email.default_locale())
102 .unwrap_or(i18n::UiLocale::En);
103 match uid {
104 Some(uid) => i18n::resolve_user_locale(self.db().read(), uid, fallback).await,
105 None => i18n::negotiate_accept_language(accept_language, fallback),
106 }
112107 }
113108
114109 pub async fn emit<T>(&self, kind: &str, payload: T, request_id: Option<&str>)
@@ -119,10 +114,6 @@
119114 .await;
120115 }
121116
122 /// 事件先写进 `event_outbox`,由 relay 投递到 RocketMQ;进程在投递前退出也不会丢。
123 ///
124 /// 写库失败时退回直接投递:outbox 表缺失或数据库抖动不应该让事件链静默断掉,
125 /// 只是这时又回到「重试 3 次后丢弃」的旧语义。
126117 pub async fn emit_labeled<T>(
127118 &self,
128119 kind: &str,
@@ -132,7 +123,7 @@
132123 ) where
133124 T: Serialize + Clone,
134125 {
135 let Some(events) = self.events.as_ref() else {
126 let Some(events) = self.events() else {
136127 tracing::error!(
137128 kind,
138129 "event handler is not configured; event was not forwarded to rocketmq"
@@ -169,11 +160,6 @@
169160 }
170161 }
171162
172 /// 事务内入队:事件行与业务写入同一个事务,提交时一起可见、回滚时一起消失。
173 ///
174 /// 与 `emit_labeled` 的差别在于**错误会向上传播**:写不进 outbox 就让业务事务回滚,
175 /// 而不是退回直接投递。这是 KenShell 的语义——宁可这次写操作失败重试,
176 /// 也不要出现「业务成功、事件永久缺失」。
177163 pub async fn emit_tx<T>(
178164 &self,
179165 conn: &impl sea_orm::ConnectionTrait,
@@ -206,9 +192,8 @@
206192 Ok(())
207193 }
208194
209 /// 启动时验证事件链路:查询一次 RocketMQ 路由,让 broker 不可达如实暴露。
210195 pub async fn verify_event_forwarding(&self) -> bool {
211 let Some(events) = self.events.as_ref() else {
196 let Some(events) = self.events() else {
212197 return false;
213198 };
214199 match tokio::time::timeout(EVENT_PUBLISH_TIMEOUT, events.health()).await {
@@ -235,7 +220,7 @@
235220 html: &str,
236221 request_id: Option<&str>,
237222 ) -> AuthResult<()> {
238 let Some(mail) = self.mail.as_ref() else {
223 let Some(mail) = self.mail() else {
239224 return Err(AuthError::Unavailable(
240225 "mail publisher is not configured".to_owned(),
241226 ));
Modified lib/auth/src/services/suspensions.rs View lib/auth/src/services/suspensions.rs
@@ -4,8 +4,8 @@
44use time::OffsetDateTime;
55use uuid::Uuid;
66
7use crate::api_error::AuthBusinessError;
8use crate::service::AuthService;
7use crate::AuthService;
8use crate::errors::AuthBusinessError;
99
1010#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
1111pub struct SuspensionDto {
Modified lib/auth/src/services/users.rs View lib/auth/src/services/users.rs
@@ -4,8 +4,8 @@
44use models::users::user::{self, ProfileVisibility};
55use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
66
7use crate::api_error::AuthBusinessError;
8use crate::service::AuthService;
7use crate::AuthService;
8use crate::errors::AuthBusinessError;
99use crate::share::account::types::UserPublic;
1010
1111const MAX_BATCH_USERS: usize = 100;
@@ -129,8 +129,8 @@
129129 }
130130}
131131
132fn map_kenauth(error: crate::auth_api_error::KenAuthError) -> AuthBusinessError {
133 use crate::auth_api_error::KenAuthError;
132fn map_kenauth(error: crate::errors::KenAuthError) -> AuthBusinessError {
133 use crate::errors::KenAuthError;
134134 match error {
135135 KenAuthError::NotFound => AuthBusinessError::NotFound,
136136 KenAuthError::Unauthenticated => AuthBusinessError::Unauthorized,
Modified lib/auth/src/share/account/validate.rs View lib/auth/src/share/account/validate.rs
@@ -1,6 +1,6 @@
11use time::{Date, OffsetDateTime};
22
3use crate::api_error::AuthBusinessError;
3use crate::errors::AuthBusinessError;
44
55pub fn validate_email(raw: &str) -> Result<String, AuthBusinessError> {
66 let email = raw.trim().to_lowercase();
Renamed lib/auth/src/pow/hash.rs → lib/auth/src/share/pow/hash.rs View lib/auth/src/share/pow/hash.rs
@@ -1,7 +1,7 @@
11use sha2::{Digest, Sha256};
22
33use crate::errors::{AuthError, AuthResult};
4use crate::handlers::identity::challenge::types::{ChallengeResponse, PowProofRequest};
4use crate::handlers::identity::challenge::{ChallengeResponse, PowProofRequest};
55use crate::share::crypto;
66
77pub fn pow_hash(challenge: &ChallengeResponse, nonce: u64, payload: &[u8]) -> [u8; 32] {
Renamed lib/auth/src/pow/mod.rs → lib/auth/src/share/pow/mod.rs View lib/auth/src/share/pow/mod.rs

No content changes.

Renamed lib/auth/src/pow/store.rs → lib/auth/src/share/pow/store.rs View lib/auth/src/share/pow/store.rs
@@ -5,9 +5,7 @@
55use crate::session::Session;
66
77use crate::errors::{AuthError, AuthResult};
8use crate::handlers::identity::challenge::types::{
9 ChallengeResponse, PowProofRequest, SESSION_CHALLENGE,
10};
8use crate::handlers::identity::challenge::{ChallengeResponse, PowProofRequest, SESSION_CHALLENGE};
119use crate::share::crypto::{now_secs, random_alnum};
1210
1311pub const USED_KEY_PREFIX: &str = "auth:pow:used:";
@@ -36,7 +34,7 @@
3634 .get::<ChallengeResponse>(SESSION_CHALLENGE)?
3735 .ok_or(AuthError::Expired)?;
3836
39 crate::pow::hash::verify_pow(&challenge, proof, expected_payload)?;
37 crate::share::pow::hash::verify_pow(&challenge, proof, expected_payload)?;
4038
4139 let remaining = challenge.expires_at.saturating_sub(now_secs()).max(1);
4240 let used_key = used_key(&challenge.context);
Renamed lib/auth/src/auth_session/cookie.rs → lib/auth/src/share/session/cookie.rs View lib/auth/src/share/session/cookie.rs

No content changes.

Renamed lib/auth/src/auth_session/csrf.rs → lib/auth/src/share/session/csrf.rs View lib/auth/src/share/session/csrf.rs

No content changes.

Renamed lib/auth/src/auth_session/mod.rs → lib/auth/src/share/session/mod.rs View lib/auth/src/share/session/mod.rs

No content changes.

Renamed lib/auth/src/auth_session/revalidate.rs → lib/auth/src/share/session/revalidate.rs View lib/auth/src/share/session/revalidate.rs
@@ -7,7 +7,7 @@
77use ::session::Session;
88use export::AppState;
99
10use crate::auth_session::cookie::append_cookie;
10use crate::share::session::cookie::append_cookie;
1111
1212const SKIP_PREFIXES: [&str; 8] = [
1313 "/healthz",
@@ -76,8 +76,8 @@
7676 .await;
7777 Ok(next.call(request).await?.map_into_boxed_body())
7878 }
79 Err(crate::auth_api_error::KenAuthError::Unauthenticated)
80 | Err(crate::auth_api_error::KenAuthError::NotFound) => {
79 Err(crate::errors::KenAuthError::Unauthenticated)
80 | Err(crate::errors::KenAuthError::NotFound) => {
8181 let mut response = HttpResponse::Unauthorized().json(serde_json::json!({
8282 "code": 401,
8383 "message": "session_revoked",
Modified lib/auth/src/share/mod.rs View lib/auth/src/share/mod.rs
@@ -6,8 +6,10 @@
66pub mod mfa;
77pub mod net;
88pub mod password;
9pub mod pow;
910pub mod rate;
1011pub mod rpc;
12pub mod session;
1113pub mod totp;
1214pub mod types;
1315pub mod user;
Modified lib/auth/src/share/rpc.rs View lib/auth/src/share/rpc.rs
@@ -3,7 +3,7 @@
33use sha2::{Digest, Sha256};
44use time::OffsetDateTime;
55
6use crate::api_error::ApiError;
6use crate::errors::ApiError;
77
88pub fn client(state: &AppState) -> Result<&dyn export::AuthApi, ApiError> {
99 state
Renamed lib/auth/src/service.rs → lib/auth/src/api.rs View lib/auth/src/api.rs
@@ -1,130 +1,79 @@
1use std::sync::Arc;
2use std::time::Duration;
3
4use config::AppConfig;
5use export::{AppRedis, AppState, Database};
1use async_trait::async_trait;
2use export::{AuthApi, KenAuthError};
63use tonic::Request;
74
8use crate::AuthSvc;
9use crate::auth_api_error::KenAuthError;
5use crate::AuthService;
6use crate::api::context::RequestMeta;
107use crate::protos::auth as proto;
8use crate::protos::auth::internal::v1::authority_service_server::AuthorityService;
9use crate::protos::auth::v1::identity_service_server::IdentityService;
10use crate::protos::auth::v1::mfa_service_server::MfaService;
11use crate::protos::auth::v1::organization_service_server::OrganizationService;
12use crate::protos::auth::v1::session_service_server::SessionService;
13use crate::protos::auth::v1::ssh_key_service_server::SshKeyService;
1114
12#[derive(Clone, Debug)]
13pub struct VerifiedAccessKey {
14 pub user_id: i64,
15 pub scope: models::users::access_scope::AccessKeyScope,
16}
17
18#[derive(Clone)]
19pub struct AuthService {
20 pub state: AppState,
21 service: Arc<AuthSvc>,
22 read_timeout: Duration,
23 write_timeout: Duration,
24}
15pub mod authority;
16pub mod context;
17pub mod convert;
18pub mod error;
19pub mod identity;
20pub mod mfa;
21pub mod organization;
22pub mod session;
23pub mod ssh_key;
24pub mod store;
2525
2626impl AuthService {
27 pub fn new(state: AppState) -> Self {
28 let service = Arc::new(AuthSvc::from_state(&state));
29 Self {
30 state,
31 service,
32 read_timeout: Duration::from_millis(300),
33 write_timeout: Duration::from_millis(2_000),
34 }
27 fn identity(&self) -> crate::api::identity::IdentityApi {
28 crate::api::identity::IdentityApi::new(self.clone())
3529 }
3630
37 pub fn with_domain(state: AppState, service: Arc<AuthSvc>) -> Self {
38 Self {
39 state,
40 service,
41 read_timeout: Duration::from_millis(300),
42 write_timeout: Duration::from_millis(2_000),
43 }
31 fn authority(&self) -> crate::api::authority::AuthorityApi {
32 crate::api::authority::AuthorityApi::new(self.clone())
4433 }
4534
46 pub fn service(&self) -> &AuthSvc {
47 self.service.as_ref()
35 fn session(&self) -> crate::api::session::SessionApi {
36 crate::api::session::SessionApi::new(self.clone())
4837 }
4938
50 pub fn domain(&self) -> &Arc<AuthSvc> {
51 &self.service
39 fn organization(&self) -> crate::api::organization::OrganizationApi {
40 crate::api::organization::OrganizationApi::new(self.clone())
5241 }
5342
54 pub fn config(&self) -> &AppConfig {
55 &self.state.config
43 fn ssh_key(&self) -> crate::api::ssh_key::SshKeyApi {
44 crate::api::ssh_key::SshKeyApi::new(self.clone())
5645 }
5746
58 pub fn db(&self) -> &Arc<dyn Database> {
59 &self.state.database
47 fn mfa(&self) -> crate::api::mfa::MfaApi {
48 crate::api::mfa::MfaApi::new(self.clone())
6049 }
6150
62 pub fn redis(&self) -> &AppRedis {
63 &self.state.redis
51 pub fn read_request<T>(&self, message: T) -> Request<T> {
52 let mut request = Request::new(message);
53 request.set_timeout(self.read_timeout);
54 request.extensions_mut().insert(RequestMeta::internal());
55 request
6456 }
6557
66 pub async fn mail_locale(
67 &self,
68 uid: Option<i64>,
69 accept_language: Option<&str>,
70 ) -> i18n::UiLocale {
71 let fallback = i18n::UiLocale::parse(self.config().email.default_locale())
72 .unwrap_or(i18n::UiLocale::En);
73 match uid {
74 Some(uid) => i18n::resolve_user_locale(self.db().read(), uid, fallback).await,
75 None => i18n::negotiate_accept_language(accept_language, fallback),
76 }
58 pub fn write_request<T>(&self, message: T) -> Request<T> {
59 let mut request = Request::new(message);
60 request.set_timeout(self.write_timeout);
61 request.extensions_mut().insert(RequestMeta::internal());
62 request
7763 }
64}
7865
79 pub async fn verify_access_token(
80 &self,
81 _token: &str,
82 ) -> Result<Option<VerifiedAccessKey>, crate::api_error::AuthBusinessError> {
83 Ok(None)
84 }
85
86 pub async fn check_health(&self) -> Result<(), KenAuthError> {
87 self.service
88 .cache()
66#[async_trait]
67impl AuthApi for AuthService {
68 async fn check_health(&self) -> Result<(), KenAuthError> {
69 self.cache()
8970 .ping()
9071 .await
9172 .map_err(|error| KenAuthError::Unavailable(error.to_string()))
9273 }
9374
94 pub fn identity(&self) -> crate::internal::bridge::IdentityClient {
95 crate::internal::bridge::IdentityClient::new(self.service.as_ref().clone())
96 }
97
98 pub fn authority(&self) -> crate::internal::bridge::AuthorityClient {
99 crate::internal::bridge::AuthorityClient::new(self.service.as_ref().clone())
100 }
101
102 pub fn session(&self) -> crate::internal::bridge::SessionClient {
103 crate::internal::bridge::SessionClient::new(self.service.as_ref().clone())
104 }
105
106 pub fn organization(&self) -> crate::internal::bridge::OrganizationClient {
107 crate::internal::bridge::OrganizationClient::new(self.service.as_ref().clone())
108 }
109
110 pub fn authorization(&self) -> crate::internal::bridge::AuthorizationClient {
111 crate::internal::bridge::AuthorizationClient::new(self.service.as_ref().clone())
112 }
113
114 pub fn ssh_key(&self) -> crate::internal::bridge::SshKeyClient {
115 crate::internal::bridge::SshKeyClient::new(self.service.as_ref().clone())
116 }
117
118 pub fn mfa(&self) -> crate::internal::bridge::MfaClient {
119 crate::internal::bridge::MfaClient::new(self.service.as_ref().clone())
120 }
121
122 pub fn agreement(&self) -> crate::internal::bridge::AgreementClient {
123 crate::internal::bridge::AgreementClient::new(self.service.as_ref().clone())
124 }
125
12675 #[allow(clippy::too_many_arguments)]
127 pub async fn change_password(
76 async fn change_password(
12877 &self,
12978 user_id: i64,
13079 old_password_hash: &str,
@@ -132,7 +81,7 @@
13281 keep_session_id: &str,
13382 client_ip: &str,
13483 ) -> Result<proto::v1::ChangePasswordResponse, KenAuthError> {
135 let mut client = self.identity();
84 let client = self.identity();
13685 client
13786 .change_password(self.write_request(proto::v1::ChangePasswordRequest {
13887 user_id,
@@ -146,11 +95,11 @@
14695 .map(|response| response.into_inner())
14796 }
14897
149 pub async fn get_mfa_status(
98 async fn get_mfa_status(
15099 &self,
151100 user_id: i64,
152101 ) -> Result<proto::v1::GetMfaStatusResponse, KenAuthError> {
153 let mut client = self.mfa();
102 let client = self.mfa();
154103 client
155104 .get_mfa_status(self.read_request(proto::v1::GetMfaStatusRequest { user_id }))
156105 .await
@@ -158,13 +107,13 @@
158107 .map(|response| response.into_inner())
159108 }
160109
161 pub async fn configure_totp(
110 async fn configure_totp(
162111 &self,
163112 user_id: i64,
164113 name: &str,
165114 client_ip: &str,
166115 ) -> Result<proto::v1::ConfigureTotpResponse, KenAuthError> {
167 let mut client = self.mfa();
116 let client = self.mfa();
168117 client
169118 .configure_totp(self.write_request(proto::v1::ConfigureTotpRequest {
170119 user_id,
@@ -176,14 +125,14 @@
176125 .map(|response| response.into_inner())
177126 }
178127
179 pub async fn enable_totp(
128 async fn enable_totp(
180129 &self,
181130 user_id: i64,
182131 code: &str,
183132 password_hash: &str,
184133 client_ip: &str,
185134 ) -> Result<proto::v1::EnableTotpResponse, KenAuthError> {
186 let mut client = self.mfa();
135 let client = self.mfa();
187136 client
188137 .enable_totp(self.write_request(proto::v1::EnableTotpRequest {
189138 user_id,
@@ -196,13 +145,13 @@
196145 .map(|response| response.into_inner())
197146 }
198147
199 pub async fn disable_totp(
148 async fn disable_totp(
200149 &self,
201150 user_id: i64,
202151 code: &str,
203152 client_ip: &str,
204153 ) -> Result<proto::v1::DisableTotpResponse, KenAuthError> {
205 let mut client = self.mfa();
154 let client = self.mfa();
206155 client
207156 .disable_totp(self.write_request(proto::v1::DisableTotpRequest {
208157 user_id,
@@ -214,13 +163,13 @@
214163 .map(|response| response.into_inner())
215164 }
216165
217 pub async fn regenerate_recovery_codes(
166 async fn regenerate_recovery_codes(
218167 &self,
219168 user_id: i64,
220169 password_hash: &str,
221170 client_ip: &str,
222171 ) -> Result<proto::v1::RegenerateRecoveryCodesResponse, KenAuthError> {
223 let mut client = self.mfa();
172 let client = self.mfa();
224173 client
225174 .regenerate_recovery_codes(self.write_request(
226175 proto::v1::RegenerateRecoveryCodesRequest {
@@ -236,13 +185,13 @@
236185
237186 #[allow(clippy::too_many_arguments)]
238187
239 pub async fn list_user_ssh_keys(
188 async fn list_user_ssh_keys(
240189 &self,
241190 user_id: i64,
242191 include_revoked: bool,
243192 page: proto::common::v1::PageRequest,
244193 ) -> Result<proto::v1::ListUserSshKeysResponse, KenAuthError> {
245 let mut client = self.ssh_key();
194 let client = self.ssh_key();
246195 client
247196 .list_user_ssh_keys(self.read_request(proto::v1::ListUserSshKeysRequest {
248197 user_id,
@@ -254,7 +203,7 @@
254203 .map(|response| response.into_inner())
255204 }
256205
257 pub async fn add_ssh_key(
206 async fn add_ssh_key(
258207 &self,
259208 user_id: i64,
260209 name: &str,
@@ -262,7 +211,7 @@
262211 description: &str,
263212 client_ip: &str,
264213 ) -> Result<proto::v1::AddSshKeyResponse, KenAuthError> {
265 let mut client = self.ssh_key();
214 let client = self.ssh_key();
266215 client
267216 .add_ssh_key(self.write_request(proto::v1::AddSshKeyRequest {
268217 user_id,
@@ -276,14 +225,14 @@
276225 .map(|response| response.into_inner())
277226 }
278227
279 pub async fn revoke_ssh_key(
228 async fn revoke_ssh_key(
280229 &self,
281230 user_id: i64,
282231 key_id: &str,
283232 reason: &str,
284233 bump_security_version: bool,
285234 ) -> Result<proto::v1::RevokeSshKeyResponse, KenAuthError> {
286 let mut client = self.ssh_key();
235 let client = self.ssh_key();
287236 client
288237 .revoke_ssh_key(self.write_request(proto::v1::RevokeSshKeyRequest {
289238 user_id,
@@ -296,14 +245,14 @@
296245 .map(|response| response.into_inner())
297246 }
298247
299 pub async fn list_user_sessions(
248 async fn list_user_sessions(
300249 &self,
301250 user_id: i64,
302251 include_revoked: bool,
303252 include_expired: bool,
304253 page: proto::common::v1::PageRequest,
305254 ) -> Result<proto::v1::ListUserSessionsResponse, KenAuthError> {
306 let mut client = self.session();
255 let client = self.session();
307256 client
308257 .list_user_sessions(self.read_request(proto::v1::ListUserSessionsRequest {
309258 user_id,
@@ -316,14 +265,14 @@
316265 .map(|response| response.into_inner())
317266 }
318267
319 pub async fn revoke_user_sessions(
268 async fn revoke_user_sessions(
320269 &self,
321270 user_id: i64,
322271 except_session_id: &str,
323272 reason: &str,
324273 bump_security_version: bool,
325274 ) -> Result<proto::v1::RevokeUserSessionsResponse, KenAuthError> {
326 let mut client = self.session();
275 let client = self.session();
327276 client
328277 .revoke_user_sessions(self.write_request(proto::v1::RevokeUserSessionsRequest {
329278 user_id,
@@ -336,25 +285,13 @@
336285 .map(|response| response.into_inner())
337286 }
338287
339 pub fn read_request<T>(&self, message: T) -> Request<T> {
340 let mut request = Request::new(message);
341 request.set_timeout(self.read_timeout);
342 request
343 }
344
345 pub fn write_request<T>(&self, message: T) -> Request<T> {
346 let mut request = Request::new(message);
347 request.set_timeout(self.write_timeout);
348 request
349 }
350
351 pub async fn get_session(
288 async fn get_session(
352289 &self,
353290 user_id: i64,
354291 version: i32,
355292 session_id: &str,
356293 ) -> Result<proto::internal::v1::AuthorityAccess, KenAuthError> {
357 let mut client = self.authority();
294 let client = self.authority();
358295 client
359296 .get_session(self.read_request(proto::internal::v1::GetSessionRequest {
360297 user_id,
@@ -366,8 +303,8 @@
366303 .map(|response| response.into_inner().access.unwrap_or_default())
367304 }
368305
369 pub async fn delete_account(&self, user_id: i64, reason: &str) -> Result<(), KenAuthError> {
370 let mut client = self.authority();
306 async fn delete_account(&self, user_id: i64, reason: &str) -> Result<(), KenAuthError> {
307 let client = self.authority();
371308 client
372309 .delete_account(
373310 self.write_request(proto::internal::v1::DeleteAccountRequest {
@@ -380,8 +317,8 @@
380317 Ok(())
381318 }
382319
383 pub async fn cancel_account_deletion(&self, user_id: i64) -> Result<(), KenAuthError> {
384 let mut client = self.authority();
320 async fn cancel_account_deletion(&self, user_id: i64) -> Result<(), KenAuthError> {
321 let client = self.authority();
385322 client
386323 .cancel_account_deletion(
387324 self.write_request(proto::internal::v1::CancelAccountDeletionRequest { user_id }),
@@ -391,8 +328,8 @@
391328 Ok(())
392329 }
393330
394 pub async fn purge_account(&self, user_id: i64) -> Result<(), KenAuthError> {
395 let mut client = self.authority();
331 async fn purge_account(&self, user_id: i64) -> Result<(), KenAuthError> {
332 let client = self.authority();
396333 client
397334 .purge_account(self.write_request(proto::internal::v1::PurgeAccountRequest { user_id }))
398335 .await
@@ -400,11 +337,11 @@
400337 Ok(())
401338 }
402339
403 pub async fn batch_get_authority_users(
340 async fn batch_get_authority_users(
404341 &self,
405342 user_ids: Vec<i64>,
406343 ) -> Result<Vec<proto::internal::v1::AuthorityUser>, KenAuthError> {
407 let mut client = self.authority();
344 let client = self.authority();
408345 client
409346 .batch_get_user(
410347 self.read_request(proto::internal::v1::BatchGetUserRequest { user_ids }),
@@ -414,11 +351,11 @@
414351 .map(|response| response.into_inner().users)
415352 }
416353
417 pub async fn get_user_status(
354 async fn get_user_status(
418355 &self,
419356 user_id: i64,
420357 ) -> Result<proto::common::v1::UserStatus, KenAuthError> {
421 let mut client = self.identity();
358 let client = self.identity();
422359 let user = Some(proto::common::v1::UserRef {
423360 key: Some(proto::common::v1::user_ref::Key::UserId(user_id)),
424361 });
@@ -429,11 +366,11 @@
429366 .map(|response| response.into_inner().status.unwrap_or_default())
430367 }
431368
432 pub async fn get_user_public_by_ref(
369 async fn get_user_public_by_ref(
433370 &self,
434371 key: proto::common::v1::user_ref::Key,
435372 ) -> Result<proto::common::v1::UserPublic, KenAuthError> {
436 let mut client = self.identity();
373 let client = self.identity();
437374 let user = Some(proto::common::v1::UserRef { key: Some(key) });
438375 client
439376 .get_user_public(self.read_request(proto::v1::GetUserPublicRequest { user }))
@@ -444,11 +381,11 @@
444381 .ok_or(KenAuthError::NotFound)
445382 }
446383
447 pub async fn get_authority_user(
384 async fn get_authority_user(
448385 &self,
449386 user_id: i64,
450387 ) -> Result<proto::internal::v1::AuthorityUser, KenAuthError> {
451 let mut client = self.authority();
388 let client = self.authority();
452389 client
453390 .get_user(self.read_request(proto::internal::v1::GetUserRequest { user_id }))
454391 .await
@@ -458,10 +395,8 @@
458395 .ok_or(KenAuthError::NotFound)
459396 }
460397
461 /// The only supported write path for `username` / `displayname`.
462 /// `version` is the account's current optimistic-lock version.
463398 #[allow(clippy::too_many_arguments)]
464 pub async fn update_authority_user(
399 async fn update_authority_user(
465400 &self,
466401 user_id: i64,
467402 username: Option<String>,
@@ -470,7 +405,7 @@
470405 public_show_email: Option<bool>,
471406 version: i32,
472407 ) -> Result<proto::internal::v1::AuthorityUser, KenAuthError> {
473 let mut client = self.authority();
408 let client = self.authority();
474409 client
475410 .update_user(self.write_request(proto::internal::v1::UpdateUserRequest {
476411 user_id,
@@ -489,11 +424,11 @@
489424 .ok_or(KenAuthError::NotFound)
490425 }
491426
492 pub async fn batch_get_user_public(
427 async fn batch_get_user_public(
493428 &self,
494429 user_ids: Vec<i64>,
495430 ) -> Result<Vec<proto::common::v1::UserPublic>, KenAuthError> {
496 let mut client = self.identity();
431 let client = self.identity();
497432 client
498433 .batch_get_user_public(
499434 self.read_request(proto::v1::BatchGetUserPublicRequest { user_ids }),
@@ -503,11 +438,11 @@
503438 .map(|response| response.into_inner().users)
504439 }
505440
506 pub async fn get_user_public(
441 async fn get_user_public(
507442 &self,
508443 user_id: i64,
509444 ) -> Result<proto::common::v1::UserPublic, KenAuthError> {
510 let mut client = self.identity();
445 let client = self.identity();
511446 let user = Some(proto::common::v1::UserRef {
512447 key: Some(proto::common::v1::user_ref::Key::UserId(user_id)),
513448 });
@@ -520,13 +455,13 @@
520455 .ok_or(KenAuthError::NotFound)
521456 }
522457
523 pub async fn revoke_session(
458 async fn revoke_session(
524459 &self,
525460 user_id: i64,
526461 session_id: &str,
527462 reason: &str,
528463 ) -> Result<(), KenAuthError> {
529 let mut client = self.session();
464 let client = self.session();
530465 client
531466 .revoke_session(self.write_request(proto::v1::RevokeSessionRequest {
532467 user_id,
@@ -538,11 +473,11 @@
538473 Ok(())
539474 }
540475
541 pub async fn lookup_ssh_key(
476 async fn lookup_ssh_key(
542477 &self,
543478 public_key: &str,
544479 ) -> Result<Option<proto::common::v1::Principal>, KenAuthError> {
545 let mut client = self.ssh_key();
480 let client = self.ssh_key();
546481 let response = client
547482 .lookup_ssh_key(self.read_request(proto::v1::LookupSshKeyRequest {
548483 fingerprint: String::new(),
@@ -558,13 +493,13 @@
558493 Ok(response.principal)
559494 }
560495
561 pub async fn ensure_bot_user(
496 async fn ensure_bot_user(
562497 &self,
563498 username: &str,
564499 displayname: &str,
565500 system: bool,
566501 ) -> Result<proto::common::v1::UserPublic, KenAuthError> {
567 let mut client = self.identity();
502 let client = self.identity();
568503 client
569504 .ensure_bot_user(self.write_request(proto::v1::EnsureBotUserRequest {
570505 username: username.to_string(),
@@ -578,14 +513,14 @@
578513 .ok_or(KenAuthError::NotFound)
579514 }
580515
581 pub async fn ensure_bot_member(
516 async fn ensure_bot_member(
582517 &self,
583518 organization_id: &str,
584519 user_id: i64,
585520 role_keys: Vec<String>,
586521 role_ids: Vec<String>,
587522 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
588 let mut client = self.organization();
523 let client = self.organization();
589524 client
590525 .ensure_bot_member(self.write_request(proto::v1::EnsureBotMemberRequest {
591526 organization_id: organization_id.to_string(),
@@ -600,13 +535,13 @@
600535 .ok_or(KenAuthError::NotFound)
601536 }
602537
603 pub async fn get_organization(
538 async fn get_organization(
604539 &self,
605540 actor_user_id: i64,
606541 organization_id: &str,
607542 slug: &str,
608543 ) -> Result<proto::v1::GetOrganizationResponse, KenAuthError> {
609 let mut client = self.organization();
544 let client = self.organization();
610545 client
611546 .get_organization(self.read_request(proto::v1::GetOrganizationRequest {
612547 actor_user_id,
@@ -618,13 +553,13 @@
618553 .map(|response| response.into_inner())
619554 }
620555
621 pub async fn get_member(
556 async fn get_member(
622557 &self,
623558 actor_user_id: i64,
624559 organization_id: &str,
625560 user_id: i64,
626561 ) -> Result<Option<proto::v1::OrganizationMemberInfo>, KenAuthError> {
627 let mut client = self.organization();
562 let client = self.organization();
628563 match client
629564 .get_member(self.read_request(proto::v1::GetMemberRequest {
630565 actor_user_id,
@@ -640,7 +575,7 @@
640575 }
641576 }
642577
643 pub async fn list_members(
578 async fn list_members(
644579 &self,
645580 actor_user_id: i64,
646581 organization_id: &str,
@@ -648,7 +583,7 @@
648583 include_banned: bool,
649584 page: proto::common::v1::PageRequest,
650585 ) -> Result<proto::v1::ListMembersResponse, KenAuthError> {
651 let mut client = self.organization();
586 let client = self.organization();
652587 client
653588 .list_members(self.read_request(proto::v1::ListMembersRequest {
654589 actor_user_id,
@@ -662,14 +597,14 @@
662597 .map(|response| response.into_inner())
663598 }
664599
665 pub async fn list_organizations(
600 async fn list_organizations(
666601 &self,
667602 actor_user_id: i64,
668603 user_id: i64,
669604 include_archived: bool,
670605 page: proto::common::v1::PageRequest,
671606 ) -> Result<proto::v1::ListOrganizationsResponse, KenAuthError> {
672 let mut client = self.organization();
607 let client = self.organization();
673608 client
674609 .list_organizations(self.read_request(proto::v1::ListOrganizationsRequest {
675610 actor_user_id,
@@ -682,12 +617,12 @@
682617 .map(|response| response.into_inner())
683618 }
684619
685 pub async fn list_public_organizations(
620 async fn list_public_organizations(
686621 &self,
687622 query: &str,
688623 page: proto::common::v1::PageRequest,
689624 ) -> Result<proto::v1::ListPublicOrganizationsResponse, KenAuthError> {
690 let mut client = self.organization();
625 let client = self.organization();
691626 client
692627 .list_public_organizations(self.read_request(
693628 proto::v1::ListPublicOrganizationsRequest {
@@ -700,14 +635,14 @@
700635 .map(|response| response.into_inner())
701636 }
702637
703 pub async fn check_permissions(
638 async fn check_permissions(
704639 &self,
705640 actor_user_id: i64,
706641 organization_id: &str,
707642 user_id: i64,
708643 permissions: Vec<String>,
709644 ) -> Result<proto::v1::CheckPermissionsResponse, KenAuthError> {
710 let mut client = self.organization();
645 let client = self.organization();
711646 client
712647 .check_permissions(self.read_request(proto::v1::CheckPermissionsRequest {
713648 actor_user_id,
@@ -720,7 +655,7 @@
720655 .map(|response| response.into_inner())
721656 }
722657
723 pub async fn create_organization(
658 async fn create_organization(
724659 &self,
725660 actor_user_id: i64,
726661 slug: &str,
@@ -736,7 +671,7 @@
736671 allow_invite: Option<bool>,
737672 allow_join_level: Option<i32>,
738673 ) -> Result<proto::v1::CreateOrganizationResponse, KenAuthError> {
739 let mut client = self.organization();
674 let client = self.organization();
740675 client
741676 .create_organization(self.write_request(proto::v1::CreateOrganizationRequest {
742677 actor_user_id,
@@ -759,7 +694,7 @@
759694 }
760695
761696 #[allow(clippy::too_many_arguments)]
762 pub async fn update_organization(
697 async fn update_organization(
763698 &self,
764699 actor_user_id: i64,
765700 organization_id: &str,
@@ -776,7 +711,7 @@
776711 allow_invite: Option<bool>,
777712 allow_join_level: Option<i32>,
778713 ) -> Result<proto::v1::OrganizationInfo, KenAuthError> {
779 let mut client = self.organization();
714 let client = self.organization();
780715 client
781716 .update_organization(self.write_request(proto::v1::UpdateOrganizationRequest {
782717 actor_user_id,
@@ -801,13 +736,13 @@
801736 .ok_or(KenAuthError::NotFound)
802737 }
803738
804 pub async fn archive_organization(
739 async fn archive_organization(
805740 &self,
806741 actor_user_id: i64,
807742 organization_id: &str,
808743 reason: &str,
809744 ) -> Result<(), KenAuthError> {
810 let mut client = self.organization();
745 let client = self.organization();
811746 client
812747 .archive_organization(self.write_request(proto::v1::ArchiveOrganizationRequest {
813748 actor_user_id,
@@ -819,12 +754,12 @@
819754 Ok(())
820755 }
821756
822 pub async fn restore_organization(
757 async fn restore_organization(
823758 &self,
824759 actor_user_id: i64,
825760 organization_id: &str,
826761 ) -> Result<(), KenAuthError> {
827 let mut client = self.organization();
762 let client = self.organization();
828763 client
829764 .restore_organization(self.write_request(proto::v1::RestoreOrganizationRequest {
830765 actor_user_id,
@@ -835,14 +770,14 @@
835770 Ok(())
836771 }
837772
838 pub async fn delete_organization(
773 async fn delete_organization(
839774 &self,
840775 actor_user_id: i64,
841776 organization_id: &str,
842777 force: bool,
843778 reason: &str,
844779 ) -> Result<(), KenAuthError> {
845 let mut client = self.organization();
780 let client = self.organization();
846781 client
847782 .delete_organization(self.write_request(proto::v1::DeleteOrganizationRequest {
848783 actor_user_id,
@@ -854,7 +789,8 @@
854789 .map_err(KenAuthError::from_status)?;
855790 Ok(())
856791 }
857 pub async fn add_member(
792
793 async fn add_member(
858794 &self,
859795 actor_user_id: i64,
860796 organization_id: &str,
@@ -862,7 +798,7 @@
862798 role: i32,
863799 role_keys: Vec<String>,
864800 ) -> Result<(proto::v1::OrganizationMemberInfo, bool), KenAuthError> {
865 let mut client = self.organization();
801 let client = self.organization();
866802 let response = client
867803 .add_member(self.write_request(proto::v1::AddMemberRequest {
868804 actor_user_id,
@@ -878,14 +814,14 @@
878814 Ok((member, response.created))
879815 }
880816
881 pub async fn remove_member(
817 async fn remove_member(
882818 &self,
883819 actor_user_id: i64,
884820 organization_id: &str,
885821 user_id: i64,
886822 reason: &str,
887823 ) -> Result<bool, KenAuthError> {
888 let mut client = self.organization();
824 let client = self.organization();
889825 client
890826 .remove_member(self.write_request(proto::v1::RemoveMemberRequest {
891827 actor_user_id,
@@ -899,12 +835,12 @@
899835 .map(|response| response.into_inner().removed)
900836 }
901837
902 pub async fn leave_organization(
838 async fn leave_organization(
903839 &self,
904840 actor_user_id: i64,
905841 organization_id: &str,
906842 ) -> Result<bool, KenAuthError> {
907 let mut client = self.organization();
843 let client = self.organization();
908844 client
909845 .leave_organization(self.write_request(proto::v1::LeaveOrganizationRequest {
910846 actor_user_id,
@@ -916,7 +852,7 @@
916852 }
917853
918854 #[allow(clippy::too_many_arguments)]
919 pub async fn update_member(
855 async fn update_member(
920856 &self,
921857 actor_user_id: i64,
922858 organization_id: &str,
@@ -936,7 +872,7 @@
936872 Some(Some(value)) => (None, Some(value)),
937873 None => (None, None),
938874 };
939 let mut client = self.organization();
875 let client = self.organization();
940876 client
941877 .update_member(self.write_request(proto::v1::UpdateMemberRequest {
942878 actor_user_id,
@@ -962,14 +898,14 @@
962898 .ok_or(KenAuthError::NotFound)
963899 }
964900
965 pub async fn ban_member(
901 async fn ban_member(
966902 &self,
967903 actor_user_id: i64,
968904 organization_id: &str,
969905 user_id: i64,
970906 reason: &str,
971907 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
972 let mut client = self.organization();
908 let client = self.organization();
973909 client
974910 .ban_member(self.write_request(proto::v1::BanMemberRequest {
975911 actor_user_id,
@@ -985,13 +921,13 @@
985921 .ok_or(KenAuthError::NotFound)
986922 }
987923
988 pub async fn unban_member(
924 async fn unban_member(
989925 &self,
990926 actor_user_id: i64,
991927 organization_id: &str,
992928 user_id: i64,
993929 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
994 let mut client = self.organization();
930 let client = self.organization();
995931 client
996932 .unban_member(self.write_request(proto::v1::UnbanMemberRequest {
997933 actor_user_id,
@@ -1007,7 +943,7 @@
1007943 }
1008944
1009945 #[allow(clippy::too_many_arguments)]
1010 pub async fn update_ban(
946 async fn update_ban(
1011947 &self,
1012948 actor_user_id: i64,
1013949 organization_id: &str,
@@ -1023,7 +959,7 @@
1023959 Some(Some(value)) => (None, Some(value)),
1024960 None => (None, None),
1025961 };
1026 let mut client = self.organization();
962 let client = self.organization();
1027963 client
1028964 .update_ban(self.write_request(proto::v1::UpdateBanRequest {
1029965 actor_user_id,
@@ -1045,7 +981,7 @@
1045981 }
1046982
1047983 #[allow(clippy::too_many_arguments)]
1048 pub async fn create_role(
984 async fn create_role(
1049985 &self,
1050986 actor_user_id: i64,
1051987 organization_id: &str,
@@ -1056,7 +992,7 @@
1056992 self_assignable: bool,
1057993 permissions: Vec<String>,
1058994 ) -> Result<proto::v1::OrganizationRoleInfo, KenAuthError> {
1059 let mut client = self.organization();
995 let client = self.organization();
1060996 client
1061997 .create_role(self.write_request(proto::v1::CreateRoleRequest {
1062998 actor_user_id,
@@ -1077,7 +1013,7 @@
10771013 }
10781014
10791015 #[allow(clippy::too_many_arguments)]
1080 pub async fn update_role(
1016 async fn update_role(
10811017 &self,
10821018 actor_user_id: i64,
10831019 organization_id: &str,
@@ -1088,7 +1024,7 @@
10881024 self_assignable: Option<bool>,
10891025 expected_version: i32,
10901026 ) -> Result<proto::v1::OrganizationRoleInfo, KenAuthError> {
1091 let mut client = self.organization();
1027 let client = self.organization();
10921028 client
10931029 .update_role(self.write_request(proto::v1::UpdateRoleRequest {
10941030 actor_user_id,
@@ -1108,14 +1044,14 @@
11081044 .ok_or(KenAuthError::NotFound)
11091045 }
11101046
1111 pub async fn set_role_permissions(
1047 async fn set_role_permissions(
11121048 &self,
11131049 actor_user_id: i64,
11141050 organization_id: &str,
11151051 role_id: &str,
11161052 permissions: Vec<String>,
11171053 ) -> Result<proto::v1::OrganizationRoleInfo, KenAuthError> {
1118 let mut client = self.organization();
1054 let client = self.organization();
11191055 client
11201056 .set_role_permissions(self.write_request(proto::v1::SetRolePermissionsRequest {
11211057 actor_user_id,
@@ -1130,13 +1066,13 @@
11301066 .ok_or(KenAuthError::NotFound)
11311067 }
11321068
1133 pub async fn delete_role(
1069 async fn delete_role(
11341070 &self,
11351071 actor_user_id: i64,
11361072 organization_id: &str,
11371073 role_id: &str,
11381074 ) -> Result<bool, KenAuthError> {
1139 let mut client = self.organization();
1075 let client = self.organization();
11401076 client
11411077 .delete_role(self.write_request(proto::v1::DeleteRoleRequest {
11421078 actor_user_id,
@@ -1148,14 +1084,14 @@
11481084 .map(|response| response.into_inner().deleted)
11491085 }
11501086
1151 pub async fn assign_role(
1087 async fn assign_role(
11521088 &self,
11531089 actor_user_id: i64,
11541090 organization_id: &str,
11551091 user_id: i64,
11561092 role_id: &str,
11571093 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
1158 let mut client = self.organization();
1094 let client = self.organization();
11591095 client
11601096 .assign_role(self.write_request(proto::v1::AssignRoleRequest {
11611097 actor_user_id,
@@ -1171,14 +1107,14 @@
11711107 .ok_or(KenAuthError::NotFound)
11721108 }
11731109
1174 pub async fn unassign_role(
1110 async fn unassign_role(
11751111 &self,
11761112 actor_user_id: i64,
11771113 organization_id: &str,
11781114 user_id: i64,
11791115 role_id: &str,
11801116 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
1181 let mut client = self.organization();
1117 let client = self.organization();
11821118 client
11831119 .unassign_role(self.write_request(proto::v1::UnassignRoleRequest {
11841120 actor_user_id,
@@ -1195,7 +1131,7 @@
11951131 }
11961132
11971133 #[allow(clippy::too_many_arguments)]
1198 pub async fn create_invite(
1134 async fn create_invite(
11991135 &self,
12001136 actor_user_id: i64,
12011137 organization_id: &str,
@@ -1206,7 +1142,7 @@
12061142 target_user_id: i64,
12071143 email: &str,
12081144 ) -> Result<(proto::v1::OrganizationInviteInfo, String), KenAuthError> {
1209 let mut client = self.organization();
1145 let client = self.organization();
12101146 let response = client
12111147 .create_invite(self.write_request(proto::v1::CreateInviteRequest {
12121148 actor_user_id,
@@ -1226,13 +1162,13 @@
12261162 Ok((invite, response.token))
12271163 }
12281164
1229 pub async fn revoke_invite(
1165 async fn revoke_invite(
12301166 &self,
12311167 actor_user_id: i64,
12321168 organization_id: &str,
12331169 invite_id: &str,
12341170 ) -> Result<bool, KenAuthError> {
1235 let mut client = self.organization();
1171 let client = self.organization();
12361172 client
12371173 .revoke_invite(self.write_request(proto::v1::RevokeInviteRequest {
12381174 actor_user_id,
@@ -1244,12 +1180,12 @@
12441180 .map(|response| response.into_inner().revoked)
12451181 }
12461182
1247 pub async fn redeem_invite(
1183 async fn redeem_invite(
12481184 &self,
12491185 actor_user_id: i64,
12501186 token: &str,
12511187 ) -> Result<proto::v1::RedeemInviteResponse, KenAuthError> {
1252 let mut client = self.organization();
1188 let client = self.organization();
12531189 client
12541190 .redeem_invite(self.write_request(proto::v1::RedeemInviteRequest {
12551191 actor_user_id,
@@ -1260,7 +1196,7 @@
12601196 .map(|response| response.into_inner())
12611197 }
12621198
1263 pub async fn create_join_rule(
1199 async fn create_join_rule(
12641200 &self,
12651201 actor_user_id: i64,
12661202 organization_id: &str,
@@ -1269,7 +1205,7 @@
12691205 matcher: &str,
12701206 enabled: bool,
12711207 ) -> Result<proto::v1::JoinRuleInfo, KenAuthError> {
1272 let mut client = self.organization();
1208 let client = self.organization();
12731209 client
12741210 .create_join_rule(self.write_request(proto::v1::CreateJoinRuleRequest {
12751211 actor_user_id,
@@ -1287,7 +1223,7 @@
12871223 }
12881224
12891225 #[allow(clippy::too_many_arguments)]
1290 pub async fn update_join_rule(
1226 async fn update_join_rule(
12911227 &self,
12921228 actor_user_id: i64,
12931229 organization_id: &str,
@@ -1297,7 +1233,7 @@
12971233 matcher: Option<String>,
12981234 enabled: Option<bool>,
12991235 ) -> Result<proto::v1::JoinRuleInfo, KenAuthError> {
1300 let mut client = self.organization();
1236 let client = self.organization();
13011237 client
13021238 .update_join_rule(self.write_request(proto::v1::UpdateJoinRuleRequest {
13031239 actor_user_id,
@@ -1315,13 +1251,13 @@
13151251 .ok_or(KenAuthError::NotFound)
13161252 }
13171253
1318 pub async fn delete_join_rule(
1254 async fn delete_join_rule(
13191255 &self,
13201256 actor_user_id: i64,
13211257 organization_id: &str,
13221258 rule_id: &str,
13231259 ) -> Result<bool, KenAuthError> {
1324 let mut client = self.organization();
1260 let client = self.organization();
13251261 client
13261262 .delete_join_rule(self.write_request(proto::v1::DeleteJoinRuleRequest {
13271263 actor_user_id,
@@ -1333,13 +1269,13 @@
13331269 .map(|response| response.into_inner().deleted)
13341270 }
13351271
1336 pub async fn create_join_request(
1272 async fn create_join_request(
13371273 &self,
13381274 actor_user_id: i64,
13391275 organization_id: &str,
13401276 note: &str,
13411277 ) -> Result<proto::v1::JoinRequestInfo, KenAuthError> {
1342 let mut client = self.organization();
1278 let client = self.organization();
13431279 client
13441280 .create_join_request(self.write_request(proto::v1::CreateJoinRequestRequest {
13451281 actor_user_id,
@@ -1353,13 +1289,13 @@
13531289 .ok_or(KenAuthError::NotFound)
13541290 }
13551291
1356 pub async fn withdraw_join_request(
1292 async fn withdraw_join_request(
13571293 &self,
13581294 actor_user_id: i64,
13591295 organization_id: &str,
13601296 request_id: &str,
13611297 ) -> Result<proto::v1::JoinRequestInfo, KenAuthError> {
1362 let mut client = self.organization();
1298 let client = self.organization();
13631299 client
13641300 .withdraw_join_request(self.write_request(proto::v1::WithdrawJoinRequestRequest {
13651301 actor_user_id,
@@ -1373,7 +1309,7 @@
13731309 .ok_or(KenAuthError::NotFound)
13741310 }
13751311
1376 pub async fn approve_join_request(
1312 async fn approve_join_request(
13771313 &self,
13781314 actor_user_id: i64,
13791315 organization_id: &str,
@@ -1385,7 +1321,7 @@
13851321 ),
13861322 KenAuthError,
13871323 > {
1388 let mut client = self.organization();
1324 let client = self.organization();
13891325 let response = client
13901326 .approve_join_request(self.write_request(proto::v1::ApproveJoinRequestRequest {
13911327 actor_user_id,
@@ -1399,14 +1335,14 @@
13991335 Ok((request, response.member))
14001336 }
14011337
1402 pub async fn reject_join_request(
1338 async fn reject_join_request(
14031339 &self,
14041340 actor_user_id: i64,
14051341 organization_id: &str,
14061342 request_id: &str,
14071343 note: &str,
14081344 ) -> Result<proto::v1::JoinRequestInfo, KenAuthError> {
1409 let mut client = self.organization();
1345 let client = self.organization();
14101346 client
14111347 .reject_join_request(self.write_request(proto::v1::RejectJoinRequestRequest {
14121348 actor_user_id,
@@ -1426,13 +1362,5 @@
14261362 proto::v1::OrganizationPermissionGrant {
14271363 permission,
14281364 effect: proto::v1::PermissionEffect::Allow as i32,
1429 }
1430}
1431
1432impl std::ops::Deref for AuthService {
1433 type Target = AuthSvc;
1434
1435 fn deref(&self) -> &Self::Target {
1436 self.service.as_ref()
14371365 }
14381366}
Deleted lib/auth/src/api_error.rs
@@ -1,125 +0,0 @@
1use std::fmt;
2
3#[derive(Debug)]
4pub enum AuthBusinessError {
5 BadRequest(String),
6 InvalidCredentials,
7 Unauthorized,
8 Forbidden,
9 NotFound,
10 Conflict(String),
11 RateLimited,
12 Internal(String),
13 TwoFactorExpired,
14 TwoFactorCodeInvalid,
15 RecoveryCodeInvalid,
16 VerificationCodeExpired,
17 VerificationCodeInvalid,
18 ResetTokenInvalid,
19}
20
21impl fmt::Display for AuthBusinessError {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 match self {
24 Self::BadRequest(message) => write!(f, "{message}"),
25 Self::InvalidCredentials => write!(f, "invalid credentials"),
26 Self::Unauthorized => write!(f, "unauthorized"),
27 Self::Forbidden => write!(f, "forbidden"),
28 Self::NotFound => write!(f, "not found"),
29 Self::Conflict(message) => write!(f, "{message}"),
30 Self::RateLimited => write!(f, "rate limit exceeded"),
31 Self::Internal(message) => write!(f, "{message}"),
32 Self::TwoFactorExpired => write!(f, "two-factor challenge expired"),
33 Self::TwoFactorCodeInvalid => write!(f, "invalid two-factor code"),
34 Self::RecoveryCodeInvalid => write!(f, "invalid or used recovery code"),
35 Self::VerificationCodeExpired => write!(f, "verification code expired"),
36 Self::VerificationCodeInvalid => write!(f, "invalid verification code"),
37 Self::ResetTokenInvalid => write!(f, "invalid or expired reset token"),
38 }
39 }
40}
41
42impl std::error::Error for AuthBusinessError {}
43
44impl From<deletion::DeletionError> for AuthBusinessError {
45 fn from(error: deletion::DeletionError) -> Self {
46 use deletion::DeletionError;
47 match error {
48 DeletionError::Database(error) => Self::from(error),
49 DeletionError::Conflict(message) => Self::Conflict(message),
50 DeletionError::NotFound(message) => {
51 tracing::debug!(%message, "deletion target not found");
52 Self::NotFound
53 }
54 DeletionError::KenAuth(message)
55 | DeletionError::KenShell(message)
56 | DeletionError::Config(message) => Self::Internal(message),
57 }
58 }
59}
60
61export::db_error_conversions!(AuthBusinessError);
62export::redis_error_conversion!(AuthBusinessError);
63export::export_error_conversion!(AuthBusinessError);
64export::json_payload_conversion!(AuthBusinessError);
65
66export::api_error! {
67 log: "auth",
68 pub enum ApiError {
69 BadRequest(400, "request failed validation"),
70 InvalidCredentials(400, "credentials rejected"),
71 Unauthorized(401, "authentication required"),
72 Forbidden(403, "operation not allowed"),
73 NotFound(404, "resource not found"),
74 Conflict(409, "resource conflict"),
75 RateLimited(429, "rate limit exceeded"),
76 Internal(500, "internal server error"),
77 }
78}
79
80impl From<AuthBusinessError> for ApiError {
81 fn from(error: AuthBusinessError) -> Self {
82 match error {
83 AuthBusinessError::BadRequest(msg) => Self::bad_request(msg),
84 AuthBusinessError::InvalidCredentials => {
85 Self::build(400, Self::payload(400, "invalid credentials"))
86 }
87 AuthBusinessError::Unauthorized => Self::unauthorized(),
88 AuthBusinessError::Forbidden => Self::forbidden(),
89 AuthBusinessError::NotFound => Self::not_found("auth resource not found"),
90 AuthBusinessError::Conflict(msg) => Self::conflict(msg),
91 AuthBusinessError::RateLimited => Self::rate_limited_exceeded(),
92 AuthBusinessError::TwoFactorExpired => Self::build(
93 400,
94 Self::payload(400, "two-factor challenge expired")
95 .with_error_code("two_factor_expired"),
96 ),
97 AuthBusinessError::TwoFactorCodeInvalid => Self::build(
98 400,
99 Self::payload(400, "invalid two-factor code")
100 .with_error_code("two_factor_code_invalid"),
101 ),
102 AuthBusinessError::RecoveryCodeInvalid => Self::build(
103 400,
104 Self::payload(400, "invalid or used recovery code")
105 .with_error_code("recovery_code_invalid"),
106 ),
107 AuthBusinessError::VerificationCodeExpired => Self::build(
108 400,
109 Self::payload(400, "verification code expired")
110 .with_error_code("verification_code_expired"),
111 ),
112 AuthBusinessError::VerificationCodeInvalid => Self::build(
113 400,
114 Self::payload(400, "invalid verification code")
115 .with_error_code("verification_code_invalid"),
116 ),
117 AuthBusinessError::ResetTokenInvalid => Self::build(
118 400,
119 Self::payload(400, "invalid or expired reset token")
120 .with_error_code("reset_token_invalid"),
121 ),
122 AuthBusinessError::Internal(message) => Self::internal(message),
123 }
124 }
125}
Deleted lib/auth/src/auth_api_error.rs
@@ -1 +0,0 @@
1pub use export::KenAuthError;
Deleted lib/auth/src/auth_api_impl.rs
@@ -1,759 +0,0 @@
1use async_trait::async_trait;
2
3use crate::protos::auth as proto;
4use crate::service::AuthService;
5use export::{AuthApi, KenAuthError};
6
7#[async_trait]
8impl AuthApi for AuthService {
9 async fn check_health(&self) -> Result<(), KenAuthError> {
10 self.check_health().await
11 }
12
13 async fn change_password(
14 &self,
15 user_id: i64,
16 old_password_hash: &str,
17 new_password_hash: &str,
18 keep_session_id: &str,
19 client_ip: &str,
20 ) -> Result<proto::v1::ChangePasswordResponse, KenAuthError> {
21 self.change_password(
22 user_id,
23 old_password_hash,
24 new_password_hash,
25 keep_session_id,
26 client_ip,
27 )
28 .await
29 }
30
31 async fn get_mfa_status(
32 &self,
33 user_id: i64,
34 ) -> Result<proto::v1::GetMfaStatusResponse, KenAuthError> {
35 self.get_mfa_status(user_id).await
36 }
37
38 async fn configure_totp(
39 &self,
40 user_id: i64,
41 name: &str,
42 client_ip: &str,
43 ) -> Result<proto::v1::ConfigureTotpResponse, KenAuthError> {
44 self.configure_totp(user_id, name, client_ip).await
45 }
46
47 async fn enable_totp(
48 &self,
49 user_id: i64,
50 code: &str,
51 password_hash: &str,
52 client_ip: &str,
53 ) -> Result<proto::v1::EnableTotpResponse, KenAuthError> {
54 self.enable_totp(user_id, code, password_hash, client_ip)
55 .await
56 }
57
58 async fn disable_totp(
59 &self,
60 user_id: i64,
61 code: &str,
62 client_ip: &str,
63 ) -> Result<proto::v1::DisableTotpResponse, KenAuthError> {
64 self.disable_totp(user_id, code, client_ip).await
65 }
66
67 async fn regenerate_recovery_codes(
68 &self,
69 user_id: i64,
70 password_hash: &str,
71 client_ip: &str,
72 ) -> Result<proto::v1::RegenerateRecoveryCodesResponse, KenAuthError> {
73 self.regenerate_recovery_codes(user_id, password_hash, client_ip)
74 .await
75 }
76
77 async fn list_user_ssh_keys(
78 &self,
79 user_id: i64,
80 include_revoked: bool,
81 page: proto::common::v1::PageRequest,
82 ) -> Result<proto::v1::ListUserSshKeysResponse, KenAuthError> {
83 self.list_user_ssh_keys(user_id, include_revoked, page)
84 .await
85 }
86
87 async fn add_ssh_key(
88 &self,
89 user_id: i64,
90 name: &str,
91 public_key: &str,
92 description: &str,
93 client_ip: &str,
94 ) -> Result<proto::v1::AddSshKeyResponse, KenAuthError> {
95 self.add_ssh_key(user_id, name, public_key, description, client_ip)
96 .await
97 }
98
99 async fn revoke_ssh_key(
100 &self,
101 user_id: i64,
102 key_id: &str,
103 reason: &str,
104 bump_security_version: bool,
105 ) -> Result<proto::v1::RevokeSshKeyResponse, KenAuthError> {
106 self.revoke_ssh_key(user_id, key_id, reason, bump_security_version)
107 .await
108 }
109
110 async fn list_user_sessions(
111 &self,
112 user_id: i64,
113 include_revoked: bool,
114 include_expired: bool,
115 page: proto::common::v1::PageRequest,
116 ) -> Result<proto::v1::ListUserSessionsResponse, KenAuthError> {
117 self.list_user_sessions(user_id, include_revoked, include_expired, page)
118 .await
119 }
120
121 async fn revoke_user_sessions(
122 &self,
123 user_id: i64,
124 except_session_id: &str,
125 reason: &str,
126 bump_security_version: bool,
127 ) -> Result<proto::v1::RevokeUserSessionsResponse, KenAuthError> {
128 self.revoke_user_sessions(user_id, except_session_id, reason, bump_security_version)
129 .await
130 }
131
132 async fn get_session(
133 &self,
134 user_id: i64,
135 version: i32,
136 session_id: &str,
137 ) -> Result<proto::internal::v1::AuthorityAccess, KenAuthError> {
138 self.get_session(user_id, version, session_id).await
139 }
140
141 async fn delete_account(&self, user_id: i64, reason: &str) -> Result<(), KenAuthError> {
142 self.delete_account(user_id, reason).await
143 }
144
145 async fn cancel_account_deletion(&self, user_id: i64) -> Result<(), KenAuthError> {
146 self.cancel_account_deletion(user_id).await
147 }
148
149 async fn purge_account(&self, user_id: i64) -> Result<(), KenAuthError> {
150 self.purge_account(user_id).await
151 }
152
153 async fn batch_get_authority_users(
154 &self,
155 user_ids: Vec<i64>,
156 ) -> Result<Vec<proto::internal::v1::AuthorityUser>, KenAuthError> {
157 self.batch_get_authority_users(user_ids).await
158 }
159
160 async fn get_user_status(
161 &self,
162 user_id: i64,
163 ) -> Result<proto::common::v1::UserStatus, KenAuthError> {
164 self.get_user_status(user_id).await
165 }
166
167 async fn get_user_public_by_ref(
168 &self,
169 key: proto::common::v1::user_ref::Key,
170 ) -> Result<proto::common::v1::UserPublic, KenAuthError> {
171 self.get_user_public_by_ref(key).await
172 }
173
174 async fn get_authority_user(
175 &self,
176 user_id: i64,
177 ) -> Result<proto::internal::v1::AuthorityUser, KenAuthError> {
178 self.get_authority_user(user_id).await
179 }
180
181 async fn update_authority_user(
182 &self,
183 user_id: i64,
184 username: Option<String>,
185 displayname: Option<String>,
186 clear_displayname: bool,
187 public_show_email: Option<bool>,
188 version: i32,
189 ) -> Result<proto::internal::v1::AuthorityUser, KenAuthError> {
190 self.update_authority_user(
191 user_id,
192 username,
193 displayname,
194 clear_displayname,
195 public_show_email,
196 version,
197 )
198 .await
199 }
200
201 async fn batch_get_user_public(
202 &self,
203 user_ids: Vec<i64>,
204 ) -> Result<Vec<proto::common::v1::UserPublic>, KenAuthError> {
205 self.batch_get_user_public(user_ids).await
206 }
207
208 async fn get_user_public(
209 &self,
210 user_id: i64,
211 ) -> Result<proto::common::v1::UserPublic, KenAuthError> {
212 self.get_user_public(user_id).await
213 }
214
215 async fn revoke_session(
216 &self,
217 user_id: i64,
218 session_id: &str,
219 reason: &str,
220 ) -> Result<(), KenAuthError> {
221 self.revoke_session(user_id, session_id, reason).await
222 }
223
224 async fn lookup_ssh_key(
225 &self,
226 public_key: &str,
227 ) -> Result<Option<proto::common::v1::Principal>, KenAuthError> {
228 self.lookup_ssh_key(public_key).await
229 }
230
231 async fn ensure_bot_user(
232 &self,
233 username: &str,
234 displayname: &str,
235 system: bool,
236 ) -> Result<proto::common::v1::UserPublic, KenAuthError> {
237 self.ensure_bot_user(username, displayname, system).await
238 }
239
240 async fn ensure_bot_member(
241 &self,
242 organization_id: &str,
243 user_id: i64,
244 role_keys: Vec<String>,
245 role_ids: Vec<String>,
246 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
247 self.ensure_bot_member(organization_id, user_id, role_keys, role_ids)
248 .await
249 }
250
251 async fn get_organization(
252 &self,
253 actor_user_id: i64,
254 organization_id: &str,
255 slug: &str,
256 ) -> Result<proto::v1::GetOrganizationResponse, KenAuthError> {
257 self.get_organization(actor_user_id, organization_id, slug)
258 .await
259 }
260
261 async fn get_member(
262 &self,
263 actor_user_id: i64,
264 organization_id: &str,
265 user_id: i64,
266 ) -> Result<Option<proto::v1::OrganizationMemberInfo>, KenAuthError> {
267 self.get_member(actor_user_id, organization_id, user_id)
268 .await
269 }
270
271 async fn list_members(
272 &self,
273 actor_user_id: i64,
274 organization_id: &str,
275 include_inactive: bool,
276 include_banned: bool,
277 page: proto::common::v1::PageRequest,
278 ) -> Result<proto::v1::ListMembersResponse, KenAuthError> {
279 self.list_members(
280 actor_user_id,
281 organization_id,
282 include_inactive,
283 include_banned,
284 page,
285 )
286 .await
287 }
288
289 async fn list_organizations(
290 &self,
291 actor_user_id: i64,
292 user_id: i64,
293 include_archived: bool,
294 page: proto::common::v1::PageRequest,
295 ) -> Result<proto::v1::ListOrganizationsResponse, KenAuthError> {
296 self.list_organizations(actor_user_id, user_id, include_archived, page)
297 .await
298 }
299
300 async fn list_public_organizations(
301 &self,
302 query: &str,
303 page: proto::common::v1::PageRequest,
304 ) -> Result<proto::v1::ListPublicOrganizationsResponse, KenAuthError> {
305 self.list_public_organizations(query, page).await
306 }
307
308 async fn check_permissions(
309 &self,
310 actor_user_id: i64,
311 organization_id: &str,
312 user_id: i64,
313 permissions: Vec<String>,
314 ) -> Result<proto::v1::CheckPermissionsResponse, KenAuthError> {
315 self.check_permissions(actor_user_id, organization_id, user_id, permissions)
316 .await
317 }
318
319 async fn create_organization(
320 &self,
321 actor_user_id: i64,
322 slug: &str,
323 name: &str,
324 description: &str,
325 avatar_url: &str,
326 join_policy: Option<i32>,
327 allow_join: Option<bool>,
328 require_email_verified: Option<bool>,
329 require_mfa: Option<bool>,
330 welcome_message: Option<String>,
331 member_must_enable_2fa: Option<bool>,
332 allow_invite: Option<bool>,
333 allow_join_level: Option<i32>,
334 ) -> Result<proto::v1::CreateOrganizationResponse, KenAuthError> {
335 self.create_organization(
336 actor_user_id,
337 slug,
338 name,
339 description,
340 avatar_url,
341 join_policy,
342 allow_join,
343 require_email_verified,
344 require_mfa,
345 welcome_message,
346 member_must_enable_2fa,
347 allow_invite,
348 allow_join_level,
349 )
350 .await
351 }
352
353 async fn update_organization(
354 &self,
355 actor_user_id: i64,
356 organization_id: &str,
357 name: Option<String>,
358 description: Option<String>,
359 avatar_url: Option<String>,
360 expected_version: i32,
361 join_policy: Option<i32>,
362 allow_join: Option<bool>,
363 require_email_verified: Option<bool>,
364 require_mfa: Option<bool>,
365 welcome_message: Option<String>,
366 member_must_enable_2fa: Option<bool>,
367 allow_invite: Option<bool>,
368 allow_join_level: Option<i32>,
369 ) -> Result<proto::v1::OrganizationInfo, KenAuthError> {
370 self.update_organization(
371 actor_user_id,
372 organization_id,
373 name,
374 description,
375 avatar_url,
376 expected_version,
377 join_policy,
378 allow_join,
379 require_email_verified,
380 require_mfa,
381 welcome_message,
382 member_must_enable_2fa,
383 allow_invite,
384 allow_join_level,
385 )
386 .await
387 }
388
389 async fn archive_organization(
390 &self,
391 actor_user_id: i64,
392 organization_id: &str,
393 reason: &str,
394 ) -> Result<(), KenAuthError> {
395 self.archive_organization(actor_user_id, organization_id, reason)
396 .await
397 }
398
399 async fn restore_organization(
400 &self,
401 actor_user_id: i64,
402 organization_id: &str,
403 ) -> Result<(), KenAuthError> {
404 self.restore_organization(actor_user_id, organization_id)
405 .await
406 }
407
408 async fn delete_organization(
409 &self,
410 actor_user_id: i64,
411 organization_id: &str,
412 force: bool,
413 reason: &str,
414 ) -> Result<(), KenAuthError> {
415 self.delete_organization(actor_user_id, organization_id, force, reason)
416 .await
417 }
418
419 async fn add_member(
420 &self,
421 actor_user_id: i64,
422 organization_id: &str,
423 user_id: i64,
424 role: i32,
425 role_keys: Vec<String>,
426 ) -> Result<(proto::v1::OrganizationMemberInfo, bool), KenAuthError> {
427 self.add_member(actor_user_id, organization_id, user_id, role, role_keys)
428 .await
429 }
430
431 async fn remove_member(
432 &self,
433 actor_user_id: i64,
434 organization_id: &str,
435 user_id: i64,
436 reason: &str,
437 ) -> Result<bool, KenAuthError> {
438 self.remove_member(actor_user_id, organization_id, user_id, reason)
439 .await
440 }
441
442 async fn leave_organization(
443 &self,
444 actor_user_id: i64,
445 organization_id: &str,
446 ) -> Result<bool, KenAuthError> {
447 self.leave_organization(actor_user_id, organization_id)
448 .await
449 }
450
451 async fn update_member(
452 &self,
453 actor_user_id: i64,
454 organization_id: &str,
455 user_id: i64,
456 alias_name: Option<Option<String>>,
457 alias_avatar_url: Option<Option<String>>,
458 message_notifications: Option<String>,
459 muted: Option<bool>,
460 mute_until: Option<Option<prost_types::Timestamp>>,
461 mobile_push: Option<bool>,
462 suppress_everyone: Option<bool>,
463 suppress_roles: Option<bool>,
464 hide_muted_channels: Option<bool>,
465 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
466 self.update_member(
467 actor_user_id,
468 organization_id,
469 user_id,
470 alias_name,
471 alias_avatar_url,
472 message_notifications,
473 muted,
474 mute_until,
475 mobile_push,
476 suppress_everyone,
477 suppress_roles,
478 hide_muted_channels,
479 )
480 .await
481 }
482
483 async fn ban_member(
484 &self,
485 actor_user_id: i64,
486 organization_id: &str,
487 user_id: i64,
488 reason: &str,
489 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
490 self.ban_member(actor_user_id, organization_id, user_id, reason)
491 .await
492 }
493
494 async fn unban_member(
495 &self,
496 actor_user_id: i64,
497 organization_id: &str,
498 user_id: i64,
499 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
500 self.unban_member(actor_user_id, organization_id, user_id)
501 .await
502 }
503
504 async fn update_ban(
505 &self,
506 actor_user_id: i64,
507 organization_id: &str,
508 user_id: i64,
509 reason: Option<Option<String>>,
510 expires_at: Option<Option<prost_types::Timestamp>>,
511 allow_speak: Option<bool>,
512 allow_push_repo: Option<bool>,
513 allow_comment_any: Option<bool>,
514 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
515 self.update_ban(
516 actor_user_id,
517 organization_id,
518 user_id,
519 reason,
520 expires_at,
521 allow_speak,
522 allow_push_repo,
523 allow_comment_any,
524 )
525 .await
526 }
527
528 async fn create_role(
529 &self,
530 actor_user_id: i64,
531 organization_id: &str,
532 key: &str,
533 name: &str,
534 description: &str,
535 rank: i32,
536 self_assignable: bool,
537 permissions: Vec<String>,
538 ) -> Result<proto::v1::OrganizationRoleInfo, KenAuthError> {
539 self.create_role(
540 actor_user_id,
541 organization_id,
542 key,
543 name,
544 description,
545 rank,
546 self_assignable,
547 permissions,
548 )
549 .await
550 }
551
552 async fn update_role(
553 &self,
554 actor_user_id: i64,
555 organization_id: &str,
556 role_id: &str,
557 name: Option<String>,
558 description: Option<String>,
559 rank: Option<i32>,
560 self_assignable: Option<bool>,
561 expected_version: i32,
562 ) -> Result<proto::v1::OrganizationRoleInfo, KenAuthError> {
563 self.update_role(
564 actor_user_id,
565 organization_id,
566 role_id,
567 name,
568 description,
569 rank,
570 self_assignable,
571 expected_version,
572 )
573 .await
574 }
575
576 async fn set_role_permissions(
577 &self,
578 actor_user_id: i64,
579 organization_id: &str,
580 role_id: &str,
581 permissions: Vec<String>,
582 ) -> Result<proto::v1::OrganizationRoleInfo, KenAuthError> {
583 self.set_role_permissions(actor_user_id, organization_id, role_id, permissions)
584 .await
585 }
586
587 async fn delete_role(
588 &self,
589 actor_user_id: i64,
590 organization_id: &str,
591 role_id: &str,
592 ) -> Result<bool, KenAuthError> {
593 self.delete_role(actor_user_id, organization_id, role_id)
594 .await
595 }
596
597 async fn assign_role(
598 &self,
599 actor_user_id: i64,
600 organization_id: &str,
601 user_id: i64,
602 role_id: &str,
603 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
604 self.assign_role(actor_user_id, organization_id, user_id, role_id)
605 .await
606 }
607
608 async fn unassign_role(
609 &self,
610 actor_user_id: i64,
611 organization_id: &str,
612 user_id: i64,
613 role_id: &str,
614 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
615 self.unassign_role(actor_user_id, organization_id, user_id, role_id)
616 .await
617 }
618
619 async fn create_invite(
620 &self,
621 actor_user_id: i64,
622 organization_id: &str,
623 method: i32,
624 max_uses: i32,
625 expires_at: Option<prost_types::Timestamp>,
626 role_ids: Vec<String>,
627 target_user_id: i64,
628 email: &str,
629 ) -> Result<(proto::v1::OrganizationInviteInfo, String), KenAuthError> {
630 self.create_invite(
631 actor_user_id,
632 organization_id,
633 method,
634 max_uses,
635 expires_at,
636 role_ids,
637 target_user_id,
638 email,
639 )
640 .await
641 }
642
643 async fn revoke_invite(
644 &self,
645 actor_user_id: i64,
646 organization_id: &str,
647 invite_id: &str,
648 ) -> Result<bool, KenAuthError> {
649 self.revoke_invite(actor_user_id, organization_id, invite_id)
650 .await
651 }
652
653 async fn redeem_invite(
654 &self,
655 actor_user_id: i64,
656 token: &str,
657 ) -> Result<proto::v1::RedeemInviteResponse, KenAuthError> {
658 self.redeem_invite(actor_user_id, token).await
659 }
660
661 async fn create_join_rule(
662 &self,
663 actor_user_id: i64,
664 organization_id: &str,
665 role_id: &str,
666 trigger: &str,
667 matcher: &str,
668 enabled: bool,
669 ) -> Result<proto::v1::JoinRuleInfo, KenAuthError> {
670 self.create_join_rule(
671 actor_user_id,
672 organization_id,
673 role_id,
674 trigger,
675 matcher,
676 enabled,
677 )
678 .await
679 }
680
681 async fn update_join_rule(
682 &self,
683 actor_user_id: i64,
684 organization_id: &str,
685 rule_id: &str,
686 role_id: Option<String>,
687 trigger: Option<String>,
688 matcher: Option<String>,
689 enabled: Option<bool>,
690 ) -> Result<proto::v1::JoinRuleInfo, KenAuthError> {
691 self.update_join_rule(
692 actor_user_id,
693 organization_id,
694 rule_id,
695 role_id,
696 trigger,
697 matcher,
698 enabled,
699 )
700 .await
701 }
702
703 async fn delete_join_rule(
704 &self,
705 actor_user_id: i64,
706 organization_id: &str,
707 rule_id: &str,
708 ) -> Result<bool, KenAuthError> {
709 self.delete_join_rule(actor_user_id, organization_id, rule_id)
710 .await
711 }
712
713 async fn create_join_request(
714 &self,
715 actor_user_id: i64,
716 organization_id: &str,
717 note: &str,
718 ) -> Result<proto::v1::JoinRequestInfo, KenAuthError> {
719 self.create_join_request(actor_user_id, organization_id, note)
720 .await
721 }
722
723 async fn withdraw_join_request(
724 &self,
725 actor_user_id: i64,
726 organization_id: &str,
727 request_id: &str,
728 ) -> Result<proto::v1::JoinRequestInfo, KenAuthError> {
729 self.withdraw_join_request(actor_user_id, organization_id, request_id)
730 .await
731 }
732
733 async fn approve_join_request(
734 &self,
735 actor_user_id: i64,
736 organization_id: &str,
737 request_id: &str,
738 ) -> Result<
739 (
740 proto::v1::JoinRequestInfo,
741 Option<proto::v1::OrganizationMemberInfo>,
742 ),
743 KenAuthError,
744 > {
745 self.approve_join_request(actor_user_id, organization_id, request_id)
746 .await
747 }
748
749 async fn reject_join_request(
750 &self,
751 actor_user_id: i64,
752 organization_id: &str,
753 request_id: &str,
754 note: &str,
755 ) -> Result<proto::v1::JoinRequestInfo, KenAuthError> {
756 self.reject_join_request(actor_user_id, organization_id, request_id, note)
757 .await
758 }
759}
Modified lib/auth/src/errors.rs View lib/auth/src/errors.rs
@@ -1,3 +1,5 @@
1pub use export::KenAuthError;
2
13use actix_web::http::StatusCode;
24use actix_web::http::header::{HeaderValue, RETRY_AFTER};
35use actix_web::{HttpResponse, ResponseError};
@@ -362,3 +364,128 @@
362364 pub code: String,
363365 pub message: String,
364366}
367use std::fmt;
368
369#[derive(Debug)]
370pub enum AuthBusinessError {
371 BadRequest(String),
372 InvalidCredentials,
373 Unauthorized,
374 Forbidden,
375 NotFound,
376 Conflict(String),
377 RateLimited,
378 Internal(String),
379 TwoFactorExpired,
380 TwoFactorCodeInvalid,
381 RecoveryCodeInvalid,
382 VerificationCodeExpired,
383 VerificationCodeInvalid,
384 ResetTokenInvalid,
385}
386
387impl fmt::Display for AuthBusinessError {
388 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389 match self {
390 Self::BadRequest(message) => write!(f, "{message}"),
391 Self::InvalidCredentials => write!(f, "invalid credentials"),
392 Self::Unauthorized => write!(f, "unauthorized"),
393 Self::Forbidden => write!(f, "forbidden"),
394 Self::NotFound => write!(f, "not found"),
395 Self::Conflict(message) => write!(f, "{message}"),
396 Self::RateLimited => write!(f, "rate limit exceeded"),
397 Self::Internal(message) => write!(f, "{message}"),
398 Self::TwoFactorExpired => write!(f, "two-factor challenge expired"),
399 Self::TwoFactorCodeInvalid => write!(f, "invalid two-factor code"),
400 Self::RecoveryCodeInvalid => write!(f, "invalid or used recovery code"),
401 Self::VerificationCodeExpired => write!(f, "verification code expired"),
402 Self::VerificationCodeInvalid => write!(f, "invalid verification code"),
403 Self::ResetTokenInvalid => write!(f, "invalid or expired reset token"),
404 }
405 }
406}
407
408impl std::error::Error for AuthBusinessError {}
409
410impl From<deletion::DeletionError> for AuthBusinessError {
411 fn from(error: deletion::DeletionError) -> Self {
412 use deletion::DeletionError;
413 match error {
414 DeletionError::Database(error) => Self::from(error),
415 DeletionError::Conflict(message) => Self::Conflict(message),
416 DeletionError::NotFound(message) => {
417 tracing::debug!(%message, "deletion target not found");
418 Self::NotFound
419 }
420 DeletionError::KenAuth(message)
421 | DeletionError::KenShell(message)
422 | DeletionError::Config(message) => Self::Internal(message),
423 }
424 }
425}
426
427export::db_error_conversions!(AuthBusinessError);
428export::redis_error_conversion!(AuthBusinessError);
429export::export_error_conversion!(AuthBusinessError);
430export::json_payload_conversion!(AuthBusinessError);
431
432export::api_error! {
433 log: "auth",
434 pub enum ApiError {
435 BadRequest(400, "request failed validation"),
436 InvalidCredentials(400, "credentials rejected"),
437 Unauthorized(401, "authentication required"),
438 Forbidden(403, "operation not allowed"),
439 NotFound(404, "resource not found"),
440 Conflict(409, "resource conflict"),
441 RateLimited(429, "rate limit exceeded"),
442 Internal(500, "internal server error"),
443 }
444}
445
446impl From<AuthBusinessError> for ApiError {
447 fn from(error: AuthBusinessError) -> Self {
448 match error {
449 AuthBusinessError::BadRequest(msg) => Self::bad_request(msg),
450 AuthBusinessError::InvalidCredentials => {
451 Self::build(400, Self::payload(400, "invalid credentials"))
452 }
453 AuthBusinessError::Unauthorized => Self::unauthorized(),
454 AuthBusinessError::Forbidden => Self::forbidden(),
455 AuthBusinessError::NotFound => Self::not_found("auth resource not found"),
456 AuthBusinessError::Conflict(msg) => Self::conflict(msg),
457 AuthBusinessError::RateLimited => Self::rate_limited_exceeded(),
458 AuthBusinessError::TwoFactorExpired => Self::build(
459 400,
460 Self::payload(400, "two-factor challenge expired")
461 .with_error_code("two_factor_expired"),
462 ),
463 AuthBusinessError::TwoFactorCodeInvalid => Self::build(
464 400,
465 Self::payload(400, "invalid two-factor code")
466 .with_error_code("two_factor_code_invalid"),
467 ),
468 AuthBusinessError::RecoveryCodeInvalid => Self::build(
469 400,
470 Self::payload(400, "invalid or used recovery code")
471 .with_error_code("recovery_code_invalid"),
472 ),
473 AuthBusinessError::VerificationCodeExpired => Self::build(
474 400,
475 Self::payload(400, "verification code expired")
476 .with_error_code("verification_code_expired"),
477 ),
478 AuthBusinessError::VerificationCodeInvalid => Self::build(
479 400,
480 Self::payload(400, "invalid verification code")
481 .with_error_code("verification_code_invalid"),
482 ),
483 AuthBusinessError::ResetTokenInvalid => Self::build(
484 400,
485 Self::payload(400, "invalid or expired reset token")
486 .with_error_code("reset_token_invalid"),
487 ),
488 AuthBusinessError::Internal(message) => Self::internal(message),
489 }
490 }
491}
Modified lib/auth/src/lib.rs View lib/auth/src/lib.rs
@@ -2,36 +2,21 @@
22
33#![recursion_limit = "512"]
44
5pub mod account_lifecycle;
6pub mod api_error;
5pub mod api;
76pub mod assets;
8pub mod auth_api_error;
9pub mod auth_api_impl;
10pub mod auth_session;
11pub mod auth_svc;
12pub mod authority;
137pub mod errors;
148pub mod handlers;
15pub mod internal;
16pub mod me;
179pub mod migrate;
1810pub mod openapi;
1911pub mod openapi_account;
20pub mod organization;
21pub mod pow;
12pub mod pages;
2213pub mod routes;
2314pub mod routes_account;
24pub mod service;
2515pub mod services;
2616pub mod share;
27pub mod ssh_key;
28pub mod templates;
2917
30pub use api_error::{ApiError, AuthBusinessError};
31pub use auth_api_error::KenAuthError;
32pub use auth_svc::AuthSvc;
33pub use errors::{AuthError, AuthResult};
34pub use service::AuthService;
18pub use errors::{ApiError, AuthBusinessError, AuthError, AuthResult, KenAuthError};
19pub use services::service::AuthService;
3520
3621pub use cache;
3722pub use config;
Modified lib/auth/src/openapi.rs View lib/auth/src/openapi.rs
@@ -8,89 +8,89 @@
88 version = env!("CARGO_PKG_VERSION")
99 ),
1010 paths(
11 crate::handlers::identity::challenge::handler::challenge,
12 crate::handlers::identity::login::handler::login,
13 crate::handlers::identity::login::handler::login_code,
14 crate::handlers::identity::login::handler::login_code_verify,
15 crate::handlers::identity::login::handler::login_verify,
16 crate::handlers::identity::logout::handler::logout,
17 crate::handlers::identity::me::handler::me,
18 crate::handlers::identity::register::handler::register_code,
19 crate::handlers::identity::register::handler::register,
20 crate::handlers::identity::password_reset::handler::password_reset_request,
21 crate::handlers::identity::password_reset::handler::password_reset,
22 crate::handlers::identity::ssh_key::handler::ssh_key_list,
23 crate::handlers::identity::ssh_key::handler::ssh_key_add,
24 crate::handlers::identity::ssh_key::handler::ssh_key_delete,
25 crate::handlers::identity::organization::handler::create_organization,
26 crate::handlers::identity::organization::handler::list_organizations,
27 crate::handlers::identity::organization::handler::get_organization,
28 crate::handlers::identity::organization::handler::update_organization,
29 crate::handlers::identity::organization::handler::archive_organization,
30 crate::handlers::identity::organization::handler::restore_organization,
31 crate::handlers::identity::organization::handler::delete_organization,
32 crate::handlers::identity::organization::handler::transfer_ownership,
33 crate::handlers::identity::organization::handler::leave_organization,
34 crate::handlers::identity::organization::handler::list_members,
35 crate::handlers::identity::organization::handler::get_member,
36 crate::handlers::identity::organization::handler::update_member,
37 crate::handlers::identity::organization::handler::update_self_member,
38 crate::handlers::identity::organization::handler::add_member,
39 crate::handlers::identity::organization::handler::remove_member,
40 crate::handlers::identity::organization::handler::set_member_role,
41 crate::handlers::identity::organization::handler::ban_member,
42 crate::handlers::identity::organization::handler::unban_member,
43 crate::handlers::identity::organization::handler::set_member_permissions,
44 crate::handlers::identity::organization::handler::list_bans,
45 crate::handlers::identity::organization::handler::get_ban,
46 crate::handlers::identity::organization::handler::update_ban,
47 crate::handlers::identity::organization::handler::unban_by_user,
48 crate::handlers::identity::organization::handler::list_roles,
49 crate::handlers::identity::organization::handler::get_role,
50 crate::handlers::identity::organization::handler::create_role,
51 crate::handlers::identity::organization::handler::update_role,
52 crate::handlers::identity::organization::handler::delete_role,
53 crate::handlers::identity::organization::handler::set_role_permissions,
54 crate::handlers::identity::organization::handler::assign_role,
55 crate::handlers::identity::organization::handler::unassign_role,
56 crate::handlers::identity::organization::handler::join_role,
57 crate::handlers::identity::organization::handler::leave_role,
58 crate::handlers::identity::organization::handler::check_permission,
59 crate::handlers::identity::organization::handler::create_invite,
60 crate::handlers::identity::organization::handler::list_invites,
61 crate::handlers::identity::organization::handler::revoke_invite,
62 crate::handlers::identity::organization::handler::redeem_invite,
11 crate::handlers::identity::challenge::challenge,
12 crate::handlers::identity::login::login,
13 crate::handlers::identity::login::login_code,
14 crate::handlers::identity::login::login_code_verify,
15 crate::handlers::identity::login::login_verify,
16 crate::handlers::identity::logout::logout,
17 crate::handlers::identity::me::me,
18 crate::handlers::identity::register::register_code,
19 crate::handlers::identity::register::register,
20 crate::handlers::identity::password_reset::password_reset_request,
21 crate::handlers::identity::password_reset::password_reset,
22 crate::handlers::identity::ssh_key::ssh_key_list,
23 crate::handlers::identity::ssh_key::ssh_key_add,
24 crate::handlers::identity::ssh_key::ssh_key_delete,
25 crate::handlers::identity::organization::create_organization,
26 crate::handlers::identity::organization::list_organizations,
27 crate::handlers::identity::organization::get_organization,
28 crate::handlers::identity::organization::update_organization,
29 crate::handlers::identity::organization::archive_organization,
30 crate::handlers::identity::organization::restore_organization,
31 crate::handlers::identity::organization::delete_organization,
32 crate::handlers::identity::organization::transfer_ownership,
33 crate::handlers::identity::organization::leave_organization,
34 crate::handlers::identity::organization::list_members,
35 crate::handlers::identity::organization::get_member,
36 crate::handlers::identity::organization::update_member,
37 crate::handlers::identity::organization::update_self_member,
38 crate::handlers::identity::organization::add_member,
39 crate::handlers::identity::organization::remove_member,
40 crate::handlers::identity::organization::set_member_role,
41 crate::handlers::identity::organization::ban_member,
42 crate::handlers::identity::organization::unban_member,
43 crate::handlers::identity::organization::set_member_permissions,
44 crate::handlers::identity::organization::list_bans,
45 crate::handlers::identity::organization::get_ban,
46 crate::handlers::identity::organization::update_ban,
47 crate::handlers::identity::organization::unban_by_user,
48 crate::handlers::identity::organization::list_roles,
49 crate::handlers::identity::organization::get_role,
50 crate::handlers::identity::organization::create_role,
51 crate::handlers::identity::organization::update_role,
52 crate::handlers::identity::organization::delete_role,
53 crate::handlers::identity::organization::set_role_permissions,
54 crate::handlers::identity::organization::assign_role,
55 crate::handlers::identity::organization::unassign_role,
56 crate::handlers::identity::organization::join_role,
57 crate::handlers::identity::organization::leave_role,
58 crate::handlers::identity::organization::check_permission,
59 crate::handlers::identity::organization::create_invite,
60 crate::handlers::identity::organization::list_invites,
61 crate::handlers::identity::organization::revoke_invite,
62 crate::handlers::identity::organization::redeem_invite,
6363 ),
6464 components(schemas(
65 crate::organization::types::OrganizationSummary,
66 crate::organization::types::MemberSummary,
67 crate::organization::types::RoleSummary,
68 crate::organization::types::InviteSummary,
69 crate::organization::types::PermissionGrant,
70 crate::organization::types::PermissionCheck,
71 crate::organization::types::CreateOrganizationBody,
72 crate::organization::types::UpdateOrganizationBody,
73 crate::organization::types::AddMemberBody,
74 crate::organization::types::SetMemberRoleBody,
75 crate::organization::types::BanMemberBody,
76 crate::organization::types::CheckPermissionBody,
77 crate::organization::types::CreateRoleBody,
78 crate::organization::types::SetRolePermissionsBody,
79 crate::handlers::identity::organization::handler::OrgPath,
80 crate::handlers::identity::organization::handler::OrgMemberPath,
81 crate::handlers::identity::organization::handler::OrgRolePath,
82 crate::handlers::identity::organization::handler::OrgInvitePath,
83 crate::handlers::identity::organization::handler::OrgListQuery,
84 crate::handlers::identity::organization::handler::MemberListQuery,
85 crate::handlers::identity::organization::handler::UpdateRoleBody,
86 crate::handlers::identity::organization::handler::TransferBody,
87 crate::handlers::identity::organization::handler::ReasonBody,
88 crate::handlers::identity::organization::handler::DeleteOrgQuery,
89 crate::handlers::identity::organization::handler::CreateInviteBody,
90 crate::handlers::identity::organization::handler::RedeemInviteBody,
91 crate::handlers::identity::organization::handler::CreateInviteResponse,
92 crate::handlers::identity::organization::handler::RedeemInviteResponse,
93 crate::handlers::identity::organization::handler::AssignRoleBody,
65 crate::services::organization::types::OrganizationSummary,
66 crate::services::organization::types::MemberSummary,
67 crate::services::organization::types::RoleSummary,
68 crate::services::organization::types::InviteSummary,
69 crate::services::organization::types::PermissionGrant,
70 crate::services::organization::types::PermissionCheck,
71 crate::services::organization::types::CreateOrganizationBody,
72 crate::services::organization::types::UpdateOrganizationBody,
73 crate::services::organization::types::AddMemberBody,
74 crate::services::organization::types::SetMemberRoleBody,
75 crate::services::organization::types::BanMemberBody,
76 crate::services::organization::types::CheckPermissionBody,
77 crate::services::organization::types::CreateRoleBody,
78 crate::services::organization::types::SetRolePermissionsBody,
79 crate::handlers::identity::organization::OrgPath,
80 crate::handlers::identity::organization::OrgMemberPath,
81 crate::handlers::identity::organization::OrgRolePath,
82 crate::handlers::identity::organization::OrgInvitePath,
83 crate::handlers::identity::organization::OrgListQuery,
84 crate::handlers::identity::organization::MemberListQuery,
85 crate::handlers::identity::organization::UpdateRoleBody,
86 crate::handlers::identity::organization::TransferBody,
87 crate::handlers::identity::organization::ReasonBody,
88 crate::handlers::identity::organization::DeleteOrgQuery,
89 crate::handlers::identity::organization::CreateInviteBody,
90 crate::handlers::identity::organization::RedeemInviteBody,
91 crate::handlers::identity::organization::CreateInviteResponse,
92 crate::handlers::identity::organization::RedeemInviteResponse,
93 crate::handlers::identity::organization::AssignRoleBody,
9494 ))
9595)]
9696pub struct ApiDoc;
Modified lib/auth/src/routes.rs View lib/auth/src/routes.rs
@@ -7,210 +7,207 @@
77pub fn configure_api(cfg: &mut web::ServiceConfig) {
88 cfg.route(
99 "/auth/challenge",
10 web::post().to(crate::handlers::identity::challenge::handler::challenge),
10 web::post().to(crate::handlers::identity::challenge::challenge),
1111 );
1212 cfg.route(
1313 "/auth/login",
14 web::post().to(crate::handlers::identity::login::handler::login),
14 web::post().to(crate::handlers::identity::login::login),
1515 );
1616 cfg.route(
1717 "/auth/login/code",
18 web::post().to(crate::handlers::identity::login::handler::login_code),
18 web::post().to(crate::handlers::identity::login::login_code),
1919 );
2020 cfg.route(
2121 "/auth/login/code/verify",
22 web::post().to(crate::handlers::identity::login::handler::login_code_verify),
22 web::post().to(crate::handlers::identity::login::login_code_verify),
2323 );
2424 cfg.route(
2525 "/auth/login/verify",
26 web::post().to(crate::handlers::identity::login::handler::login_verify),
26 web::post().to(crate::handlers::identity::login::login_verify),
2727 );
2828 cfg.route(
2929 "/auth/logout",
30 web::post().to(crate::handlers::identity::logout::handler::logout),
31 );
32 cfg.route(
33 "/auth/me",
34 web::get().to(crate::handlers::identity::me::handler::me),
30 web::post().to(crate::handlers::identity::logout::logout),
3531 );
32 cfg.route("/auth/me", web::get().to(crate::handlers::identity::me::me));
3633 cfg.route(
3734 "/auth/register/code",
38 web::post().to(crate::handlers::identity::register::handler::register_code),
35 web::post().to(crate::handlers::identity::register::register_code),
3936 );
4037 cfg.route(
4138 "/auth/register",
42 web::post().to(crate::handlers::identity::register::handler::register),
39 web::post().to(crate::handlers::identity::register::register),
4340 );
4441 cfg.route(
4542 "/auth/password/reset",
46 web::post().to(crate::handlers::identity::password_reset::handler::password_reset_request),
43 web::post().to(crate::handlers::identity::password_reset::password_reset_request),
4744 );
4845 cfg.route(
4946 "/auth/password/reset/confirm",
50 web::post().to(crate::handlers::identity::password_reset::handler::password_reset),
47 web::post().to(crate::handlers::identity::password_reset::password_reset),
5148 );
5249 cfg.route(
5350 "/auth/ssh-keys",
54 web::get().to(crate::handlers::identity::ssh_key::handler::ssh_key_list),
51 web::get().to(crate::handlers::identity::ssh_key::ssh_key_list),
5552 );
5653 cfg.route(
5754 "/auth/ssh-keys",
58 web::post().to(crate::handlers::identity::ssh_key::handler::ssh_key_add),
55 web::post().to(crate::handlers::identity::ssh_key::ssh_key_add),
5956 );
6057 cfg.route(
6158 "/auth/ssh-keys/{key_id}",
62 web::delete().to(crate::handlers::identity::ssh_key::handler::ssh_key_delete),
59 web::delete().to(crate::handlers::identity::ssh_key::ssh_key_delete),
6360 );
6461 cfg.route(
6562 "/organizations",
66 web::post().to(crate::handlers::identity::organization::handler::create_organization),
63 web::post().to(crate::handlers::identity::organization::create_organization),
6764 );
6865 cfg.route(
6966 "/organizations",
70 web::get().to(crate::handlers::identity::organization::handler::list_organizations),
67 web::get().to(crate::handlers::identity::organization::list_organizations),
7168 );
7269 cfg.route(
7370 "/organizations/invites/redeem",
74 web::post().to(crate::handlers::identity::organization::handler::redeem_invite),
71 web::post().to(crate::handlers::identity::organization::redeem_invite),
7572 );
7673 cfg.route(
7774 "/organizations/{organization_id}",
78 web::get().to(crate::handlers::identity::organization::handler::get_organization),
75 web::get().to(crate::handlers::identity::organization::get_organization),
7976 );
8077 cfg.route(
8178 "/organizations/{organization_id}",
82 web::patch().to(crate::handlers::identity::organization::handler::update_organization),
79 web::patch().to(crate::handlers::identity::organization::update_organization),
8380 );
8481 cfg.route(
8582 "/organizations/{organization_id}",
86 web::delete().to(crate::handlers::identity::organization::handler::delete_organization),
83 web::delete().to(crate::handlers::identity::organization::delete_organization),
8784 );
8885 cfg.route(
8986 "/organizations/{organization_id}/archive",
90 web::post().to(crate::handlers::identity::organization::handler::archive_organization),
87 web::post().to(crate::handlers::identity::organization::archive_organization),
9188 );
9289 cfg.route(
9390 "/organizations/{organization_id}/restore",
94 web::post().to(crate::handlers::identity::organization::handler::restore_organization),
91 web::post().to(crate::handlers::identity::organization::restore_organization),
9592 );
9693 cfg.route(
9794 "/organizations/{organization_id}/transfer",
98 web::post().to(crate::handlers::identity::organization::handler::transfer_ownership),
95 web::post().to(crate::handlers::identity::organization::transfer_ownership),
9996 );
10097 cfg.route(
10198 "/organizations/{organization_id}/leave",
102 web::post().to(crate::handlers::identity::organization::handler::leave_organization),
99 web::post().to(crate::handlers::identity::organization::leave_organization),
103100 );
104101 cfg.route(
105102 "/organizations/{organization_id}/members",
106 web::get().to(crate::handlers::identity::organization::handler::list_members),
103 web::get().to(crate::handlers::identity::organization::list_members),
107104 );
108105 cfg.route(
109106 "/organizations/{organization_id}/members",
110 web::post().to(crate::handlers::identity::organization::handler::add_member),
107 web::post().to(crate::handlers::identity::organization::add_member),
111108 );
112109 cfg.route(
113110 "/organizations/{organization_id}/members/@me",
114 web::patch().to(crate::handlers::identity::organization::handler::update_self_member),
111 web::patch().to(crate::handlers::identity::organization::update_self_member),
115112 );
116113 cfg.route(
117114 "/organizations/{organization_id}/members/{member_id}",
118 web::get().to(crate::handlers::identity::organization::handler::get_member),
115 web::get().to(crate::handlers::identity::organization::get_member),
119116 );
120117 cfg.route(
121118 "/organizations/{organization_id}/members/{member_id}",
122 web::patch().to(crate::handlers::identity::organization::handler::update_member),
119 web::patch().to(crate::handlers::identity::organization::update_member),
123120 );
124121 cfg.route(
125122 "/organizations/{organization_id}/members/{member_id}",
126 web::delete().to(crate::handlers::identity::organization::handler::remove_member),
123 web::delete().to(crate::handlers::identity::organization::remove_member),
127124 );
128125 cfg.route(
129126 "/organizations/{organization_id}/members/{member_id}/role",
130 web::patch().to(crate::handlers::identity::organization::handler::set_member_role),
127 web::patch().to(crate::handlers::identity::organization::set_member_role),
131128 );
132129 cfg.route(
133130 "/organizations/{organization_id}/members/{member_id}/ban",
134 web::post().to(crate::handlers::identity::organization::handler::ban_member),
131 web::post().to(crate::handlers::identity::organization::ban_member),
135132 );
136133 cfg.route(
137134 "/organizations/{organization_id}/members/{member_id}/unban",
138 web::post().to(crate::handlers::identity::organization::handler::unban_member),
135 web::post().to(crate::handlers::identity::organization::unban_member),
139136 );
140137 cfg.route(
141138 "/organizations/{organization_id}/members/{member_id}/permissions",
142 web::put().to(crate::handlers::identity::organization::handler::set_member_permissions),
139 web::put().to(crate::handlers::identity::organization::set_member_permissions),
143140 );
144141 cfg.route(
145142 "/organizations/{organization_id}/bans",
146 web::get().to(crate::handlers::identity::organization::handler::list_bans),
143 web::get().to(crate::handlers::identity::organization::list_bans),
147144 );
148145 cfg.route(
149146 "/organizations/{organization_id}/bans/{user_id}",
150 web::get().to(crate::handlers::identity::organization::handler::get_ban),
147 web::get().to(crate::handlers::identity::organization::get_ban),
151148 );
152149 cfg.route(
153150 "/organizations/{organization_id}/bans/{user_id}",
154 web::patch().to(crate::handlers::identity::organization::handler::update_ban),
151 web::patch().to(crate::handlers::identity::organization::update_ban),
155152 );
156153 cfg.route(
157154 "/organizations/{organization_id}/bans/{user_id}",
158 web::delete().to(crate::handlers::identity::organization::handler::unban_by_user),
155 web::delete().to(crate::handlers::identity::organization::unban_by_user),
159156 );
160157 cfg.route(
161158 "/organizations/{organization_id}/roles",
162 web::get().to(crate::handlers::identity::organization::handler::list_roles),
159 web::get().to(crate::handlers::identity::organization::list_roles),
163160 );
164161 cfg.route(
165162 "/organizations/{organization_id}/roles",
166 web::post().to(crate::handlers::identity::organization::handler::create_role),
163 web::post().to(crate::handlers::identity::organization::create_role),
167164 );
168165 cfg.route(
169166 "/organizations/{organization_id}/roles/assign",
170 web::post().to(crate::handlers::identity::organization::handler::assign_role),
167 web::post().to(crate::handlers::identity::organization::assign_role),
171168 );
172169 cfg.route(
173170 "/organizations/{organization_id}/roles/unassign",
174 web::post().to(crate::handlers::identity::organization::handler::unassign_role),
171 web::post().to(crate::handlers::identity::organization::unassign_role),
175172 );
176173 cfg.route(
177174 "/organizations/{organization_id}/roles/{role_id}",
178 web::get().to(crate::handlers::identity::organization::handler::get_role),
175 web::get().to(crate::handlers::identity::organization::get_role),
179176 );
180177 cfg.route(
181178 "/organizations/{organization_id}/roles/{role_id}",
182 web::patch().to(crate::handlers::identity::organization::handler::update_role),
179 web::patch().to(crate::handlers::identity::organization::update_role),
183180 );
184181 cfg.route(
185182 "/organizations/{organization_id}/roles/{role_id}/join",
186 web::post().to(crate::handlers::identity::organization::handler::join_role),
183 web::post().to(crate::handlers::identity::organization::join_role),
187184 );
188185 cfg.route(
189186 "/organizations/{organization_id}/roles/{role_id}/join",
190 web::delete().to(crate::handlers::identity::organization::handler::leave_role),
187 web::delete().to(crate::handlers::identity::organization::leave_role),
191188 );
192189 cfg.route(
193190 "/organizations/{organization_id}/roles/{role_id}",
194 web::delete().to(crate::handlers::identity::organization::handler::delete_role),
191 web::delete().to(crate::handlers::identity::organization::delete_role),
195192 );
196193 cfg.route(
197194 "/organizations/{organization_id}/roles/{role_id}/permissions",
198 web::put().to(crate::handlers::identity::organization::handler::set_role_permissions),
195 web::put().to(crate::handlers::identity::organization::set_role_permissions),
199196 );
200197 cfg.route(
201198 "/organizations/{organization_id}/permissions/check",
202 web::post().to(crate::handlers::identity::organization::handler::check_permission),
199 web::post().to(crate::handlers::identity::organization::check_permission),
203200 );
204201 cfg.route(
205202 "/organizations/{organization_id}/invites",
206 web::post().to(crate::handlers::identity::organization::handler::create_invite),
203 web::post().to(crate::handlers::identity::organization::create_invite),
207204 );
208205 cfg.route(
209206 "/organizations/{organization_id}/invites",
210 web::get().to(crate::handlers::identity::organization::handler::list_invites),
207 web::get().to(crate::handlers::identity::organization::list_invites),
211208 );
212209 cfg.route(
213210 "/organizations/{organization_id}/invites/{invite_id}",
214 web::delete().to(crate::handlers::identity::organization::handler::revoke_invite),
211 web::delete().to(crate::handlers::identity::organization::revoke_invite),
215212 );
216213}
Modified lib/auth/src/routes_account.rs View lib/auth/src/routes_account.rs
@@ -1,6 +1,7 @@
11use export::http::Router;
22use export::http::routing::{delete, get, post, put};
33
4use crate::AuthService;
45use crate::handlers::account::accept_agreement::accept_agreement;
56use crate::handlers::account::account_deletion::{
67 account_deletion_status, cancel_account_deletion, request_account_deletion,
@@ -17,7 +18,6 @@
1718use crate::handlers::account::update_preferences::update_preferences;
1819use crate::handlers::account::update_profile::update_profile;
1920use crate::handlers::account::users;
20use crate::service::AuthService;
2121
2222pub fn router(_service: AuthService) -> Router<AuthService> {
2323 let public = Router::<AuthService>::new().route("/auth/agreements", get(list_agreements));
Modified lib/webapi/src/lib.rs View lib/webapi/src/lib.rs
@@ -43,7 +43,7 @@
4343
4444 let cleanup_router = cleanup::router(space.clone(), issues.clone(), git.clone());
4545
46 let auth_domain = auth.service().clone();
46 let auth_domain = auth.clone();
4747 let public = Router::<()>::new()
4848 .setup(move |scope| {
4949 scope