feat: complete Epic 1 production foundation
This commit is contained in:
@@ -5,18 +5,52 @@ use serde_json::json;
|
||||
use crate::{
|
||||
auth::{AuthenticatedSession, cleared_session_cookie, extract_session_token, session_cookie},
|
||||
error::ApiError,
|
||||
rate_limit::ClientIdentityBucket,
|
||||
service::{
|
||||
ChangePasswordPayload, LoginPayload, UpdateCurrentWorkspacePayload, UpdateProfilePayload,
|
||||
ChangePasswordPayload, CompleteBootstrapPayload, LoginPayload,
|
||||
UpdateCurrentWorkspacePayload, UpdateProfilePayload,
|
||||
},
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
pub async fn bootstrap_status(
|
||||
State(state): State<AppState>,
|
||||
) -> Result<Json<serde_json::Value>, ApiError> {
|
||||
Ok(Json(serde_json::json!(
|
||||
state.service.bootstrap_status().await?
|
||||
)))
|
||||
}
|
||||
|
||||
pub async fn complete_bootstrap(
|
||||
State(state): State<AppState>,
|
||||
jar: CookieJar,
|
||||
Json(payload): Json<CompleteBootstrapPayload>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let (session_data, session) = state.service.complete_bootstrap(payload).await?;
|
||||
let cookie_value = format!(
|
||||
"{}.{}",
|
||||
session_data.session_id.as_str(),
|
||||
session_data.value
|
||||
);
|
||||
let jar = jar.add(session_cookie(state.service.auth_settings(), &cookie_value));
|
||||
|
||||
Ok((jar, Json(serde_json::json!(session))))
|
||||
}
|
||||
|
||||
pub async fn login(
|
||||
State(state): State<AppState>,
|
||||
jar: CookieJar,
|
||||
client_bucket: Option<Extension<ClientIdentityBucket>>,
|
||||
Json(payload): Json<LoginPayload>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let (session_data, session) = state.service.login(payload).await?;
|
||||
let client_bucket = client_bucket
|
||||
.as_ref()
|
||||
.map(|Extension(bucket)| bucket.0.as_str())
|
||||
.unwrap_or("anonymous:/api/auth/login");
|
||||
let (session_data, session) = state
|
||||
.service
|
||||
.login_with_client_bucket(payload, client_bucket)
|
||||
.await?;
|
||||
let cookie_value = format!(
|
||||
"{}.{}",
|
||||
session_data.session_id.as_str(),
|
||||
@@ -54,6 +88,22 @@ pub async fn get_session(
|
||||
Ok(Json(serde_json::json!(session)))
|
||||
}
|
||||
|
||||
pub async fn refresh_session_csrf(
|
||||
State(state): State<AppState>,
|
||||
jar: CookieJar,
|
||||
) -> Result<Json<serde_json::Value>, ApiError> {
|
||||
let (session_id, session_value) = extract_session_token(&jar)
|
||||
.ok_or_else(|| ApiError::unauthorized("authentication required"))?;
|
||||
state
|
||||
.service
|
||||
.get_session(&session_id, &session_value)
|
||||
.await?
|
||||
.ok_or_else(|| ApiError::unauthorized("session is invalid or expired"))?;
|
||||
let csrf_token = state.service.rotate_session_csrf_token(&session_id).await?;
|
||||
|
||||
Ok(Json(json!({ "csrf_token": csrf_token })))
|
||||
}
|
||||
|
||||
pub async fn get_profile(
|
||||
Extension(session): Extension<AuthenticatedSession>,
|
||||
) -> Result<Json<serde_json::Value>, ApiError> {
|
||||
@@ -73,6 +123,7 @@ pub async fn update_profile(
|
||||
.service
|
||||
.update_profile(
|
||||
&session.user.id,
|
||||
&session.session_id,
|
||||
session.current_workspace_id.as_ref(),
|
||||
payload,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user