Skip to main content

tr_authz_plugin/
module.rs

1//! TR `AuthZ` resolver plugin module.
2
3use std::sync::{Arc, OnceLock};
4
5use async_trait::async_trait;
6use authz_resolver_sdk::{AuthZResolverPluginClient, AuthZResolverPluginSpecV1};
7use modkit::Module;
8use modkit::client_hub::ClientScope;
9use modkit::context::ModuleCtx;
10use modkit::gts::BaseModkitPluginV1;
11use tenant_resolver_sdk::TenantResolverClient;
12use tracing::info;
13use types_registry_sdk::{RegisterResult, TypesRegistryClient};
14
15use crate::config::TrAuthZPluginConfig;
16use crate::domain::Service;
17
18/// TR-based `AuthZ` resolver plugin module.
19///
20/// Resolves tenant hierarchy via `TenantResolverClient` instead of
21/// accessing Resource Group directly.
22#[modkit::module(
23    name = "tr-authz-plugin",
24    deps = ["types-registry", "tenant-resolver"]
25)]
26pub struct TrAuthZPlugin {
27    service: OnceLock<Arc<Service>>,
28}
29
30impl Default for TrAuthZPlugin {
31    fn default() -> Self {
32        Self {
33            service: OnceLock::new(),
34        }
35    }
36}
37
38#[async_trait]
39impl Module for TrAuthZPlugin {
40    async fn init(&self, ctx: &ModuleCtx) -> anyhow::Result<()> {
41        let cfg: TrAuthZPluginConfig = ctx.config_or_default()?;
42        info!(
43            vendor = %cfg.vendor,
44            priority = cfg.priority,
45            "Loaded TR AuthZ plugin configuration"
46        );
47
48        // Generate plugin instance ID
49        let instance_id = AuthZResolverPluginSpecV1::gts_make_instance_id(
50            "hyperspot.builtin.tr_authz_resolver.plugin.v1",
51        );
52
53        // Resolve Tenant Resolver client from ClientHub first — if it's not
54        // available we want to fail before leaving a dangling instance in
55        // the types-registry.
56        let tr: Arc<dyn TenantResolverClient> =
57            ctx.client_hub().get::<dyn TenantResolverClient>()?;
58
59        // Register plugin instance in types-registry
60        let registry = ctx.client_hub().get::<dyn TypesRegistryClient>()?;
61        let instance = BaseModkitPluginV1::<AuthZResolverPluginSpecV1> {
62            id: instance_id.clone(),
63            vendor: cfg.vendor.clone(),
64            priority: cfg.priority,
65            properties: AuthZResolverPluginSpecV1,
66        };
67        let instance_json = serde_json::to_value(&instance)?;
68
69        let results = registry.register(vec![instance_json]).await?;
70        RegisterResult::ensure_all_ok(&results)?;
71
72        // Create service with TR dependency
73        let service = Arc::new(Service::new(tr));
74        self.service
75            .set(service.clone())
76            .map_err(|_| anyhow::anyhow!("{} module already initialized", Self::MODULE_NAME))?;
77
78        // Register scoped client in ClientHub
79        let api: Arc<dyn AuthZResolverPluginClient> = service;
80        ctx.client_hub()
81            .register_scoped::<dyn AuthZResolverPluginClient>(
82                ClientScope::gts_id(&instance_id),
83                api,
84            );
85
86        info!(instance_id = %instance_id);
87        Ok(())
88    }
89}