chore(v1.1.8): cargo fmt --all (reviewer)
8 files needed re-wrapping after the F2 clippy-fix pass landed un-fmt'd. Pure cosmetic — no behavioral change. Re-runs of `cargo fmt --all -- --check` now exit clean. Reviewer-supplied per REVIEW.md §7; folded onto the branch to avoid a NEEDS-CHANGES round-trip on a purely mechanical fix.
This commit is contained in:
@@ -328,7 +328,8 @@ fn bind_complete_password_reset(
|
|||||||
let svc = svc.clone();
|
let svc = svc.clone();
|
||||||
let cx = cx.clone();
|
let cx = cx.clone();
|
||||||
let user_opt = block_on(async move {
|
let user_opt = block_on(async move {
|
||||||
svc.complete_password_reset(&cx, &token, &new_password).await
|
svc.complete_password_reset(&cx, &token, &new_password)
|
||||||
|
.await
|
||||||
})?;
|
})?;
|
||||||
Ok(user_opt.map_or(Dynamic::UNIT, |u| Dynamic::from(user_to_map(&u))))
|
Ok(user_opt.map_or(Dynamic::UNIT, |u| Dynamic::from(user_to_map(&u))))
|
||||||
},
|
},
|
||||||
@@ -390,7 +391,8 @@ fn bind_accept_invite(module: &mut Module, svc: &Arc<dyn UsersService>, cx: &Arc
|
|||||||
let svc = svc.clone();
|
let svc = svc.clone();
|
||||||
let cx = cx.clone();
|
let cx = cx.clone();
|
||||||
let accept_opt: Option<GeneratedAccept> = block_on(async move {
|
let accept_opt: Option<GeneratedAccept> = block_on(async move {
|
||||||
svc.accept_invite(&cx, &token, &password, display_name).await
|
svc.accept_invite(&cx, &token, &password, display_name)
|
||||||
|
.await
|
||||||
})?;
|
})?;
|
||||||
Ok(accept_opt.map_or(Dynamic::UNIT, |a| Dynamic::from(a.session.token)))
|
Ok(accept_opt.map_or(Dynamic::UNIT, |a| Dynamic::from(a.session.token)))
|
||||||
},
|
},
|
||||||
@@ -471,21 +473,26 @@ fn user_to_map(user: &AppUser) -> Map {
|
|||||||
user.last_login_at
|
user.last_login_at
|
||||||
.map_or(Dynamic::UNIT, |d| Dynamic::from(d.to_rfc3339())),
|
.map_or(Dynamic::UNIT, |d| Dynamic::from(d.to_rfc3339())),
|
||||||
);
|
);
|
||||||
m.insert("created_at".into(), Dynamic::from(user.created_at.to_rfc3339()));
|
m.insert(
|
||||||
m.insert("updated_at".into(), Dynamic::from(user.updated_at.to_rfc3339()));
|
"created_at".into(),
|
||||||
let roles: Array = user
|
Dynamic::from(user.created_at.to_rfc3339()),
|
||||||
.roles
|
);
|
||||||
.iter()
|
m.insert(
|
||||||
.cloned()
|
"updated_at".into(),
|
||||||
.map(Dynamic::from)
|
Dynamic::from(user.updated_at.to_rfc3339()),
|
||||||
.collect();
|
);
|
||||||
|
let roles: Array = user.roles.iter().cloned().map(Dynamic::from).collect();
|
||||||
m.insert("roles".into(), roles.into());
|
m.insert("roles".into(), roles.into());
|
||||||
m
|
m
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_page_to_map(page: &UsersListPage) -> Map {
|
fn list_page_to_map(page: &UsersListPage) -> Map {
|
||||||
let mut m = Map::new();
|
let mut m = Map::new();
|
||||||
let items: Array = page.items.iter().map(|u| Dynamic::from(user_to_map(u))).collect();
|
let items: Array = page
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.map(|u| Dynamic::from(user_to_map(u)))
|
||||||
|
.collect();
|
||||||
m.insert("users".into(), items.into());
|
m.insert("users".into(), items.into());
|
||||||
m.insert(
|
m.insert(
|
||||||
"next_cursor".into(),
|
"next_cursor".into(),
|
||||||
|
|||||||
@@ -121,7 +121,8 @@ impl AppSecretsRepo for PostgresAppSecretsRepo {
|
|||||||
.fetch_one(&self.pool)
|
.fetch_one(&self.pool)
|
||||||
.await?;
|
.await?;
|
||||||
// A row exists by construction, so a key must decode.
|
// A row exists by construction, so a key must decode.
|
||||||
self.decode(row.0, row.1)?.ok_or(AppSecretsRepoError::Crypto)
|
self.decode(row.0, row.1)?
|
||||||
|
.ok_or(AppSecretsRepoError::Crypto)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn signing_key(&self, app_id: AppId) -> Result<Option<Vec<u8>>, AppSecretsRepoError> {
|
async fn signing_key(&self, app_id: AppId) -> Result<Option<Vec<u8>>, AppSecretsRepoError> {
|
||||||
@@ -167,8 +168,8 @@ mod tests {
|
|||||||
let secret = vec![3u8; 32];
|
let secret = vec![3u8; 32];
|
||||||
let enc = crypto::encrypt(&secret, key().as_bytes());
|
let enc = crypto::encrypt(&secret, key().as_bytes());
|
||||||
let other = MasterKey::from_bytes([1u8; 32]);
|
let other = MasterKey::from_bytes([1u8; 32]);
|
||||||
let err = decode_signing_key(&other, Some(enc.ciphertext), Some(enc.nonce.to_vec()))
|
let err =
|
||||||
.unwrap_err();
|
decode_signing_key(&other, Some(enc.ciphertext), Some(enc.nonce.to_vec())).unwrap_err();
|
||||||
assert!(matches!(err, AppSecretsRepoError::Crypto));
|
assert!(matches!(err, AppSecretsRepoError::Crypto));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -759,7 +759,9 @@ mod tests {
|
|||||||
let p = principal(InstanceRole::Member);
|
let p = principal(InstanceRole::Member);
|
||||||
repo.grant(p.user_id, app, AppRole::Editor).await;
|
repo.grant(p.user_id, app, AppRole::Editor).await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
can(&repo, &p, Capability::AppUsersAdmin(app)).await.unwrap(),
|
can(&repo, &p, Capability::AppUsersAdmin(app))
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
Decision::Deny,
|
Decision::Deny,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ pub mod app_user_repo;
|
|||||||
pub mod app_user_role_repo;
|
pub mod app_user_role_repo;
|
||||||
pub mod app_user_session_repo;
|
pub mod app_user_session_repo;
|
||||||
pub mod app_user_verification_repo;
|
pub mod app_user_verification_repo;
|
||||||
pub mod users_admin_api;
|
|
||||||
pub mod users_service;
|
|
||||||
pub mod apps_api;
|
pub mod apps_api;
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod auth_api;
|
pub mod auth_api;
|
||||||
@@ -72,6 +70,8 @@ pub mod topics_api;
|
|||||||
pub mod trigger_config;
|
pub mod trigger_config;
|
||||||
pub mod trigger_repo;
|
pub mod trigger_repo;
|
||||||
pub mod triggers_api;
|
pub mod triggers_api;
|
||||||
|
pub mod users_admin_api;
|
||||||
|
pub mod users_service;
|
||||||
|
|
||||||
pub use abandoned_repo::{
|
pub use abandoned_repo::{
|
||||||
AbandonedRepo, AbandonedRepoError, NewAbandonedExecution, PostgresAbandonedRepo,
|
AbandonedRepo, AbandonedRepoError, NewAbandonedExecution, PostgresAbandonedRepo,
|
||||||
@@ -84,27 +84,6 @@ pub use admin_user_repo::{
|
|||||||
AdminUserCredentials, AdminUserRepository, AdminUserRepositoryError, AdminUserRow,
|
AdminUserCredentials, AdminUserRepository, AdminUserRepositoryError, AdminUserRow,
|
||||||
PostgresAdminUserRepository,
|
PostgresAdminUserRepository,
|
||||||
};
|
};
|
||||||
pub use app_user_repo::{
|
|
||||||
AppUserCredentials, AppUserRepository, AppUserRepositoryError, AppUserRow,
|
|
||||||
ListOpts as AppUserListOpts, ListPage as AppUserListPage, PostgresAppUserRepository,
|
|
||||||
};
|
|
||||||
pub use app_user_session_repo::{
|
|
||||||
AppUserSessionLookup, AppUserSessionRepository, AppUserSessionRepositoryError,
|
|
||||||
PostgresAppUserSessionRepository,
|
|
||||||
};
|
|
||||||
pub use app_user_invitation_repo::{
|
|
||||||
AppUserInvitationRepo, AppUserInvitationRepoError, ConsumedInvitation, InvitationRow,
|
|
||||||
PostgresAppUserInvitationRepo,
|
|
||||||
};
|
|
||||||
pub use app_user_password_reset_repo::{
|
|
||||||
AppUserPasswordResetRepo, AppUserPasswordResetRepoError, PostgresAppUserPasswordResetRepo,
|
|
||||||
};
|
|
||||||
pub use app_user_role_repo::{AppUserRoleRepo, AppUserRoleRepoError, PostgresAppUserRoleRepo};
|
|
||||||
pub use users_admin_api::{app_users_router, AppUsersApiError, AppUsersState};
|
|
||||||
pub use app_user_verification_repo::{
|
|
||||||
AppUserVerificationRepo, AppUserVerificationRepoError, PostgresAppUserVerificationRepo,
|
|
||||||
};
|
|
||||||
pub use users_service::{UsersServiceConfig, UsersServiceImpl};
|
|
||||||
pub use admin_users_api::{admins_router, AdminsState};
|
pub use admin_users_api::{admins_router, AdminsState};
|
||||||
pub use api::{admin_router, AdminState};
|
pub use api::{admin_router, AdminState};
|
||||||
pub use api_key_repo::{
|
pub use api_key_repo::{
|
||||||
@@ -123,6 +102,25 @@ pub use app_repo::{resolve_app, AppLookup, AppRepository, PostgresAppRepository}
|
|||||||
pub use app_secrets_repo::{
|
pub use app_secrets_repo::{
|
||||||
AppSecretsRepo, AppSecretsRepoError, PostgresAppSecretsRepo, SIGNING_KEY_LEN,
|
AppSecretsRepo, AppSecretsRepoError, PostgresAppSecretsRepo, SIGNING_KEY_LEN,
|
||||||
};
|
};
|
||||||
|
pub use app_user_invitation_repo::{
|
||||||
|
AppUserInvitationRepo, AppUserInvitationRepoError, ConsumedInvitation, InvitationRow,
|
||||||
|
PostgresAppUserInvitationRepo,
|
||||||
|
};
|
||||||
|
pub use app_user_password_reset_repo::{
|
||||||
|
AppUserPasswordResetRepo, AppUserPasswordResetRepoError, PostgresAppUserPasswordResetRepo,
|
||||||
|
};
|
||||||
|
pub use app_user_repo::{
|
||||||
|
AppUserCredentials, AppUserRepository, AppUserRepositoryError, AppUserRow,
|
||||||
|
ListOpts as AppUserListOpts, ListPage as AppUserListPage, PostgresAppUserRepository,
|
||||||
|
};
|
||||||
|
pub use app_user_role_repo::{AppUserRoleRepo, AppUserRoleRepoError, PostgresAppUserRoleRepo};
|
||||||
|
pub use app_user_session_repo::{
|
||||||
|
AppUserSessionLookup, AppUserSessionRepository, AppUserSessionRepositoryError,
|
||||||
|
PostgresAppUserSessionRepository,
|
||||||
|
};
|
||||||
|
pub use app_user_verification_repo::{
|
||||||
|
AppUserVerificationRepo, AppUserVerificationRepoError, PostgresAppUserVerificationRepo,
|
||||||
|
};
|
||||||
pub use apps_api::{apps_router, AppsState};
|
pub use apps_api::{apps_router, AppsState};
|
||||||
pub use auth_api::auth_router;
|
pub use auth_api::auth_router;
|
||||||
pub use auth_bootstrap::{
|
pub use auth_bootstrap::{
|
||||||
@@ -192,3 +190,5 @@ pub use trigger_repo::{
|
|||||||
Trigger, TriggerDetails, TriggerDispatchMode, TriggerKind, TriggerRepo, TriggerRepoError,
|
Trigger, TriggerDetails, TriggerDispatchMode, TriggerKind, TriggerRepo, TriggerRepoError,
|
||||||
};
|
};
|
||||||
pub use triggers_api::{triggers_router, TriggersApiError, TriggersState};
|
pub use triggers_api::{triggers_router, TriggersApiError, TriggersState};
|
||||||
|
pub use users_admin_api::{app_users_router, AppUsersApiError, AppUsersState};
|
||||||
|
pub use users_service::{UsersServiceConfig, UsersServiceImpl};
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ use async_trait::async_trait;
|
|||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use picloud_shared::{
|
use picloud_shared::{
|
||||||
AppId, AppUser, AppUserId, CreateUserInput, EmailError, EmailService, EmailTemplateOpts,
|
AppId, AppUser, AppUserId, CreateUserInput, EmailError, EmailService, EmailTemplateOpts,
|
||||||
GeneratedAccept, GeneratedSession, InviteOpts, Invitation, InvitationId, OutboundEmail,
|
GeneratedAccept, GeneratedSession, Invitation, InvitationId, InviteOpts, OutboundEmail,
|
||||||
Principal, SdkCallCx, ServiceEvent, ServiceEventEmitter, UpdateUserInput, UsersError,
|
Principal, SdkCallCx, ServiceEvent, ServiceEventEmitter, UpdateUserInput, UsersError,
|
||||||
UsersListOpts, UsersListPage, UsersService,
|
UsersListOpts, UsersListPage, UsersService,
|
||||||
};
|
};
|
||||||
@@ -84,7 +84,10 @@ impl UsersServiceConfig {
|
|||||||
pub fn from_env() -> Self {
|
pub fn from_env() -> Self {
|
||||||
let default = Self::default();
|
let default = Self::default();
|
||||||
Self {
|
Self {
|
||||||
session_ttl: env_duration_hours("PICLOUD_APP_USER_SESSION_TTL_HOURS", default.session_ttl),
|
session_ttl: env_duration_hours(
|
||||||
|
"PICLOUD_APP_USER_SESSION_TTL_HOURS",
|
||||||
|
default.session_ttl,
|
||||||
|
),
|
||||||
session_absolute_ttl: env_duration_hours(
|
session_absolute_ttl: env_duration_hours(
|
||||||
"PICLOUD_APP_USER_SESSION_ABSOLUTE_HOURS",
|
"PICLOUD_APP_USER_SESSION_ABSOLUTE_HOURS",
|
||||||
default.session_absolute_ttl,
|
default.session_absolute_ttl,
|
||||||
@@ -162,7 +165,11 @@ impl UsersServiceImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn require(&self, principal: Option<&Principal>, cap: Capability) -> Result<(), UsersError> {
|
async fn require(
|
||||||
|
&self,
|
||||||
|
principal: Option<&Principal>,
|
||||||
|
cap: Capability,
|
||||||
|
) -> Result<(), UsersError> {
|
||||||
let Some(p) = principal else {
|
let Some(p) = principal else {
|
||||||
// Public HTTP execution — script-as-gate; not denied here.
|
// Public HTTP execution — script-as-gate; not denied here.
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -192,7 +199,11 @@ impl UsersServiceImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_roles(&self, app_id: AppId, user_id: AppUserId) -> Result<Vec<String>, UsersError> {
|
async fn fetch_roles(
|
||||||
|
&self,
|
||||||
|
app_id: AppId,
|
||||||
|
user_id: AppUserId,
|
||||||
|
) -> Result<Vec<String>, UsersError> {
|
||||||
Ok(self.roles.list_for_user(app_id, user_id).await?)
|
Ok(self.roles.list_for_user(app_id, user_id).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,19 +369,14 @@ fn internal_cx(cx: &SdkCallCx) -> SdkCallCx {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl UsersService for UsersServiceImpl {
|
impl UsersService for UsersServiceImpl {
|
||||||
async fn create(
|
async fn create(&self, cx: &SdkCallCx, input: CreateUserInput) -> Result<AppUser, UsersError> {
|
||||||
&self,
|
|
||||||
cx: &SdkCallCx,
|
|
||||||
input: CreateUserInput,
|
|
||||||
) -> Result<AppUser, UsersError> {
|
|
||||||
self.require(cx.principal.as_ref(), Capability::AppUsersWrite(cx.app_id))
|
self.require(cx.principal.as_ref(), Capability::AppUsersWrite(cx.app_id))
|
||||||
.await?;
|
.await?;
|
||||||
let email = validate_email(&input.email)?;
|
let email = validate_email(&input.email)?;
|
||||||
validate_password(&input.password)?;
|
validate_password(&input.password)?;
|
||||||
let display_name = validate_display_name(input.display_name.as_deref())?;
|
let display_name = validate_display_name(input.display_name.as_deref())?;
|
||||||
let password_hash = hash_password(&input.password).map_err(|e| {
|
let password_hash = hash_password(&input.password)
|
||||||
UsersError::Backend(format!("password hashing failed: {e}"))
|
.map_err(|e| UsersError::Backend(format!("password hashing failed: {e}")))?;
|
||||||
})?;
|
|
||||||
let row = self
|
let row = self
|
||||||
.users
|
.users
|
||||||
.create(cx.app_id, &email, &password_hash, display_name.as_deref())
|
.create(cx.app_id, &email, &password_hash, display_name.as_deref())
|
||||||
@@ -443,11 +449,7 @@ impl UsersService for UsersServiceImpl {
|
|||||||
Ok(deleted)
|
Ok(deleted)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list(
|
async fn list(&self, cx: &SdkCallCx, opts: UsersListOpts) -> Result<UsersListPage, UsersError> {
|
||||||
&self,
|
|
||||||
cx: &SdkCallCx,
|
|
||||||
opts: UsersListOpts,
|
|
||||||
) -> Result<UsersListPage, UsersError> {
|
|
||||||
self.require(cx.principal.as_ref(), Capability::AppUsersRead(cx.app_id))
|
self.require(cx.principal.as_ref(), Capability::AppUsersRead(cx.app_id))
|
||||||
.await?;
|
.await?;
|
||||||
let limit = opts.limit.unwrap_or(50).clamp(1, 500);
|
let limit = opts.limit.unwrap_or(50).clamp(1, 500);
|
||||||
@@ -540,11 +542,7 @@ impl UsersService for UsersServiceImpl {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn verify(
|
async fn verify(&self, cx: &SdkCallCx, token: &str) -> Result<Option<AppUser>, UsersError> {
|
||||||
&self,
|
|
||||||
cx: &SdkCallCx,
|
|
||||||
token: &str,
|
|
||||||
) -> Result<Option<AppUser>, UsersError> {
|
|
||||||
self.require(cx.principal.as_ref(), Capability::AppUsersRead(cx.app_id))
|
self.require(cx.principal.as_ref(), Capability::AppUsersRead(cx.app_id))
|
||||||
.await?;
|
.await?;
|
||||||
self.resolve_session(cx.app_id, token).await
|
self.resolve_session(cx.app_id, token).await
|
||||||
@@ -626,10 +624,7 @@ impl UsersService for UsersServiceImpl {
|
|||||||
let Some(user_id) = self.verifications.consume(cx.app_id, &token_hash).await? else {
|
let Some(user_id) = self.verifications.consume(cx.app_id, &token_hash).await? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let row = self
|
let row = self.users.mark_email_verified(cx.app_id, user_id).await?;
|
||||||
.users
|
|
||||||
.mark_email_verified(cx.app_id, user_id)
|
|
||||||
.await?;
|
|
||||||
let roles = self.fetch_roles(cx.app_id, row.id).await?;
|
let roles = self.fetch_roles(cx.app_id, row.id).await?;
|
||||||
let user = Self::to_app_user(row, roles);
|
let user = Self::to_app_user(row, roles);
|
||||||
self.emit(cx, "email_verified", user.id).await;
|
self.emit(cx, "email_verified", user.id).await;
|
||||||
@@ -792,7 +787,11 @@ impl UsersService for UsersServiceImpl {
|
|||||||
// No require: the invitation token IS the authorization.
|
// No require: the invitation token IS the authorization.
|
||||||
validate_password(password)?;
|
validate_password(password)?;
|
||||||
let token_hash = hash_token(token);
|
let token_hash = hash_token(token);
|
||||||
let Some(consumed) = self.invitations.consume_by_token(cx.app_id, &token_hash).await? else {
|
let Some(consumed) = self
|
||||||
|
.invitations
|
||||||
|
.consume_by_token(cx.app_id, &token_hash)
|
||||||
|
.await?
|
||||||
|
else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1114,4 +1113,3 @@ impl UsersServiceImpl {
|
|||||||
Ok(Some(Self::to_app_user(row, roles)))
|
Ok(Some(Self::to_app_user(row, roles)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,14 +24,13 @@ use picloud_manager_core::{
|
|||||||
PostgresAppRepository, PostgresAppSecretsRepo, PostgresAppUserInvitationRepo,
|
PostgresAppRepository, PostgresAppSecretsRepo, PostgresAppUserInvitationRepo,
|
||||||
PostgresAppUserPasswordResetRepo, PostgresAppUserRepository, PostgresAppUserRoleRepo,
|
PostgresAppUserPasswordResetRepo, PostgresAppUserRepository, PostgresAppUserRoleRepo,
|
||||||
PostgresAppUserSessionRepository, PostgresAppUserVerificationRepo, PostgresDeadLetterRepo,
|
PostgresAppUserSessionRepository, PostgresAppUserVerificationRepo, PostgresDeadLetterRepo,
|
||||||
PostgresDeadLetterService,
|
PostgresDeadLetterService, PostgresDocsRepo, PostgresExecutionLogRepository,
|
||||||
PostgresDocsRepo, PostgresExecutionLogRepository, PostgresExecutionLogSink, PostgresKvRepo,
|
PostgresExecutionLogSink, PostgresKvRepo, PostgresOutboxRepo, PostgresPubsubRepo,
|
||||||
PostgresOutboxRepo, PostgresPubsubRepo, PostgresRouteRepository, PostgresScriptRepository,
|
PostgresRouteRepository, PostgresScriptRepository, PostgresSecretsRepo, PostgresTopicRepo,
|
||||||
PostgresSecretsRepo, PostgresTopicRepo, PostgresTriggerRepo, PrincipalResolver,
|
PostgresTriggerRepo, PrincipalResolver, PubsubServiceImpl, RealtimeAuthorityImpl, RepoResolver,
|
||||||
PubsubServiceImpl, RealtimeAuthorityImpl, RepoResolver, RouteAdminState, RouteRepository,
|
RouteAdminState, RouteRepository, SandboxCeiling, ScriptRepository, SecretsConfig,
|
||||||
SandboxCeiling, ScriptRepository, SecretsConfig, SecretsServiceImpl, SecretsState,
|
SecretsServiceImpl, SecretsState, SubscriberTokenConfig, TopicRepo, TopicsState, TriggerConfig,
|
||||||
SubscriberTokenConfig, TopicRepo, TopicsState, TriggerConfig, TriggerRepo, TriggersState,
|
TriggerRepo, TriggersState, UsersServiceConfig, UsersServiceImpl,
|
||||||
UsersServiceConfig, UsersServiceImpl,
|
|
||||||
};
|
};
|
||||||
use picloud_orchestrator_core::realtime::DEFAULT_GC_INTERVAL_SECS;
|
use picloud_orchestrator_core::realtime::DEFAULT_GC_INTERVAL_SECS;
|
||||||
use picloud_orchestrator_core::routing::{AppDomainTable, RouteTable};
|
use picloud_orchestrator_core::routing::{AppDomainTable, RouteTable};
|
||||||
@@ -237,12 +236,10 @@ pub async fn build_app(
|
|||||||
// `users::*` namespace and the admin /apps/{id}/users HTTP surface.
|
// `users::*` namespace and the admin /apps/{id}/users HTTP surface.
|
||||||
let app_users_repo = Arc::new(PostgresAppUserRepository::new(pool.clone()));
|
let app_users_repo = Arc::new(PostgresAppUserRepository::new(pool.clone()));
|
||||||
let app_user_sessions_repo = Arc::new(PostgresAppUserSessionRepository::new(pool.clone()));
|
let app_user_sessions_repo = Arc::new(PostgresAppUserSessionRepository::new(pool.clone()));
|
||||||
let app_user_verifications_repo =
|
let app_user_verifications_repo = Arc::new(PostgresAppUserVerificationRepo::new(pool.clone()));
|
||||||
Arc::new(PostgresAppUserVerificationRepo::new(pool.clone()));
|
|
||||||
let app_user_password_resets_repo =
|
let app_user_password_resets_repo =
|
||||||
Arc::new(PostgresAppUserPasswordResetRepo::new(pool.clone()));
|
Arc::new(PostgresAppUserPasswordResetRepo::new(pool.clone()));
|
||||||
let app_user_invitations_repo =
|
let app_user_invitations_repo = Arc::new(PostgresAppUserInvitationRepo::new(pool.clone()));
|
||||||
Arc::new(PostgresAppUserInvitationRepo::new(pool.clone()));
|
|
||||||
let app_user_roles_repo = Arc::new(PostgresAppUserRoleRepo::new(pool.clone()));
|
let app_user_roles_repo = Arc::new(PostgresAppUserRoleRepo::new(pool.clone()));
|
||||||
let users: Arc<dyn UsersService> = Arc::new(UsersServiceImpl::new(
|
let users: Arc<dyn UsersService> = Arc::new(UsersServiceImpl::new(
|
||||||
app_users_repo.clone(),
|
app_users_repo.clone(),
|
||||||
@@ -473,7 +470,9 @@ pub async fn build_app(
|
|||||||
.merge(admins_router(admins_state))
|
.merge(admins_router(admins_state))
|
||||||
.merge(apps_router(apps_state))
|
.merge(apps_router(apps_state))
|
||||||
.merge(app_members_router(app_members_state))
|
.merge(app_members_router(app_members_state))
|
||||||
.merge(picloud_manager_core::app_users_router(app_users_admin_state))
|
.merge(picloud_manager_core::app_users_router(
|
||||||
|
app_users_admin_state,
|
||||||
|
))
|
||||||
.merge(api_keys_router(api_keys_state))
|
.merge(api_keys_router(api_keys_state))
|
||||||
.merge(triggers_router(triggers_state))
|
.merge(triggers_router(triggers_state))
|
||||||
.merge(files_admin_router(files_admin_state))
|
.merge(files_admin_router(files_admin_state))
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ pub use trigger_event::{
|
|||||||
DeadLetterEventDetail, DocsEventOp, FilesEventOp, KvEventOp, TriggerEvent,
|
DeadLetterEventDetail, DocsEventOp, FilesEventOp, KvEventOp, TriggerEvent,
|
||||||
};
|
};
|
||||||
pub use users::{
|
pub use users::{
|
||||||
AppUser, CreateUserInput, EmailTemplateOpts, GeneratedAccept, GeneratedSession, InviteOpts,
|
AppUser, CreateUserInput, EmailTemplateOpts, GeneratedAccept, GeneratedSession, Invitation,
|
||||||
Invitation, NoopUsersService, UpdateUserInput, UsersError, UsersListOpts, UsersListPage,
|
InviteOpts, NoopUsersService, UpdateUserInput, UsersError, UsersListOpts, UsersListPage,
|
||||||
UsersService,
|
UsersService,
|
||||||
};
|
};
|
||||||
pub use validator::{ScriptValidator, ValidatedScript, ValidationError};
|
pub use validator::{ScriptValidator, ValidatedScript, ValidationError};
|
||||||
|
|||||||
@@ -180,11 +180,7 @@ pub enum UsersError {
|
|||||||
pub trait UsersService: Send + Sync {
|
pub trait UsersService: Send + Sync {
|
||||||
// ---- CRUD ----
|
// ---- CRUD ----
|
||||||
|
|
||||||
async fn create(
|
async fn create(&self, cx: &SdkCallCx, input: CreateUserInput) -> Result<AppUser, UsersError>;
|
||||||
&self,
|
|
||||||
cx: &SdkCallCx,
|
|
||||||
input: CreateUserInput,
|
|
||||||
) -> Result<AppUser, UsersError>;
|
|
||||||
|
|
||||||
async fn get(&self, cx: &SdkCallCx, id: AppUserId) -> Result<Option<AppUser>, UsersError>;
|
async fn get(&self, cx: &SdkCallCx, id: AppUserId) -> Result<Option<AppUser>, UsersError>;
|
||||||
|
|
||||||
@@ -203,11 +199,7 @@ pub trait UsersService: Send + Sync {
|
|||||||
|
|
||||||
async fn delete(&self, cx: &SdkCallCx, id: AppUserId) -> Result<bool, UsersError>;
|
async fn delete(&self, cx: &SdkCallCx, id: AppUserId) -> Result<bool, UsersError>;
|
||||||
|
|
||||||
async fn list(
|
async fn list(&self, cx: &SdkCallCx, opts: UsersListOpts) -> Result<UsersListPage, UsersError>;
|
||||||
&self,
|
|
||||||
cx: &SdkCallCx,
|
|
||||||
opts: UsersListOpts,
|
|
||||||
) -> Result<UsersListPage, UsersError>;
|
|
||||||
|
|
||||||
// ---- Auth ----
|
// ---- Auth ----
|
||||||
|
|
||||||
@@ -224,11 +216,7 @@ pub trait UsersService: Send + Sync {
|
|||||||
/// Verify a session token. Returns the owning user on success,
|
/// Verify a session token. Returns the owning user on success,
|
||||||
/// `None` on missing / expired / revoked / cross-app token. Bumps
|
/// `None` on missing / expired / revoked / cross-app token. Bumps
|
||||||
/// the sliding-window TTL on success (capped at the absolute cap).
|
/// the sliding-window TTL on success (capped at the absolute cap).
|
||||||
async fn verify(
|
async fn verify(&self, cx: &SdkCallCx, token: &str) -> Result<Option<AppUser>, UsersError>;
|
||||||
&self,
|
|
||||||
cx: &SdkCallCx,
|
|
||||||
token: &str,
|
|
||||||
) -> Result<Option<AppUser>, UsersError>;
|
|
||||||
|
|
||||||
/// Logout — revokes the session. No-op for already-revoked /
|
/// Logout — revokes the session. No-op for already-revoked /
|
||||||
/// already-expired tokens.
|
/// already-expired tokens.
|
||||||
@@ -277,12 +265,8 @@ pub trait UsersService: Send + Sync {
|
|||||||
|
|
||||||
// ---- Invitations (commit 7) ----
|
// ---- Invitations (commit 7) ----
|
||||||
|
|
||||||
async fn invite(
|
async fn invite(&self, cx: &SdkCallCx, email: &str, opts: InviteOpts)
|
||||||
&self,
|
-> Result<(), UsersError>;
|
||||||
cx: &SdkCallCx,
|
|
||||||
email: &str,
|
|
||||||
opts: InviteOpts,
|
|
||||||
) -> Result<(), UsersError>;
|
|
||||||
|
|
||||||
async fn accept_invite(
|
async fn accept_invite(
|
||||||
&self,
|
&self,
|
||||||
@@ -383,11 +367,7 @@ impl UsersService for NoopUsersService {
|
|||||||
) -> Result<AppUser, UsersError> {
|
) -> Result<AppUser, UsersError> {
|
||||||
Err(UsersError::Backend(NOOP_MSG.into()))
|
Err(UsersError::Backend(NOOP_MSG.into()))
|
||||||
}
|
}
|
||||||
async fn get(
|
async fn get(&self, _cx: &SdkCallCx, _id: AppUserId) -> Result<Option<AppUser>, UsersError> {
|
||||||
&self,
|
|
||||||
_cx: &SdkCallCx,
|
|
||||||
_id: AppUserId,
|
|
||||||
) -> Result<Option<AppUser>, UsersError> {
|
|
||||||
Err(UsersError::Backend(NOOP_MSG.into()))
|
Err(UsersError::Backend(NOOP_MSG.into()))
|
||||||
}
|
}
|
||||||
async fn find_by_email(
|
async fn find_by_email(
|
||||||
@@ -423,11 +403,7 @@ impl UsersService for NoopUsersService {
|
|||||||
) -> Result<Option<GeneratedSession>, UsersError> {
|
) -> Result<Option<GeneratedSession>, UsersError> {
|
||||||
Err(UsersError::Backend(NOOP_MSG.into()))
|
Err(UsersError::Backend(NOOP_MSG.into()))
|
||||||
}
|
}
|
||||||
async fn verify(
|
async fn verify(&self, _cx: &SdkCallCx, _token: &str) -> Result<Option<AppUser>, UsersError> {
|
||||||
&self,
|
|
||||||
_cx: &SdkCallCx,
|
|
||||||
_token: &str,
|
|
||||||
) -> Result<Option<AppUser>, UsersError> {
|
|
||||||
Err(UsersError::Backend(NOOP_MSG.into()))
|
Err(UsersError::Backend(NOOP_MSG.into()))
|
||||||
}
|
}
|
||||||
async fn logout(&self, _cx: &SdkCallCx, _token: &str) -> Result<(), UsersError> {
|
async fn logout(&self, _cx: &SdkCallCx, _token: &str) -> Result<(), UsersError> {
|
||||||
|
|||||||
Reference in New Issue
Block a user