package access import ( "slices" "strings" ) // Visibility scope tokens shared by user-owned, filterable objects across uopi // (config sets/instances, control-logic graphs, synthetic signals). They drive // the per-tree "Mine / Group / Global" selector. Storage carries the scope as a // plain string so each subsystem can embed it in its own JSON without depending // on this package's types. const ( // ScopePrivate: visible only to the owner. ScopePrivate = "private" // ScopeGroup: visible to the owner and members of any listed group. ScopeGroup = "group" // ScopeGlobal: visible to everyone. This is also the legacy default — an // empty/unknown scope is treated as global so objects created before scopes // existed stay visible to all. ScopeGlobal = "global" ) // CanSee reports whether user (a member of userGroups) may see an object with the // given owner, scope and itemGroups. An empty or unrecognised scope is treated as // global, so legacy objects without a scope remain visible to everyone. // // This is a visibility filter for selector trees, not a hard security boundary: // it governs which objects are offered in listings, and intentionally always // shows an object to its owner regardless of scope. func CanSee(user, owner, scope string, itemGroups, userGroups []string) bool { switch strings.ToLower(strings.TrimSpace(scope)) { case ScopePrivate: return owner != "" && owner == user case ScopeGroup: if owner != "" && owner == user { return true } for _, g := range itemGroups { if slices.Contains(userGroups, g) { return true } } return false default: // global / empty / unknown return true } } // CanSee reports whether user may see an object with the given owner, scope and // itemGroups, resolving the user's group memberships from the policy. It is the // convenience wrapper list handlers use. func (p *Policy) CanSee(user, owner, scope string, itemGroups []string) bool { return CanSee(user, owner, scope, itemGroups, p.GroupsOf(user)) }