bf9395e022
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / results (push) Has been cancelled
CI / deadcode (push) Has been cancelled
CI / e2e-live (push) Has been cancelled
94 lines
2.7 KiB
Go
94 lines
2.7 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package cmdpolicy
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/larksuite/cli/extension/platform"
|
|
)
|
|
|
|
// ActivePolicy is the resolved user-layer policy after applyUserPolicyPruning
|
|
// has run during bootstrap. `lark-cli config policy show` reads this to
|
|
// answer "what rule is currently in effect, and how many commands does
|
|
// it hide?".
|
|
//
|
|
// Set once at bootstrap time; consumed read-only thereafter.
|
|
//
|
|
// Rules is the full set the winning source contributed (one rule for the
|
|
// common single-rule case, several when a plugin or yaml declares scoped
|
|
// grants). nil/empty means "no rule applied".
|
|
type ActivePolicy struct {
|
|
Rules []*platform.Rule
|
|
Source ResolveSource
|
|
DeniedPaths int // number of commands the engine marked as denied (post-aggregation)
|
|
}
|
|
|
|
var (
|
|
activeMu sync.RWMutex
|
|
activePolicy *ActivePolicy
|
|
)
|
|
|
|
// SetActive records the policy that ends up applied. Called exactly once
|
|
// per process from cmd/policy.go::applyUserPolicyPruning. The mutex is
|
|
// belt-and-braces in case future test paths interleave with bootstrap.
|
|
//
|
|
// A deep copy is taken so the snapshot is immune to later mutations of
|
|
// the input by the caller (a plugin-supplied *Rule could otherwise
|
|
// mutate the embedded Allow/Deny/Identities slices after we stored it).
|
|
func SetActive(p *ActivePolicy) {
|
|
activeMu.Lock()
|
|
defer activeMu.Unlock()
|
|
if p == nil {
|
|
activePolicy = nil
|
|
return
|
|
}
|
|
activePolicy = cloneActivePolicy(p)
|
|
}
|
|
|
|
// GetActive returns a deep copy of the recorded policy, or nil if
|
|
// bootstrap has not finished or no rule applied. Callers can freely
|
|
// mutate the result — including the embedded Rule slices — without
|
|
// affecting the stored global.
|
|
func GetActive() *ActivePolicy {
|
|
activeMu.RLock()
|
|
defer activeMu.RUnlock()
|
|
if activePolicy == nil {
|
|
return nil
|
|
}
|
|
return cloneActivePolicy(activePolicy)
|
|
}
|
|
|
|
// cloneActivePolicy deep-copies the top-level struct, the Rules slice, and
|
|
// each Rule's own slice fields. Other fields (Source, DeniedPaths) are
|
|
// value types so the struct copy already disjoints them.
|
|
func cloneActivePolicy(in *ActivePolicy) *ActivePolicy {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
cp := *in
|
|
if in.Rules != nil {
|
|
cp.Rules = make([]*platform.Rule, len(in.Rules))
|
|
for i, r := range in.Rules {
|
|
if r == nil {
|
|
continue
|
|
}
|
|
rule := *r
|
|
rule.Allow = append([]string(nil), r.Allow...)
|
|
rule.Deny = append([]string(nil), r.Deny...)
|
|
rule.Identities = append([]platform.Identity(nil), r.Identities...)
|
|
cp.Rules[i] = &rule
|
|
}
|
|
}
|
|
return &cp
|
|
}
|
|
|
|
// ResetActiveForTesting clears the recorded policy. Tests must call this
|
|
// in t.Cleanup when they exercise the bootstrap path.
|
|
func ResetActiveForTesting() {
|
|
activeMu.Lock()
|
|
defer activeMu.Unlock()
|
|
activePolicy = nil
|
|
}
|