mirror of
https://github.com/larksuite/cli.git
synced 2026-09-14 18:42:53 +08:00
7a6a4dbdf5
* fix: preserve credential and config read failures Report corrupt or inaccessible stored credentials as typed errors instead of treating them as missing. Preserve config load failures across auth diagnostics and keep trusted request metadata available when host signal collection is unavailable. Fixes #1925 * fix(auth): attach re-authorization recovery to corrupt stored token errors A stored token that fails to decode or fails semantic validation is now reported as internal/storage but carried no recovery hint, so `api --as user`, `auth check`, `auth status`, and `doctor` all ended in a dead end. A new login overwrites the damaged entry, so attach the canonical user-authorization recovery on the two read-side corruption branches. The write-side validator stays hint-free: a rejected write leaves nothing on disk to re-authorize. `auth status` keeps the storage failure in `note` and appends the recovery instead of replacing it. Tests pin the hint on GetStoredToken, GetValidAccessToken, identitydiag (including reduced-distribution projection when auth login is concealed), auth check, and auth status, and pin that SetStoredToken replaces a corrupt entry. ---------
112 lines
3.9 KiB
Go
112 lines
3.9 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// Package surface describes the build-local command surface presented by one
|
|
// Cobra tree. It is deliberately independent from policy enforcement: a denied
|
|
// command may remain visible, or an opt-in distribution may conceal it.
|
|
package surface
|
|
|
|
import "strings"
|
|
|
|
// CommandID is a command's canonical slash-separated path relative to the
|
|
// binary root, for example "auth/login" or "config/strict-mode".
|
|
type CommandID string
|
|
|
|
// Canonical framework command IDs. Centralizing these paths keeps recovery,
|
|
// notices, diagnostics, and flag projection on the same exact-path vocabulary.
|
|
const (
|
|
CommandAuthLogin CommandID = "auth/login"
|
|
CommandConfig CommandID = "config"
|
|
CommandConfigInit CommandID = "config/init"
|
|
CommandConfigBind CommandID = "config/bind"
|
|
CommandConfigKeychainDowngrade CommandID = "config/keychain-downgrade"
|
|
CommandConfigStrictMode CommandID = "config/strict-mode"
|
|
CommandConfigPolicyShow CommandID = "config/policy/show"
|
|
CommandConfigPluginsShow CommandID = "config/plugins/show"
|
|
CommandProfile CommandID = "profile"
|
|
CommandProfileAdd CommandID = "profile/add"
|
|
CommandProfileList CommandID = "profile/list"
|
|
CommandSchema CommandID = "schema"
|
|
CommandUpdate CommandID = "update"
|
|
CommandSkills CommandID = "skills"
|
|
CommandSkillsRead CommandID = "skills/read"
|
|
)
|
|
|
|
// CommandState describes how one command is presented by a distribution.
|
|
type CommandState uint8
|
|
|
|
const (
|
|
// CommandAvailable is the default for paths absent from a Plan.
|
|
CommandAvailable CommandState = iota
|
|
// CommandDeniedVisible keeps the command and references to it visible while
|
|
// policy enforcement rejects execution.
|
|
CommandDeniedVisible
|
|
// CommandConcealed presents the command as absent from this distribution.
|
|
CommandConcealed
|
|
)
|
|
|
|
// Plan is an immutable, build-local snapshot of command presentation states.
|
|
//
|
|
// NewPlan defensively copies its input and Plan exposes no mutator, so a Plan
|
|
// can safely be captured by the renderers belonging to one Cobra tree. The
|
|
// zero value (and a nil *Plan) describes the default, fully visible surface.
|
|
type Plan struct {
|
|
commands map[CommandID]CommandState
|
|
}
|
|
|
|
// NewPlan returns an immutable snapshot of states. Paths omitted from states
|
|
// are CommandAvailable.
|
|
func NewPlan(states map[CommandID]CommandState) *Plan {
|
|
commands := make(map[CommandID]CommandState, len(states))
|
|
for id, state := range states {
|
|
commands[id] = state
|
|
}
|
|
return &Plan{commands: commands}
|
|
}
|
|
|
|
// State returns the state explicitly recorded for id. It intentionally does
|
|
// not infer a child state from an ancestor; use IsConcealed or CanReference
|
|
// when effective presentation is required.
|
|
func (p *Plan) State(id CommandID) CommandState {
|
|
if p == nil {
|
|
return CommandAvailable
|
|
}
|
|
if state, ok := p.commands[id]; ok {
|
|
return state
|
|
}
|
|
return CommandAvailable
|
|
}
|
|
|
|
// IsConcealed reports whether id or any canonical ancestor is concealed.
|
|
// Ancestor concealment dominates an explicit child state: a child cannot be
|
|
// presented through a parent that the distribution presents as absent.
|
|
func (p *Plan) IsConcealed(id CommandID) bool {
|
|
for {
|
|
if p.State(id) == CommandConcealed {
|
|
return true
|
|
}
|
|
parent, ok := parentOf(id)
|
|
if !ok {
|
|
return false
|
|
}
|
|
id = parent
|
|
}
|
|
}
|
|
|
|
// CanReference reports whether user-facing output may point at id.
|
|
// Denied-visible commands remain valid recovery targets because following the
|
|
// reference produces the existing, policy-rich denial. Concealed commands do
|
|
// not.
|
|
func (p *Plan) CanReference(id CommandID) bool {
|
|
return !p.IsConcealed(id)
|
|
}
|
|
|
|
func parentOf(id CommandID) (CommandID, bool) {
|
|
path := string(id)
|
|
i := strings.LastIndexByte(path, '/')
|
|
if i < 0 {
|
|
return "", false
|
|
}
|
|
return CommandID(path[:i]), true
|
|
}
|