mirror of
https://github.com/larksuite/cli.git
synced 2026-07-25 16:22:59 +08:00
Integrators that build their own lark-cli distribution can now ship a package where everything they cut out truly disappears, instead of lingering as half-disabled stubs that mislead users and agents. Commands an integrator plugin restricts no longer show up in help or completion, and invoking one (including asking for its help) answers "command not included in this build" (subtype command_unavailable) with no policy vocabulary and no dead-end recovery steering. Integrators can replace that message with their own product wording via Rule.DeniedMessage. Cutting a whole domain also retires everything that points at it: --profile hides and rejects use when the profile domain goes, the skills-setup footer, update notices, and the auth-login recovery hint stop rendering when their domains go. The policy self-inspection commands (config policy show / plugins show) stay executable by default so operators can still see which rule locked the build, hidden from help but available on request. Fully-managed distributions can retire even those with HideDiagnostics(). Plugins can also trim the embedded skills to match: EmbeddedSkills removes, overlays, or replaces entries over the CLI's built-in set, and skills list/read plus every help pointer serve the same trimmed tree. Users who write their own ~/.lark-cli/policy.yml see no change: their denials stay visible and keep explaining how to adjust the policy they own. Default builds are byte-for-byte unchanged.
73 lines
2.6 KiB
Go
73 lines
2.6 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package auth
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/larksuite/cli/errs"
|
|
"github.com/larksuite/cli/internal/output"
|
|
"github.com/larksuite/cli/internal/policystate"
|
|
)
|
|
|
|
const (
|
|
needUserAuthorizationMarker = "need_user_authorization"
|
|
)
|
|
|
|
// TokenRetryCodes contains error codes that allow retry after token refresh.
|
|
var TokenRetryCodes = map[int]bool{
|
|
output.LarkErrTokenInvalid: true,
|
|
output.LarkErrTokenExpired: true,
|
|
}
|
|
|
|
// NeedAuthorizationError is the sentinel preserved in the Cause chain of the
|
|
// typed missing-UAT error so existing errors.As(&NeedAuthorizationError{})
|
|
// consumers keep matching after the construction site moved to the typed
|
|
// taxonomy. It is never surfaced on the wire on its own.
|
|
type NeedAuthorizationError struct {
|
|
UserOpenId string
|
|
}
|
|
|
|
// Error returns the error message for NeedAuthorizationError.
|
|
func (e *NeedAuthorizationError) Error() string {
|
|
return fmt.Sprintf("%s (user: %s)", needUserAuthorizationMarker, e.UserOpenId)
|
|
}
|
|
|
|
// NewNeedUserAuthorizationError builds the typed *errs.AuthenticationError
|
|
// returned when no valid UAT exists for userOpenID. The Message keeps the
|
|
// need_user_authorization marker, the Hint converges on the same auth-login
|
|
// recovery vocabulary as the token-missing surface in internal/client, and the
|
|
// legacy *NeedAuthorizationError sentinel is preserved in the Cause chain for
|
|
// errors.As / errors.Is traversal.
|
|
func NewNeedUserAuthorizationError(userOpenID string) *errs.AuthenticationError {
|
|
e := errs.NewAuthenticationError(errs.SubtypeTokenMissing,
|
|
"%s (user: %s)", needUserAuthorizationMarker, userOpenID).
|
|
WithUserOpenID(userOpenID).
|
|
WithCause(&NeedAuthorizationError{UserOpenId: userOpenID})
|
|
// No recovery hint when the auth domain is absent from this build.
|
|
if !policystate.DomainDeniedByPlugin("auth") {
|
|
e = e.WithHint("run: lark-cli auth login to re-authorize")
|
|
}
|
|
return e
|
|
}
|
|
|
|
// IsNeedUserAuthorizationError reports whether err represents a missing-UAT
|
|
// failure. It matches the legacy *NeedAuthorizationError sentinel, which is
|
|
// preserved in the Cause chain of the typed missing-UAT error, so errors.As
|
|
// traverses into the typed *errs.AuthenticationError as well.
|
|
func IsNeedUserAuthorizationError(err error) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
|
|
var needAuthErr *NeedAuthorizationError
|
|
return errors.As(err, &needAuthErr)
|
|
}
|
|
|
|
// SecurityPolicyError is preserved as a Go type alias so existing
|
|
// errors.As(&SecurityPolicyError{}) consumers (cmd/root.go etc.) keep working.
|
|
// The concrete struct lives in errs/types.go.
|
|
type SecurityPolicyError = errs.SecurityPolicyError
|