greentic_types/
tenant_config.rs

1//! Tenant-facing configuration document shapes (skin/auth/config/did).
2//!
3//! These structs mirror the JSON documents served to the Loveable UI. They intentionally avoid
4//! hard-coding UI navigation semantics (tabs, slots, etc.) to keep the types crate forward
5//! compatible.
6
7use alloc::collections::BTreeMap;
8use alloc::string::String;
9use alloc::vec::Vec;
10
11#[cfg(feature = "schemars")]
12use schemars::JsonSchema;
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15use serde_json::Value;
16
17/// Branding and layout configuration for a tenant (`skin.json`).
18#[derive(Clone, Debug, PartialEq, Eq)]
19#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
20#[cfg_attr(feature = "schemars", derive(JsonSchema))]
21pub struct RepoSkin {
22    /// Tenant identifier the skin belongs to.
23    pub tenant_id: String,
24    /// Optional human-readable tenant name.
25    #[cfg_attr(
26        feature = "serde",
27        serde(default, skip_serializing_if = "Option::is_none")
28    )]
29    pub tenant_name: Option<String>,
30    /// Optional product name to display.
31    #[cfg_attr(
32        feature = "serde",
33        serde(default, skip_serializing_if = "Option::is_none")
34    )]
35    pub product_name: Option<String>,
36    /// Theme configuration (colors, fonts, imagery).
37    pub theme: RepoSkinTheme,
38    /// Optional layout flags controlling navigation visibility and hero band.
39    #[cfg_attr(
40        feature = "serde",
41        serde(default, skip_serializing_if = "Option::is_none")
42    )]
43    pub layout: Option<RepoSkinLayout>,
44    /// Optional worker panel configuration.
45    #[cfg_attr(
46        feature = "serde",
47        serde(default, skip_serializing_if = "Option::is_none")
48    )]
49    pub worker_panel: Option<RepoWorkerPanel>,
50    /// Optional tenant links for docs/support/status.
51    #[cfg_attr(
52        feature = "serde",
53        serde(default, skip_serializing_if = "Option::is_none")
54    )]
55    pub links: Option<RepoSkinLinks>,
56}
57
58/// Theme settings for login and console surfaces.
59#[derive(Clone, Debug, PartialEq, Eq)]
60#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
61#[cfg_attr(feature = "schemars", derive(JsonSchema))]
62pub struct RepoSkinTheme {
63    /// Primary logo URL.
64    pub logo_url: String,
65    /// Optional favicon URL.
66    #[cfg_attr(
67        feature = "serde",
68        serde(default, skip_serializing_if = "Option::is_none")
69    )]
70    pub favicon_url: Option<String>,
71    /// Optional hero image URL.
72    #[cfg_attr(
73        feature = "serde",
74        serde(default, skip_serializing_if = "Option::is_none")
75    )]
76    pub hero_image_url: Option<String>,
77    /// Primary brand color.
78    pub primary_color: String,
79    /// Accent brand color.
80    pub accent_color: String,
81    /// Optional background color.
82    #[cfg_attr(
83        feature = "serde",
84        serde(default, skip_serializing_if = "Option::is_none")
85    )]
86    pub background_color: Option<String>,
87    /// Optional background gradient.
88    #[cfg_attr(
89        feature = "serde",
90        serde(default, skip_serializing_if = "Option::is_none")
91    )]
92    pub background_gradient: Option<String>,
93    /// Optional font family override.
94    #[cfg_attr(
95        feature = "serde",
96        serde(default, skip_serializing_if = "Option::is_none")
97    )]
98    pub font_family: Option<String>,
99    /// Optional success color override.
100    #[cfg_attr(
101        feature = "serde",
102        serde(default, skip_serializing_if = "Option::is_none")
103    )]
104    pub success_color: Option<String>,
105    /// Optional warning color override.
106    #[cfg_attr(
107        feature = "serde",
108        serde(default, skip_serializing_if = "Option::is_none")
109    )]
110    pub warning_color: Option<String>,
111    /// Optional danger color override.
112    #[cfg_attr(
113        feature = "serde",
114        serde(default, skip_serializing_if = "Option::is_none")
115    )]
116    pub danger_color: Option<String>,
117}
118
119/// Layout toggles describing visible console sections.
120#[derive(Clone, Debug, PartialEq, Eq, Default)]
121#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
122#[cfg_attr(feature = "serde", serde(default))]
123#[cfg_attr(feature = "schemars", derive(JsonSchema))]
124pub struct RepoSkinLayout {
125    /// Whether to show the dashboard tab.
126    pub show_dashboard: bool,
127    /// Whether to show the repositories tab.
128    pub show_repositories: bool,
129    /// Whether to show the pipeline tab.
130    pub show_pipeline: bool,
131    /// Whether to show the packs tab.
132    pub show_packs: bool,
133    /// Whether to show the trust & access tab.
134    pub show_trust_access: bool,
135    /// Whether to show the audit & compliance tab.
136    pub show_audit_compliance: bool,
137    /// Whether to show the admin/config tab.
138    pub show_admin_config: Option<bool>,
139    /// Whether to render the hero band.
140    pub show_hero_band: Option<bool>,
141    /// Optional hero title.
142    #[cfg_attr(
143        feature = "serde",
144        serde(default, skip_serializing_if = "Option::is_none")
145    )]
146    pub hero_title: Option<String>,
147    /// Optional hero subtitle.
148    #[cfg_attr(
149        feature = "serde",
150        serde(default, skip_serializing_if = "Option::is_none")
151    )]
152    pub hero_subtitle: Option<String>,
153}
154
155/// Worker panel placement and defaults.
156#[derive(Clone, Debug, PartialEq, Eq)]
157#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
158#[cfg_attr(feature = "schemars", derive(JsonSchema))]
159pub struct RepoWorkerPanel {
160    /// Whether the worker panel is enabled.
161    pub enabled: bool,
162    /// Optional display title.
163    #[cfg_attr(
164        feature = "serde",
165        serde(default, skip_serializing_if = "Option::is_none")
166    )]
167    pub title: Option<String>,
168    /// Whether the panel should open by default.
169    #[cfg_attr(
170        feature = "serde",
171        serde(default, skip_serializing_if = "Option::is_none")
172    )]
173    pub default_open: Option<bool>,
174    /// Preferred position (for example `left` or `right`).
175    #[cfg_attr(
176        feature = "serde",
177        serde(default, skip_serializing_if = "Option::is_none")
178    )]
179    pub position: Option<String>,
180}
181
182/// Optional tenant links for navigation.
183#[derive(Clone, Debug, PartialEq, Eq, Default)]
184#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
185#[cfg_attr(feature = "serde", serde(default))]
186#[cfg_attr(feature = "schemars", derive(JsonSchema))]
187pub struct RepoSkinLinks {
188    /// Optional documentation URL.
189    pub docs_url: Option<String>,
190    /// Optional support URL.
191    pub support_url: Option<String>,
192    /// Optional status page URL.
193    pub status_url: Option<String>,
194}
195
196/// Login options for a tenant (`auth.json`).
197#[derive(Clone, Debug, PartialEq, Eq)]
198#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
199#[cfg_attr(feature = "schemars", derive(JsonSchema))]
200pub struct RepoAuth {
201    /// Tenant identifier the auth config belongs to.
202    pub tenant_id: String,
203    /// Available identity providers.
204    #[cfg_attr(
205        feature = "serde",
206        serde(default, skip_serializing_if = "Vec::is_empty")
207    )]
208    pub identity_providers: Vec<IdentityProviderOption>,
209}
210
211/// Identity provider option shown on the login page.
212#[derive(Clone, Debug, PartialEq, Eq)]
213#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
214#[cfg_attr(feature = "schemars", derive(JsonSchema))]
215pub struct IdentityProviderOption {
216    /// Provider pack identifier.
217    pub id: String,
218    /// Optional kind hint (for example `identity-provider`).
219    #[cfg_attr(
220        feature = "serde",
221        serde(default, skip_serializing_if = "Option::is_none")
222    )]
223    pub kind: Option<String>,
224    /// Button label.
225    pub label: String,
226    /// Optional icon identifier.
227    #[cfg_attr(
228        feature = "serde",
229        serde(default, skip_serializing_if = "Option::is_none")
230    )]
231    pub icon: Option<String>,
232    /// Optional button style.
233    #[cfg_attr(
234        feature = "serde",
235        serde(default, skip_serializing_if = "Option::is_none")
236    )]
237    pub button_style: Option<String>,
238    /// Optional ordering hint.
239    #[cfg_attr(
240        feature = "serde",
241        serde(default, skip_serializing_if = "Option::is_none")
242    )]
243    pub order: Option<i32>,
244    /// Login URL for the provider.
245    pub login_url: String,
246    /// Optional description text.
247    #[cfg_attr(
248        feature = "serde",
249        serde(default, skip_serializing_if = "Option::is_none")
250    )]
251    pub description: Option<String>,
252    /// Whether the provider is recommended.
253    #[cfg_attr(
254        feature = "serde",
255        serde(default, skip_serializing_if = "Option::is_none")
256    )]
257    pub recommended: Option<bool>,
258}
259
260/// Tenant UI configuration exposed to the console (`config.json`).
261#[derive(Clone, Debug, PartialEq, Eq)]
262#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
263#[cfg_attr(feature = "schemars", derive(JsonSchema))]
264pub struct RepoTenantConfig {
265    /// Tenant identifier the config belongs to.
266    pub tenant_id: String,
267    /// Structural flags: active sections for the UI. Values are conventions, not enforced here.
268    #[cfg_attr(
269        feature = "serde",
270        serde(default, skip_serializing_if = "Vec::is_empty")
271    )]
272    pub enabled_tabs: Vec<String>,
273    /// Enabled packs grouped by kind.
274    pub enabled_packs: EnabledPacks,
275    /// Default pipeline choices.
276    #[cfg_attr(
277        feature = "serde",
278        serde(default, skip_serializing_if = "Option::is_none")
279    )]
280    pub default_pipeline: Option<DefaultPipeline>,
281    /// Optional configured stores.
282    #[cfg_attr(
283        feature = "serde",
284        serde(default, skip_serializing_if = "Option::is_none")
285    )]
286    pub stores: Option<Vec<StoreTarget>>,
287    /// Optional configured distributors.
288    #[cfg_attr(
289        feature = "serde",
290        serde(default, skip_serializing_if = "Option::is_none")
291    )]
292    pub distributors: Option<Vec<DistributorTarget>>,
293    /// Feature flags for the UI.
294    #[cfg_attr(
295        feature = "serde",
296        serde(default, skip_serializing_if = "Option::is_none")
297    )]
298    pub features: Option<RepoConfigFeatures>,
299    /// Maps page slots to UI action handler pack identifiers.
300    #[cfg_attr(
301        feature = "serde",
302        serde(default, skip_serializing_if = "Option::is_none")
303    )]
304    pub page_handlers: Option<BTreeMap<String, String>>,
305}
306
307/// Enabled packs grouped by capability.
308#[derive(Clone, Debug, PartialEq, Eq, Default)]
309#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
310#[cfg_attr(feature = "serde", serde(default))]
311#[cfg_attr(feature = "schemars", derive(JsonSchema))]
312pub struct EnabledPacks {
313    /// Identity provider pack identifiers.
314    pub identity_providers: Option<Vec<String>>,
315    /// Source provider pack identifiers.
316    pub source_providers: Option<Vec<String>>,
317    /// Scanner pack identifiers.
318    pub scanners: Option<Vec<String>>,
319    /// Signing pack identifiers.
320    pub signing: Option<Vec<String>>,
321    /// Attestation pack identifiers.
322    pub attestation: Option<Vec<String>>,
323    /// Policy engine pack identifiers.
324    pub policy_engines: Option<Vec<String>>,
325    /// OCI provider pack identifiers.
326    pub oci_providers: Option<Vec<String>>,
327}
328
329/// Default pipeline selections per capability.
330#[derive(Clone, Debug, PartialEq, Eq, Default)]
331#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
332#[cfg_attr(feature = "serde", serde(default))]
333#[cfg_attr(feature = "schemars", derive(JsonSchema))]
334pub struct DefaultPipeline {
335    /// Default scanners to run.
336    pub scanners: Option<Vec<String>>,
337    /// Default signing provider identifier.
338    pub signing: Option<String>,
339    /// Default attestation provider identifier.
340    pub attestation: Option<String>,
341    /// Default policy engine identifier.
342    pub policy_engine: Option<String>,
343    /// Default OCI provider identifier.
344    pub oci_provider: Option<String>,
345}
346
347/// Public store target descriptor.
348#[derive(Clone, Debug, PartialEq, Eq)]
349#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
350#[cfg_attr(feature = "schemars", derive(JsonSchema))]
351pub struct StoreTarget {
352    /// Store identifier.
353    pub id: String,
354    /// Human-readable label.
355    pub label: String,
356    /// Store URL.
357    pub url: String,
358    /// Optional description.
359    #[cfg_attr(
360        feature = "serde",
361        serde(default, skip_serializing_if = "Option::is_none")
362    )]
363    pub description: Option<String>,
364}
365
366/// Public distributor target descriptor.
367#[derive(Clone, Debug, PartialEq, Eq)]
368#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
369#[cfg_attr(feature = "schemars", derive(JsonSchema))]
370pub struct DistributorTarget {
371    /// Distributor identifier.
372    pub id: String,
373    /// Human-readable label.
374    pub label: String,
375    /// Distributor URL.
376    pub url: String,
377    /// Optional description.
378    #[cfg_attr(
379        feature = "serde",
380        serde(default, skip_serializing_if = "Option::is_none")
381    )]
382    pub description: Option<String>,
383}
384
385/// Feature flags for the UI.
386#[derive(Clone, Debug, PartialEq, Eq, Default)]
387#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
388#[cfg_attr(feature = "serde", serde(default))]
389#[cfg_attr(feature = "schemars", derive(JsonSchema))]
390pub struct RepoConfigFeatures {
391    /// Whether manual approvals are allowed.
392    pub allow_manual_approve: Option<bool>,
393    /// Whether to show advanced scan views.
394    pub show_advanced_scan_views: Option<bool>,
395    /// Whether to surface experimental modules.
396    pub show_experimental_modules: Option<bool>,
397}
398
399/// DID document used for tenant discovery (`did.json`).
400#[derive(Clone, Debug, PartialEq)]
401#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
402#[cfg_attr(feature = "schemars", derive(JsonSchema))]
403pub struct TenantDidDocument {
404    /// Raw @context, preserved as provided.
405    #[cfg_attr(feature = "serde", serde(rename = "@context", default))]
406    pub raw_context: Option<DidContext>,
407    /// Document identifier (did:web).
408    pub id: String,
409    /// Verification methods (optional).
410    #[cfg_attr(
411        feature = "serde",
412        serde(
413            rename = "verificationMethod",
414            default,
415            skip_serializing_if = "Option::is_none"
416        )
417    )]
418    pub verification_method: Option<Vec<VerificationMethod>>,
419    /// Authentication references (optional).
420    #[cfg_attr(
421        feature = "serde",
422        serde(default, skip_serializing_if = "Option::is_none")
423    )]
424    pub authentication: Option<Vec<String>>,
425    /// Service endpoints.
426    #[cfg_attr(
427        feature = "serde",
428        serde(default, skip_serializing_if = "Vec::is_empty")
429    )]
430    pub service: Vec<DidService>,
431}
432
433impl TenantDidDocument {
434    /// Returns the normalized `@context` value as a vector of strings.
435    pub fn context(&self) -> Vec<String> {
436        match &self.raw_context {
437            Some(DidContext::Single(value)) => alloc::vec![value.clone()],
438            Some(DidContext::Multiple(values)) => values.clone(),
439            None => Vec::new(),
440        }
441    }
442}
443
444/// @context representation supporting single string or array.
445#[derive(Clone, Debug, PartialEq)]
446#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
447#[cfg_attr(feature = "serde", serde(untagged))]
448#[cfg_attr(feature = "schemars", derive(JsonSchema))]
449pub enum DidContext {
450    /// Single string context.
451    Single(String),
452    /// Array of contexts.
453    Multiple(Vec<String>),
454}
455
456/// Verification method descriptor within a DID document.
457#[derive(Clone, Debug, PartialEq)]
458#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
459#[cfg_attr(feature = "schemars", derive(JsonSchema))]
460pub struct VerificationMethod {
461    /// Identifier for the verification method.
462    pub id: String,
463    /// Type of verification method.
464    #[cfg_attr(feature = "serde", serde(rename = "type"))]
465    pub r#type: String,
466    /// Controller of the verification method.
467    pub controller: String,
468    /// Optional JWK.
469    #[cfg_attr(
470        feature = "serde",
471        serde(
472            rename = "publicKeyJwk",
473            default,
474            skip_serializing_if = "Option::is_none"
475        )
476    )]
477    pub public_key_jwk: Option<Value>,
478    /// Optional multibase key.
479    #[cfg_attr(
480        feature = "serde",
481        serde(
482            rename = "publicKeyMultibase",
483            default,
484            skip_serializing_if = "Option::is_none"
485        )
486    )]
487    pub public_key_multibase: Option<String>,
488}
489
490/// Service endpoint descriptor within a DID document.
491#[derive(Clone, Debug, PartialEq, Eq)]
492#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
493#[cfg_attr(feature = "schemars", derive(JsonSchema))]
494pub struct DidService {
495    /// Service identifier.
496    pub id: String,
497    /// Service type.
498    #[cfg_attr(feature = "serde", serde(rename = "type"))]
499    pub r#type: String,
500    /// Service endpoint URL.
501    #[cfg_attr(feature = "serde", serde(rename = "serviceEndpoint"))]
502    pub service_endpoint: String,
503}