feat(openapi): add /api/auth to openapi declaration
This commit is contained in:
@@ -6,11 +6,12 @@ use axum::{
|
|||||||
routing::{get, post, put},
|
routing::{get, post, put},
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use utoipa::ToSchema;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::application::dtos::user_dto::{
|
use crate::application::dtos::user_dto::{
|
||||||
ChangePasswordDto, LoginDto, OidcCallbackQueryDto, OidcExchangeDto, OidcProviderInfoDto,
|
AuthResponseDto, ChangePasswordDto, LoginDto, OidcCallbackQueryDto, OidcExchangeDto,
|
||||||
RefreshTokenDto, RegisterDto, SetupAdminDto,
|
OidcProviderInfoDto, RefreshTokenDto, RegisterDto, SetupAdminDto, UserDto,
|
||||||
};
|
};
|
||||||
use crate::application::services::auth_application_service::OidcCallbackResult;
|
use crate::application::services::auth_application_service::OidcCallbackResult;
|
||||||
use crate::common::di::AppState;
|
use crate::common::di::AppState;
|
||||||
@@ -60,7 +61,20 @@ pub fn setup_route() -> Router<Arc<AppState>> {
|
|||||||
Router::new().route("/setup", post(setup_admin))
|
Router::new().route("/setup", post(setup_admin))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn register(
|
/// Register a new user account.
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/api/auth/register",
|
||||||
|
request_body = RegisterDto,
|
||||||
|
responses(
|
||||||
|
(status = 201, description = "User registered successfully", body = UserDto),
|
||||||
|
(status = 400, description = "Validation error"),
|
||||||
|
(status = 403, description = "Registration disabled"),
|
||||||
|
(status = 409, description = "Username or email already taken"),
|
||||||
|
),
|
||||||
|
tag = "auth"
|
||||||
|
)]
|
||||||
|
pub async fn register(
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
Json(dto): Json<RegisterDto>,
|
Json(dto): Json<RegisterDto>,
|
||||||
) -> Result<impl IntoResponse, AppError> {
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
@@ -122,7 +136,23 @@ async fn register(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn login(
|
/// Authenticate with username and password.
|
||||||
|
///
|
||||||
|
/// On success, sets `oxicloud_access`, `oxicloud_refresh`, and `oxicloud_csrf`
|
||||||
|
/// HttpOnly cookies in addition to returning the tokens in the JSON body.
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/api/auth/login",
|
||||||
|
request_body = LoginDto,
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Login successful — tokens in body and cookies", body = AuthResponseDto),
|
||||||
|
(status = 401, description = "Invalid credentials or password login disabled"),
|
||||||
|
(status = 403, description = "Account disabled"),
|
||||||
|
(status = 429, description = "Account temporarily locked (too many failed attempts)"),
|
||||||
|
),
|
||||||
|
tag = "auth"
|
||||||
|
)]
|
||||||
|
pub async fn login(
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Json(dto): Json<LoginDto>,
|
Json(dto): Json<LoginDto>,
|
||||||
@@ -238,10 +268,25 @@ async fn login(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Token refresh — accepts the refresh token from **either**:
|
/// Refresh an access token.
|
||||||
/// 1. JSON body `{ "refresh_token": "..." }` (API clients, backward compat)
|
///
|
||||||
/// 2. HttpOnly cookie `oxicloud_refresh` (browsers)
|
/// Accepts the refresh token from **either**:
|
||||||
async fn refresh_token(
|
/// 1. JSON body `{ "refresh_token": "..." }` (API clients / backward compat)
|
||||||
|
/// 2. HttpOnly `oxicloud_refresh` cookie (browsers)
|
||||||
|
///
|
||||||
|
/// Issues new access + refresh tokens and rotates all three auth cookies.
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/api/auth/refresh",
|
||||||
|
request_body(content = inline(RefreshTokenDto),
|
||||||
|
description = "Optional — omit when using the HttpOnly cookie"),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "New tokens issued", body = AuthResponseDto),
|
||||||
|
(status = 401, description = "Refresh token missing, expired, or revoked"),
|
||||||
|
),
|
||||||
|
tag = "auth"
|
||||||
|
)]
|
||||||
|
pub async fn refresh_token(
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
body: axum::body::Bytes,
|
body: axum::body::Bytes,
|
||||||
@@ -283,7 +328,18 @@ async fn refresh_token(
|
|||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_current_user(
|
/// Return the authenticated user's profile, including live storage usage.
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/auth/me",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Current user profile", body = UserDto),
|
||||||
|
(status = 401, description = "Not authenticated"),
|
||||||
|
),
|
||||||
|
security(("bearerAuth" = [])),
|
||||||
|
tag = "auth"
|
||||||
|
)]
|
||||||
|
pub async fn get_current_user(
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
CurrentUserId(user_id): CurrentUserId,
|
CurrentUserId(user_id): CurrentUserId,
|
||||||
) -> Result<impl IntoResponse, AppError> {
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
@@ -330,7 +386,20 @@ pub struct UpdateUserImageDto {
|
|||||||
pub image: Option<String>,
|
pub image: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn change_password(
|
/// Change the current user's password.
|
||||||
|
#[utoipa::path(
|
||||||
|
put,
|
||||||
|
path = "/api/auth/change-password",
|
||||||
|
request_body = ChangePasswordDto,
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Password changed successfully"),
|
||||||
|
(status = 400, description = "New password does not meet requirements"),
|
||||||
|
(status = 401, description = "Not authenticated or current password incorrect"),
|
||||||
|
),
|
||||||
|
security(("bearerAuth" = [])),
|
||||||
|
tag = "auth"
|
||||||
|
)]
|
||||||
|
pub async fn change_password(
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
CurrentUserId(user_id): CurrentUserId,
|
CurrentUserId(user_id): CurrentUserId,
|
||||||
Json(dto): Json<ChangePasswordDto>,
|
Json(dto): Json<ChangePasswordDto>,
|
||||||
@@ -348,6 +417,7 @@ async fn change_password(
|
|||||||
Ok(StatusCode::OK)
|
Ok(StatusCode::OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: add utoipa
|
||||||
pub async fn update_user_image(
|
pub async fn update_user_image(
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
CurrentUserId(user_id): CurrentUserId,
|
CurrentUserId(user_id): CurrentUserId,
|
||||||
@@ -371,7 +441,24 @@ pub async fn update_user_image(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn logout(
|
/// Revoke the current session and clear auth cookies.
|
||||||
|
///
|
||||||
|
/// Accepts the refresh token from **either** a JSON body
|
||||||
|
/// `{ "refresh_token": "..." }` (API clients) or the `oxicloud_refresh`
|
||||||
|
/// HttpOnly cookie (browsers).
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/api/auth/logout",
|
||||||
|
request_body(content = inline(RefreshTokenDto),
|
||||||
|
description = "Optional — omit when using the HttpOnly cookie"),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Logged out, auth cookies cleared"),
|
||||||
|
(status = 401, description = "Not authenticated or refresh token missing"),
|
||||||
|
),
|
||||||
|
security(("bearerAuth" = [])),
|
||||||
|
tag = "auth"
|
||||||
|
)]
|
||||||
|
pub async fn logout(
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
CurrentUserId(user_id): CurrentUserId,
|
CurrentUserId(user_id): CurrentUserId,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
@@ -405,16 +492,23 @@ async fn logout(
|
|||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// POST /api/setup — One-time endpoint to create the first admin user.
|
/// One-time endpoint to create the first admin user.
|
||||||
///
|
///
|
||||||
/// Available only when the system is not yet initialized (no admin exists).
|
/// Available only when the system is not yet initialized (no admin exists).
|
||||||
/// Once the admin is created, the system is marked as initialized and this
|
/// Once the admin is created the endpoint permanently returns 403.
|
||||||
/// endpoint returns 403 for all subsequent requests.
|
/// Uses an atomic "claim" operation so concurrent requests cannot both succeed.
|
||||||
///
|
#[utoipa::path(
|
||||||
/// Uses an atomic "claim" operation to prevent race conditions: even if two
|
post,
|
||||||
/// requests arrive simultaneously, only one will succeed in marking the
|
path = "/api/setup",
|
||||||
/// system as initialized and creating the admin.
|
request_body = SetupAdminDto,
|
||||||
async fn setup_admin(
|
responses(
|
||||||
|
(status = 201, description = "First admin created and system initialized", body = UserDto),
|
||||||
|
(status = 403, description = "System already initialized"),
|
||||||
|
(status = 503, description = "Auth service not configured"),
|
||||||
|
),
|
||||||
|
tag = "auth"
|
||||||
|
)]
|
||||||
|
pub async fn setup_admin(
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
Json(dto): Json<SetupAdminDto>,
|
Json(dto): Json<SetupAdminDto>,
|
||||||
) -> Result<impl IntoResponse, AppError> {
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
@@ -498,19 +592,28 @@ async fn setup_admin(
|
|||||||
Ok((StatusCode::CREATED, Json(user)))
|
Ok((StatusCode::CREATED, Json(user)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get system status - returns whether admin is configured
|
/// System initialisation state, returned by `GET /api/auth/status`.
|
||||||
/// This is a public endpoint used to determine if setup is needed
|
#[derive(serde::Serialize, ToSchema)]
|
||||||
#[derive(serde::Serialize)]
|
pub struct SystemStatus {
|
||||||
struct SystemStatus {
|
/// Whether the system has been set up with an admin.
|
||||||
/// Whether the system has been set up with an admin
|
|
||||||
initialized: bool,
|
initialized: bool,
|
||||||
/// Number of admin users in the system
|
/// Number of admin users in the system.
|
||||||
admin_count: i64,
|
admin_count: i64,
|
||||||
/// Whether registration is allowed (only if admin exists)
|
/// Whether self-registration is allowed.
|
||||||
registration_allowed: bool,
|
registration_allowed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_system_status(
|
/// Return the system initialisation state (used by the UI before setup).
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/auth/status",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "System status", body = SystemStatus),
|
||||||
|
(status = 503, description = "Auth service not configured"),
|
||||||
|
),
|
||||||
|
tag = "auth"
|
||||||
|
)]
|
||||||
|
pub async fn get_system_status(
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
) -> Result<impl IntoResponse, AppError> {
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
let auth_service = state
|
let auth_service = state
|
||||||
@@ -552,8 +655,19 @@ async fn get_system_status(
|
|||||||
// OIDC Handlers
|
// OIDC Handlers
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
/// GET /api/auth/oidc/providers — Returns OIDC provider info for the UI
|
/// Return OIDC provider information for the login UI.
|
||||||
async fn oidc_providers(State(state): State<Arc<AppState>>) -> Result<impl IntoResponse, AppError> {
|
///
|
||||||
|
/// Returns `enabled: false` when OIDC is not configured.
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/auth/oidc/providers",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "OIDC provider info (enabled=false when OIDC not configured)", body = OidcProviderInfoDto),
|
||||||
|
(status = 503, description = "Auth service not configured"),
|
||||||
|
),
|
||||||
|
tag = "auth"
|
||||||
|
)]
|
||||||
|
pub async fn oidc_providers(State(state): State<Arc<AppState>>) -> Result<impl IntoResponse, AppError> {
|
||||||
let auth_service = state
|
let auth_service = state
|
||||||
.auth_service
|
.auth_service
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -580,8 +694,21 @@ async fn oidc_providers(State(state): State<Arc<AppState>>) -> Result<impl IntoR
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GET /api/auth/oidc/authorize — Redirects user to the OIDC provider
|
/// Initiate OIDC authorization — redirects to the configured identity provider.
|
||||||
async fn oidc_authorize(State(state): State<Arc<AppState>>) -> Result<impl IntoResponse, AppError> {
|
///
|
||||||
|
/// Generates PKCE, CSRF state, and nonce then issues a 302 redirect to the
|
||||||
|
/// provider's authorization endpoint.
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/auth/oidc/authorize",
|
||||||
|
responses(
|
||||||
|
(status = 302, description = "Redirect to OIDC provider authorization URL"),
|
||||||
|
(status = 404, description = "OIDC not enabled"),
|
||||||
|
(status = 503, description = "Auth service not configured"),
|
||||||
|
),
|
||||||
|
tag = "auth"
|
||||||
|
)]
|
||||||
|
pub async fn oidc_authorize(State(state): State<Arc<AppState>>) -> Result<impl IntoResponse, AppError> {
|
||||||
let auth_service = state
|
let auth_service = state
|
||||||
.auth_service
|
.auth_service
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -605,8 +732,26 @@ async fn oidc_authorize(State(state): State<Arc<AppState>>) -> Result<impl IntoR
|
|||||||
Ok(Redirect::temporary(&authorize_url))
|
Ok(Redirect::temporary(&authorize_url))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GET /api/auth/oidc/callback?code=...&state=... — Handles OIDC callback
|
/// Handle the OIDC provider callback.
|
||||||
async fn oidc_callback(
|
///
|
||||||
|
/// Validates the `state` / PKCE / nonce, exchanges the code for tokens, then
|
||||||
|
/// redirects the browser to the frontend with a short-lived exchange code
|
||||||
|
/// (`/?oidc_code=…`).
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/auth/oidc/callback",
|
||||||
|
params(
|
||||||
|
("code" = String, Query, description = "Authorization code from the OIDC provider"),
|
||||||
|
("state" = String, Query, description = "CSRF state value echoed by the provider"),
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 302, description = "Redirect to frontend with one-time exchange code"),
|
||||||
|
(status = 401, description = "OIDC validation failed (bad state, nonce, or code)"),
|
||||||
|
(status = 404, description = "OIDC not enabled"),
|
||||||
|
),
|
||||||
|
tag = "auth"
|
||||||
|
)]
|
||||||
|
pub async fn oidc_callback(
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
Query(query): Query<OidcCallbackQueryDto>,
|
Query(query): Query<OidcCallbackQueryDto>,
|
||||||
) -> Result<impl IntoResponse, AppError> {
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
@@ -694,9 +839,21 @@ async fn oidc_callback(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// POST /api/auth/oidc/exchange — Exchange one-time code for auth tokens
|
/// Exchange a one-time OIDC code for access + refresh tokens.
|
||||||
/// Request body: { "code": "<one_time_code>" }
|
///
|
||||||
async fn oidc_exchange(
|
/// The frontend calls this after being redirected back with `?oidc_code=…`.
|
||||||
|
/// The exchange code is valid for a single use and expires in 60 s.
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/api/auth/oidc/exchange",
|
||||||
|
request_body = OidcExchangeDto,
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Tokens issued, auth cookies set", body = AuthResponseDto),
|
||||||
|
(status = 401, description = "Exchange code invalid or expired"),
|
||||||
|
),
|
||||||
|
tag = "auth"
|
||||||
|
)]
|
||||||
|
pub async fn oidc_exchange(
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
Json(body): Json<OidcExchangeDto>,
|
Json(body): Json<OidcExchangeDto>,
|
||||||
) -> Result<Response, AppError> {
|
) -> Result<Response, AppError> {
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ pub use routes::create_api_routes;
|
|||||||
pub use routes::create_health_routes;
|
pub use routes::create_health_routes;
|
||||||
pub use routes::create_public_api_routes;
|
pub use routes::create_public_api_routes;
|
||||||
|
|
||||||
use utoipa::OpenApi;
|
use utoipa::{Modify, OpenApi};
|
||||||
|
use utoipa::openapi::security::{Http, HttpAuthScheme, SecurityScheme};
|
||||||
|
|
||||||
use crate::application::dtos::contact_dto::{
|
use crate::application::dtos::contact_dto::{
|
||||||
AddressDto, ContactDto, ContactGroupDto, EmailDto, PhoneDto,
|
AddressDto, ContactDto, ContactGroupDto, EmailDto, PhoneDto,
|
||||||
@@ -40,8 +41,8 @@ use crate::application::dtos::trash_dto::{
|
|||||||
DeletePermanentlyRequest, MoveToTrashRequest, RestoreFromTrashRequest, TrashedItemDto,
|
DeletePermanentlyRequest, MoveToTrashRequest, RestoreFromTrashRequest, TrashedItemDto,
|
||||||
};
|
};
|
||||||
use crate::application::dtos::user_dto::{
|
use crate::application::dtos::user_dto::{
|
||||||
AuthResponseDto, ChangePasswordDto, LoginDto, RefreshTokenDto, RegisterDto, SetupAdminDto,
|
AuthResponseDto, ChangePasswordDto, LoginDto, OidcExchangeDto, OidcProviderInfoDto,
|
||||||
UserDto,
|
RefreshTokenDto, RegisterDto, SetupAdminDto, UserDto,
|
||||||
};
|
};
|
||||||
use crate::application::ports::chunked_upload_ports::{
|
use crate::application::ports::chunked_upload_ports::{
|
||||||
ChunkUploadResponseDto, CreateUploadResponseDto, UploadStatusResponseDto,
|
ChunkUploadResponseDto, CreateUploadResponseDto, UploadStatusResponseDto,
|
||||||
@@ -56,11 +57,26 @@ use crate::interfaces::api::handlers::contacts_handler::{
|
|||||||
use crate::interfaces::api::handlers::dedup_handler::{
|
use crate::interfaces::api::handlers::dedup_handler::{
|
||||||
DedupUploadResponse, HashCheckResponse, StatsResponse,
|
DedupUploadResponse, HashCheckResponse, StatsResponse,
|
||||||
};
|
};
|
||||||
|
use crate::interfaces::api::handlers::auth_handler::SystemStatus;
|
||||||
use crate::interfaces::api::handlers::file_handler::MoveFilePayload;
|
use crate::interfaces::api::handlers::file_handler::MoveFilePayload;
|
||||||
|
|
||||||
#[derive(OpenApi)]
|
#[derive(OpenApi)]
|
||||||
#[openapi(
|
#[openapi(
|
||||||
|
modifiers(&SecurityAddon),
|
||||||
paths(
|
paths(
|
||||||
|
// Auth handlers (public, protected, OIDC)
|
||||||
|
handlers::auth_handler::register,
|
||||||
|
handlers::auth_handler::login,
|
||||||
|
handlers::auth_handler::refresh_token,
|
||||||
|
handlers::auth_handler::get_current_user,
|
||||||
|
handlers::auth_handler::change_password,
|
||||||
|
handlers::auth_handler::logout,
|
||||||
|
handlers::auth_handler::setup_admin,
|
||||||
|
handlers::auth_handler::get_system_status,
|
||||||
|
handlers::auth_handler::oidc_providers,
|
||||||
|
handlers::auth_handler::oidc_authorize,
|
||||||
|
handlers::auth_handler::oidc_callback,
|
||||||
|
handlers::auth_handler::oidc_exchange,
|
||||||
// File handlers (free functions — see file_handler.rs for why)
|
// File handlers (free functions — see file_handler.rs for why)
|
||||||
handlers::file_handler::list_files_query,
|
handlers::file_handler::list_files_query,
|
||||||
handlers::file_handler::upload_file_with_thumbnails,
|
handlers::file_handler::upload_file_with_thumbnails,
|
||||||
@@ -236,6 +252,9 @@ use crate::interfaces::api::handlers::file_handler::MoveFilePayload;
|
|||||||
AuthResponseDto,
|
AuthResponseDto,
|
||||||
ChangePasswordDto,
|
ChangePasswordDto,
|
||||||
RefreshTokenDto,
|
RefreshTokenDto,
|
||||||
|
SystemStatus,
|
||||||
|
OidcProviderInfoDto,
|
||||||
|
OidcExchangeDto,
|
||||||
// Share schemas
|
// Share schemas
|
||||||
ShareDto,
|
ShareDto,
|
||||||
SharePermissionsDto,
|
SharePermissionsDto,
|
||||||
@@ -302,6 +321,7 @@ use crate::interfaces::api::handlers::file_handler::MoveFilePayload;
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
tags(
|
tags(
|
||||||
|
(name = "auth", description = "Authentication and session management endpoints"),
|
||||||
(name = "files", description = "File management endpoints"),
|
(name = "files", description = "File management endpoints"),
|
||||||
(name = "folders", description = "Folder management endpoints"),
|
(name = "folders", description = "Folder management endpoints"),
|
||||||
(name = "trash", description = "Trash / recycle bin endpoints"),
|
(name = "trash", description = "Trash / recycle bin endpoints"),
|
||||||
@@ -328,6 +348,19 @@ use crate::interfaces::api::handlers::file_handler::MoveFilePayload;
|
|||||||
)]
|
)]
|
||||||
pub struct ApiDoc;
|
pub struct ApiDoc;
|
||||||
|
|
||||||
|
/// Injects the `bearerAuth` HTTP Bearer security scheme into the generated spec.
|
||||||
|
struct SecurityAddon;
|
||||||
|
|
||||||
|
impl Modify for SecurityAddon {
|
||||||
|
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
|
||||||
|
let components = openapi.components.get_or_insert_with(Default::default);
|
||||||
|
components.add_security_scheme(
|
||||||
|
"bearerAuth",
|
||||||
|
SecurityScheme::Http(Http::new(HttpAuthScheme::Bearer)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user