bucketwarden-server 0.1.0

BucketWarden storage server runtime.
Documentation
use super::*;

const BROWSER_UI_BOUNDARY_ID: &str = "bnd:bucketwarden.browser-ui-full-product-scope";
const BROWSER_UI_CLAIM_TIER: &str = "T3";
const SESSION_DURATION_SECONDS: u64 = 3600;

mod catalog;
mod features;
mod foundation;

pub use bucketwarden_browser_ui_shell::{
    browser_ui_accessibility_checks, browser_ui_action_filter_controls, browser_ui_api_endpoints,
    browser_ui_responsive_breakpoints, browser_ui_routes, browser_ui_secondary_inspector_regions,
    browser_ui_security_policy, browser_ui_sidebar_order, browser_ui_topbar_context_order,
    BrowserUiRoute, BrowserUiSecurityPolicy,
};
use catalog::{browser_ui_assets, browser_ui_feature_statuses, browser_ui_product_e2e_workflows};
pub use catalog::{
    browser_ui_css, browser_ui_html, browser_ui_js, browser_ui_manifest_json, browser_ui_v1_html,
};
pub use foundation::*;

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct BrowserUiFeatureStatus {
    pub feature_id: String,
    pub title: String,
    pub category: String,
    pub runtime_surface: String,
    pub implementation_status: String,
    pub claim_tier: String,
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct BrowserUiAsset {
    pub path: String,
    pub content_type: String,
    pub bytes: usize,
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct BrowserUiProductReport {
    pub boundary_id: String,
    pub claim_tier: String,
    pub implementation_status: String,
    pub generated_at_epoch_seconds: u64,
    pub features: Vec<BrowserUiFeatureStatus>,
    pub routes: Vec<BrowserUiRoute>,
    pub api_endpoints: Vec<String>,
    pub assets: Vec<BrowserUiAsset>,
    pub security: BrowserUiSecurityPolicy,
    pub accessibility_checks: Vec<String>,
    pub responsive_breakpoints: Vec<String>,
    pub preference_keys: Vec<String>,
    pub export_actions: Vec<String>,
    pub e2e_workflows: Vec<String>,
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct BrowserUiLoginRequest {
    pub principal_id: String,
    pub shared_secret: String,
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct BrowserUiSession {
    pub principal_id: String,
    pub access_key_id: String,
    pub session_token: String,
    pub expires_at_epoch_seconds: u64,
    pub role_count: usize,
    pub scope: String,
}

impl BucketWarden {
    pub fn browser_ui_product_report(
        &mut self,
        principal: &str,
    ) -> Result<BrowserUiProductReport, RuntimeError> {
        self.require_operator_action(
            principal,
            OperatorAction::ReadDiagnostics,
            "*",
            "ui:BrowserProductReport",
        )?;
        let report = BrowserUiProductReport {
            boundary_id: BROWSER_UI_BOUNDARY_ID.to_string(),
            claim_tier: BROWSER_UI_CLAIM_TIER.to_string(),
            implementation_status: "implemented".to_string(),
            generated_at_epoch_seconds: self.clock_epoch_seconds,
            features: browser_ui_feature_statuses(),
            routes: browser_ui_routes(),
            api_endpoints: browser_ui_api_endpoints(),
            assets: browser_ui_assets(),
            security: browser_ui_security_policy(),
            accessibility_checks: browser_ui_accessibility_checks(),
            responsive_breakpoints: browser_ui_responsive_breakpoints(),
            preference_keys: vec![
                "bucketwarden.ui.density".to_string(),
                "bucketwarden.ui.visibleColumns".to_string(),
                "bucketwarden.ui.reportFilters".to_string(),
                "bucketwarden.ui.refreshSeconds".to_string(),
            ],
            export_actions: vec![
                "download health report JSON".to_string(),
                "download config report JSON".to_string(),
                "download evidence export JSON".to_string(),
                "download audit log JSONL".to_string(),
            ],
            e2e_workflows: browser_ui_product_e2e_workflows(),
        };
        self.audit.append(
            principal,
            "ui:BrowserProductReport",
            "*",
            AuditOutcome::Allowed,
            Some(format!(
                "features={},routes={},assets={}",
                report.features.len(),
                report.routes.len(),
                report.assets.len()
            )),
        );
        Ok(report)
    }

    pub fn browser_ui_login(
        &mut self,
        request: BrowserUiLoginRequest,
    ) -> Result<BrowserUiSession, RuntimeError> {
        self.auth
            .enforce_login_attempt_limit(&request.principal_id, 5)?;
        let access_key_id = format!("BWUI-{}", sanitize_token_part(&request.principal_id));
        let session_token = format!(
            "bwui-session-{}-{}",
            sanitize_token_part(&request.principal_id),
            self.clock_epoch_seconds
        );
        let session = self.auth.assume_role_with_custom_identity(
            AssumeRoleWithCustomIdentityRequest::new(
                request.principal_id.clone(),
                request.shared_secret.clone(),
                SESSION_DURATION_SECONDS,
                CredentialScope::new(
                    vec!["ops:*".to_string(), "ui:*".to_string()],
                    vec!["*".to_string()],
                ),
                access_key_id.clone(),
                "browser-ui-session-secret",
                session_token.clone(),
            ),
            self.clock_epoch_seconds,
        );
        match session {
            Ok(session) => {
                self.auth
                    .record_login_success(&request.principal_id, self.clock_epoch_seconds);
                let role_count = self.auth.role_assignments(&request.principal_id).len();
                let session_token = session.credentials().session_token.unwrap_or_default();
                self.audit.append(
                    &request.principal_id,
                    "ui:BrowserLogin",
                    "*",
                    AuditOutcome::Allowed,
                    Some(format!("session={}", session.access_key_id)),
                );
                Ok(BrowserUiSession {
                    principal_id: session.principal_id,
                    access_key_id: session.access_key_id,
                    session_token,
                    expires_at_epoch_seconds: session.expires_at_epoch_seconds,
                    role_count,
                    scope: "*".to_string(),
                })
            }
            Err(error) => {
                self.auth.record_login_failure(
                    &request.principal_id,
                    self.clock_epoch_seconds,
                    error.to_string(),
                );
                self.audit.append(
                    &request.principal_id,
                    "ui:BrowserLogin",
                    "*",
                    AuditOutcome::Denied,
                    Some(error.to_string()),
                );
                Err(RuntimeError::Auth(error))
            }
        }
    }

    pub fn browser_ui_current_user(
        &self,
        access_key_id: &str,
    ) -> Result<BrowserUiSession, RuntimeError> {
        let credential = self
            .auth
            .resolve_credential(access_key_id, self.clock_epoch_seconds)?;
        Ok(BrowserUiSession {
            principal_id: credential.principal_id.clone(),
            access_key_id: credential.access_key_id,
            session_token: credential.credentials.session_token.unwrap_or_default(),
            expires_at_epoch_seconds: credential
                .scope
                .as_ref()
                .and_then(|_| self.auth.credential(access_key_id))
                .and_then(|record| match record {
                    CredentialRecord::Session(session) => Some(session.expires_at_epoch_seconds),
                    CredentialRecord::AccessKey(_) => None,
                })
                .unwrap_or(self.clock_epoch_seconds),
            role_count: self.auth.role_assignments(&credential.principal_id).len(),
            scope: credential
                .scope
                .as_ref()
                .map(|scope| scope.resource_prefixes.join(","))
                .unwrap_or_else(|| "*".to_string()),
        })
    }

    pub fn browser_ui_logout(&mut self, access_key_id: &str) -> Result<(), RuntimeError> {
        let principal = self
            .auth
            .resolve_credential(access_key_id, self.clock_epoch_seconds)?
            .principal_id;
        self.auth
            .revoke_credential(access_key_id, self.clock_epoch_seconds)?;
        self.audit.append(
            &principal,
            "ui:BrowserLogout",
            "*",
            AuditOutcome::Allowed,
            Some(format!("session={access_key_id}")),
        );
        Ok(())
    }
}

fn sanitize_token_part(value: &str) -> String {
    value
        .chars()
        .map(|ch| {
            if ch.is_ascii_alphanumeric() {
                ch.to_ascii_uppercase()
            } else {
                '-'
            }
        })
        .collect()
}

fn escape_html(value: &str) -> String {
    value
        .replace('&', "&amp;")
        .replace('<', "&lt;")
        .replace('>', "&gt;")
        .replace('"', "&quot;")
}