feat(manager-core,picloud): api_keys_api + deactivation cascade
* auth: generate_api_key() mints pic_<base32(32 bytes)>, splits the
indexed 8-char prefix, and Argon2-hashes the body. Adds the
data-encoding workspace dep for unpadded base32.
* api_keys_api: POST /api/v1/admin/api-keys (mint, returns raw_token
exactly once), GET (caller's own, no raw), DELETE {id} (caller's
own; 404 deliberately covers both 'missing' and 'not yours').
Mint validation rejects bound keys carrying instance:* scopes (422).
* AdminsState gains the api keys repo; PATCH set_active(false) now
expires every active key for that user alongside session wipe —
Phase 3.5 deactivation symmetry.
* picloud lib wires PostgresApiKeyRepository through AuthDeps into
AdminsState + ApiKeysState; api_keys_router merges into the
guarded_admin layer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,13 +10,14 @@ use axum::middleware::from_fn_with_state;
|
||||
use axum::{routing::get, Json, Router};
|
||||
use picloud_executor_core::{Engine, Limits};
|
||||
use picloud_manager_core::{
|
||||
admin_router, admins_router, apps_api, apps_router, auth_router, compile_routes, migrations,
|
||||
require_authenticated, route_admin_router, AdminSessionRepository, AdminState,
|
||||
AdminUserRepository, AdminsState, ApiKeyRepository, AppDomainRepository, AppRepository,
|
||||
AppsState, AuthState, PostgresAdminSessionRepository, PostgresAdminUserRepository,
|
||||
PostgresApiKeyRepository, PostgresAppDomainRepository, PostgresAppRepository,
|
||||
PostgresExecutionLogRepository, PostgresExecutionLogSink, PostgresRouteRepository,
|
||||
PostgresScriptRepository, RepoResolver, RouteAdminState, RouteRepository, SandboxCeiling,
|
||||
admin_router, admins_router, api_keys_router, apps_api, apps_router, auth_router,
|
||||
compile_routes, migrations, require_authenticated, route_admin_router, AdminSessionRepository,
|
||||
AdminState, AdminUserRepository, AdminsState, ApiKeyRepository, ApiKeysState,
|
||||
AppDomainRepository, AppRepository, AppsState, AuthState, PostgresAdminSessionRepository,
|
||||
PostgresAdminUserRepository, PostgresApiKeyRepository, PostgresAppDomainRepository,
|
||||
PostgresAppRepository, PostgresExecutionLogRepository, PostgresExecutionLogSink,
|
||||
PostgresRouteRepository, PostgresScriptRepository, RepoResolver, RouteAdminState,
|
||||
RouteRepository, SandboxCeiling,
|
||||
};
|
||||
use picloud_orchestrator_core::routing::{AppDomainTable, RouteTable};
|
||||
use picloud_orchestrator_core::{
|
||||
@@ -148,12 +149,16 @@ pub async fn build_app(pool: PgPool, auth: AuthDeps) -> anyhow::Result<Router> {
|
||||
let auth_state = AuthState {
|
||||
users: auth.users.clone(),
|
||||
sessions: auth.sessions.clone(),
|
||||
keys: auth.keys,
|
||||
keys: auth.keys.clone(),
|
||||
ttl: auth.ttl,
|
||||
};
|
||||
let admins_state = AdminsState {
|
||||
users: auth.users,
|
||||
sessions: auth.sessions,
|
||||
keys: auth.keys.clone(),
|
||||
};
|
||||
let api_keys_state = ApiKeysState {
|
||||
keys: auth.keys,
|
||||
};
|
||||
|
||||
// /admin/auth/login + /logout are unguarded by design (login is how
|
||||
@@ -167,6 +172,7 @@ pub async fn build_app(pool: PgPool, auth: AuthDeps) -> anyhow::Result<Router> {
|
||||
.merge(route_admin_router(route_admin))
|
||||
.merge(admins_router(admins_state))
|
||||
.merge(apps_router(apps_state))
|
||||
.merge(api_keys_router(api_keys_state))
|
||||
.layer(from_fn_with_state(
|
||||
auth_state.clone(),
|
||||
require_authenticated,
|
||||
|
||||
Reference in New Issue
Block a user