Summary
Add an optional namespaces field to policy entries that restricts the entry's applicability to things within matching namespaces. This enables namespace-scoped authorization without changes to the search index structure.
Motivation
Policy entries currently apply universally regardless of the thing's namespace. This limitation affects:
- Multi-Tenant Scenarios: Organizations need tenant isolation without maintaining separate policies per tenant
- Namespace-Based Organization: Different user groups need different permissions per namespace (e.g., vehicle managers vs. facility managers)
Proposed Solution
Add an optional namespaces array to policy entries:
{
"policyId": "com.acme:shared-policy",
"entries": {
"vehicle-admins": {
"subjects": { "nginx:fleet-manager": { "type": "generated" } },
"resources": { "thing:/": { "grant": ["READ", "WRITE"], "revoke": [] } },
"namespaces": ["com.acme.vehicles", "com.acme.vehicles.*"]
},
"analysts": {
"subjects": { "nginx:data-analyst": { "type": "generated" } },
"resources": { "thing:/": { "grant": ["READ"], "revoke": [] } }
}
}
}
Semantics
namespaces field |
Behavior |
| Absent |
Entry applies to all namespaces (backward compatible) |
Empty array [] |
Same as absent |
| Array with patterns |
Entry only applies when thing's namespace matches at least one pattern |
Namespace Pattern Matching
com.acme - exact match only
com.acme.* - matches everything below com.acme but not com.acme itself
To match both: ["com.acme", "com.acme.*"]
Multi-Tenant Example
{
"policyId": "platform:multi-tenant-base",
"entries": {
"tenant-a-users": {
"subjects": { "oidc:tenant-a-group": {} },
"resources": { "thing:/": { "grant": ["READ", "WRITE"], "revoke": [] } },
"namespaces": ["com.tenant-a", "com.tenant-a.*"]
},
"tenant-b-users": {
"subjects": { "oidc:tenant-b-group": {} },
"resources": { "thing:/": { "grant": ["READ", "WRITE"], "revoke": [] } },
"namespaces": ["com.tenant-b", "com.tenant-b.*"]
}
}
}
Tenant A users cannot access Tenant B things (and vice versa).
Implementation Notes
- Search index unchanged: Namespace filtering applied at index write time in
EvaluatedPolicy.of() - entries filtered by thing's namespace before extracting subjects
- Backward compatible: Absent field means "applies to all namespaces"
- Validation: Reuse existing
RegexPatterns.NAMESPACE_PATTERN with * allowed as trailing wildcard
Related
Summary
Add an optional
namespacesfield to policy entries that restricts the entry's applicability to things within matching namespaces. This enables namespace-scoped authorization without changes to the search index structure.Motivation
Policy entries currently apply universally regardless of the thing's namespace. This limitation affects:
Proposed Solution
Add an optional
namespacesarray to policy entries:{ "policyId": "com.acme:shared-policy", "entries": { "vehicle-admins": { "subjects": { "nginx:fleet-manager": { "type": "generated" } }, "resources": { "thing:/": { "grant": ["READ", "WRITE"], "revoke": [] } }, "namespaces": ["com.acme.vehicles", "com.acme.vehicles.*"] }, "analysts": { "subjects": { "nginx:data-analyst": { "type": "generated" } }, "resources": { "thing:/": { "grant": ["READ"], "revoke": [] } } } } }Semantics
namespacesfield[]Namespace Pattern Matching
com.acme- exact match onlycom.acme.*- matches everything belowcom.acmebut notcom.acmeitselfTo match both:
["com.acme", "com.acme.*"]Multi-Tenant Example
{ "policyId": "platform:multi-tenant-base", "entries": { "tenant-a-users": { "subjects": { "oidc:tenant-a-group": {} }, "resources": { "thing:/": { "grant": ["READ", "WRITE"], "revoke": [] } }, "namespaces": ["com.tenant-a", "com.tenant-a.*"] }, "tenant-b-users": { "subjects": { "oidc:tenant-b-group": {} }, "resources": { "thing:/": { "grant": ["READ", "WRITE"], "revoke": [] } }, "namespaces": ["com.tenant-b", "com.tenant-b.*"] } } }Tenant A users cannot access Tenant B things (and vice versa).
Implementation Notes
EvaluatedPolicy.of()- entries filtered by thing's namespace before extracting subjectsRegexPatterns.NAMESPACE_PATTERNwith*allowed as trailing wildcardRelated