Skip to main content

coil_auth/capability/
defaults.rs

1use std::collections::HashMap;
2
3use zanzibar::Schema;
4
5use crate::{Namespace, Relation, default_schema};
6
7use super::*;
8
9#[derive(Debug, Clone)]
10pub struct DefaultAuthModelPackage {
11    manifest: AuthModelManifest,
12    schema: Schema,
13    capability_bindings: HashMap<Capability, CapabilityBinding>,
14}
15
16impl Default for DefaultAuthModelPackage {
17    fn default() -> Self {
18        Self::new()
19    }
20}
21
22impl DefaultAuthModelPackage {
23    pub fn new() -> Self {
24        Self {
25            manifest: default_manifest(),
26            schema: default_schema(),
27            capability_bindings: default_capability_bindings(),
28        }
29    }
30}
31
32impl AuthModelPackage for DefaultAuthModelPackage {
33    fn manifest(&self) -> &AuthModelManifest {
34        &self.manifest
35    }
36
37    fn schema(&self) -> &Schema {
38        &self.schema
39    }
40
41    fn capability_bindings(&self) -> &HashMap<Capability, CapabilityBinding> {
42        &self.capability_bindings
43    }
44}
45
46#[derive(Debug, Clone)]
47pub struct ConfiguredAuthModelPackage {
48    manifest: AuthModelManifest,
49    schema: Schema,
50    capability_bindings: HashMap<Capability, CapabilityBinding>,
51}
52
53impl ConfiguredAuthModelPackage {
54    /// Build a package selection that keeps the configured identity but reuses the
55    /// shipped default schema and capability bindings.
56    pub fn new(name: impl Into<String>) -> Self {
57        let mut manifest = default_manifest();
58        manifest.name = name.into();
59        Self {
60            manifest,
61            schema: default_schema(),
62            capability_bindings: default_capability_bindings(),
63        }
64    }
65}
66
67impl AuthModelPackage for ConfiguredAuthModelPackage {
68    fn manifest(&self) -> &AuthModelManifest {
69        &self.manifest
70    }
71
72    fn schema(&self) -> &Schema {
73        &self.schema
74    }
75
76    fn capability_bindings(&self) -> &HashMap<Capability, CapabilityBinding> {
77        &self.capability_bindings
78    }
79}
80
81pub fn default_auth_model_package() -> DefaultAuthModelPackage {
82    DefaultAuthModelPackage::default()
83}
84
85pub fn configured_auth_model_package(name: impl Into<String>) -> ConfiguredAuthModelPackage {
86    ConfiguredAuthModelPackage::new(name)
87}
88
89/// Build an auth package selection from the deployment-configured package identity.
90///
91/// This preserves the configured manifest name while reusing the shipped default
92/// schema and capability bindings so replacement packages remain live-explainable.
93pub fn configured_auth_model_package_selection(
94    name: impl Into<String>,
95) -> AuthModelPackageSelection {
96    AuthModelPackageSelection::new(configured_auth_model_package(name))
97}
98
99/// Compatibility alias for callers that already refer to the deployment-selected
100/// package identity.
101pub fn deployment_auth_model_package(name: impl Into<String>) -> ConfiguredAuthModelPackage {
102    ConfiguredAuthModelPackage::new(name)
103}
104
105/// Compatibility alias for callers that already refer to the deployment-selected
106/// package identity.
107pub fn deployment_auth_model_package_selection(
108    name: impl Into<String>,
109) -> AuthModelPackageSelection {
110    configured_auth_model_package_selection(name)
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116    use crate::{AuthModelPackage, Capability};
117
118    #[test]
119    fn configured_package_selection_preserves_identity_and_bindings() {
120        let package = configured_auth_model_package_selection("coil-extended-auth");
121
122        assert_eq!(package.manifest().name, "coil-extended-auth");
123        assert_eq!(
124            package
125                .package()
126                .binding_for(Capability::CmsPageRead)
127                .unwrap(),
128            DefaultAuthModelPackage::default()
129                .binding_for(Capability::CmsPageRead)
130                .unwrap()
131        );
132    }
133
134    #[test]
135    fn deployment_package_preserves_identity_while_reusing_default_bindings() {
136        let package = deployment_auth_model_package("coil-extended-auth");
137
138        assert_eq!(package.manifest().name, "coil-extended-auth");
139        assert_eq!(
140            package.binding_for(Capability::CmsPageRead).unwrap(),
141            DefaultAuthModelPackage::default()
142                .binding_for(Capability::CmsPageRead)
143                .unwrap()
144        );
145    }
146}
147
148pub fn default_manifest() -> AuthModelManifest {
149    AuthModelManifest {
150        name: "coil-default-auth".to_string(),
151        version: PackageVersion::new(1, 0, 0),
152        mode: PackageMode::Replace,
153        storage_schema_version: 1,
154        model_version: 1,
155        capability_binding_version: 1,
156        imports: Vec::new(),
157    }
158}
159
160pub fn default_capability_bindings() -> HashMap<Capability, CapabilityBinding> {
161    HashMap::from([
162        binding(
163            Capability::SystemModuleManage,
164            vec![Namespace::Tenant],
165            Relation::Manage,
166        ),
167        binding(
168            Capability::SystemConfigRead,
169            vec![Namespace::Tenant],
170            Relation::View,
171        ),
172        binding(
173            Capability::SystemConfigWrite,
174            vec![Namespace::Tenant],
175            Relation::Manage,
176        ),
177        binding(
178            Capability::AdminShellAccess,
179            vec![Namespace::AdminModule],
180            Relation::View,
181        ),
182        binding(
183            Capability::AdminAuditRead,
184            vec![Namespace::AdminModule],
185            Relation::Read,
186        ),
187        binding(
188            Capability::CmsPageRead,
189            vec![Namespace::Page],
190            Relation::View,
191        ),
192        binding(
193            Capability::CmsPagePublish,
194            vec![Namespace::Page],
195            Relation::Publish,
196        ),
197        binding(
198            Capability::CmsPageEdit,
199            vec![Namespace::Page],
200            Relation::Edit,
201        ),
202        binding(
203            Capability::CmsNavigationEdit,
204            vec![Namespace::Navigation],
205            Relation::Edit,
206        ),
207        binding(
208            Capability::CatalogProductRead,
209            vec![Namespace::Product],
210            Relation::View,
211        ),
212        binding(
213            Capability::CatalogProductEdit,
214            vec![Namespace::Product],
215            Relation::Edit,
216        ),
217        binding(
218            Capability::CatalogCollectionEdit,
219            vec![Namespace::Collection],
220            Relation::Edit,
221        ),
222        binding(
223            Capability::CheckoutSessionCreate,
224            vec![Namespace::Storefront],
225            Relation::Checkout,
226        ),
227        binding(
228            Capability::OrderRead,
229            vec![Namespace::Order],
230            Relation::View,
231        ),
232        binding(
233            Capability::OrderRefundIssue,
234            vec![Namespace::Order],
235            Relation::Refund,
236        ),
237        binding(
238            Capability::MembershipSubscriptionManage,
239            vec![Namespace::Subscription],
240            Relation::Manage,
241        ),
242        binding(
243            Capability::MembershipTierEdit,
244            vec![Namespace::MembershipTier],
245            Relation::Edit,
246        ),
247        binding(
248            Capability::EventsEventPublish,
249            vec![Namespace::Event],
250            Relation::Publish,
251        ),
252        binding(
253            Capability::EventsSlotManage,
254            vec![Namespace::EventSlot],
255            Relation::Manage,
256        ),
257        binding(
258            Capability::EventsBookingManage,
259            vec![Namespace::Booking],
260            Relation::Manage,
261        ),
262        binding(
263            Capability::EventsBookingCreate,
264            vec![Namespace::EventSlot],
265            Relation::Book,
266        ),
267        binding(
268            Capability::EventsBookingCheckIn,
269            vec![Namespace::Booking],
270            Relation::CheckIn,
271        ),
272        binding(
273            Capability::AssetRead,
274            vec![Namespace::Asset],
275            Relation::Read,
276        ),
277        binding(
278            Capability::AssetReadPublic,
279            vec![Namespace::Asset],
280            Relation::ReadPublic,
281        ),
282        binding(
283            Capability::AssetPublish,
284            vec![Namespace::Asset],
285            Relation::Publish,
286        ),
287        binding(
288            Capability::AssetReplace,
289            vec![Namespace::Asset],
290            Relation::Replace,
291        ),
292        binding(
293            Capability::AssetManageStorage,
294            vec![Namespace::Asset],
295            Relation::ManageStorage,
296        ),
297        binding(
298            Capability::SeoMetadataEdit,
299            vec![Namespace::Page, Namespace::Product, Namespace::Event],
300            Relation::Edit,
301        ),
302        binding(
303            Capability::I18nTranslationEdit,
304            vec![
305                Namespace::Page,
306                Namespace::Navigation,
307                Namespace::Product,
308                Namespace::MembershipTier,
309                Namespace::Event,
310            ],
311            Relation::Edit,
312        ),
313    ])
314}
315
316fn binding(
317    capability: Capability,
318    resource_namespaces: Vec<Namespace>,
319    relation: Relation,
320) -> (Capability, CapabilityBinding) {
321    (
322        capability,
323        CapabilityBinding {
324            capability,
325            resource_namespaces,
326            relation,
327        },
328    )
329}