'array', 'app_client_secret' => 'encrypted', 'is_current' => 'boolean', 'rbac_last_checked_at' => 'datetime', 'rbac_last_setup_at' => 'datetime', 'rbac_canary_results' => 'array', 'rbac_last_warnings' => 'array', ]; public function getRbacCanaryResultsAttribute($value): array { if (is_string($value)) { $decoded = json_decode($value, true); return is_array($decoded) ? $decoded : []; } return $value ?? []; } public function getRbacLastWarningsAttribute($value): array { if (is_string($value)) { $decoded = json_decode($value, true); return is_array($decoded) ? $decoded : []; } $warnings = $value ?? []; if ($this->rbac_scope_mode === 'scope_group' || filled($this->rbac_scope_id)) { $warnings[] = 'scope_limited'; } return $warnings; } protected static function booted(): void { static::creating(function (Tenant $tenant) { if (empty($tenant->external_id)) { $tenant->external_id = $tenant->tenant_id ?? (string) Str::uuid(); } if (empty($tenant->status)) { $tenant->status = 'active'; } }); static::saving(function (Tenant $tenant) { if (! empty($tenant->tenant_id)) { $tenant->external_id = $tenant->tenant_id; } }); static::deleting(function (Tenant $tenant) { if ($tenant->isForceDeleting()) { return; } $tenant->status = 'archived'; $tenant->saveQuietly(); }); static::restored(function (Tenant $tenant) { $tenant->forceFill(['status' => 'active'])->saveQuietly(); }); } public static function activeQuery(): Builder { return static::query() ->whereNull('deleted_at') ->where('status', 'active'); } public function makeCurrent(): void { if ($this->trashed() || $this->status !== 'active') { throw new RuntimeException('Only active tenants can be made current.'); } DB::transaction(function () { static::activeQuery()->update(['is_current' => false]); $this->forceFill(['is_current' => true])->save(); }); } public static function current(): self { $envTenantId = env('INTUNE_TENANT_ID') ?: null; if ($envTenantId) { $tenant = static::activeQuery() ->where(function (Builder $query) use ($envTenantId) { $query->where('tenant_id', $envTenantId) ->orWhere('external_id', $envTenantId); }) ->first(); if (! $tenant) { throw new RuntimeException('Configured INTUNE_TENANT_ID tenant is missing or inactive.'); } return $tenant; } $tenant = static::activeQuery() ->where('is_current', true) ->first(); if (! $tenant) { throw new RuntimeException('No current tenant selected.'); } return $tenant; } public function policies(): HasMany { return $this->hasMany(Policy::class); } public function backupSets(): HasMany { return $this->hasMany(BackupSet::class); } public function policyVersions(): HasMany { return $this->hasMany(PolicyVersion::class); } public function restoreRuns(): HasMany { return $this->hasMany(RestoreRun::class); } public function auditLogs(): HasMany { return $this->hasMany(AuditLog::class); } public function permissions(): HasMany { return $this->hasMany(TenantPermission::class); } public function graphTenantId(): ?string { return $this->tenant_id ?? $this->external_id; } /** * @return array{tenant:?string,client_id:?string,client_secret:?string} */ public function graphOptions(): array { return [ 'tenant' => $this->graphTenantId(), 'client_id' => $this->app_client_id, 'client_secret' => $this->app_client_secret, ]; } public function scopeForTenant(Builder $query, self|int|string $tenant): Builder { if ($tenant instanceof self) { return $query->whereKey($tenant->getKey()); } if (is_int($tenant) || ctype_digit((string) $tenant)) { return $query->whereKey($tenant); } return $query ->where('tenant_id', $tenant) ->orWhere('external_id', $tenant); } public function isActive(): bool { return ! $this->trashed() && ($this->status ?? 'active') === 'active'; } }