auth: remove cluster-binding model and command surface · Entire
auth: remove cluster-binding model and command surface
5a4163f→main·
toothbrush·1mo ago·9 files·+24 added/-346 removed
Removes the cluster→context binding now that account selection is recomputed per operation:
- contexts: drop File.Resolve and BindCluster. The ClusterContexts field is kept but inert (deprecated) so an existing contexts.json round-trips without data loss during the transition; Delete still prunes it and a full logout still clears it.
- auth: drop ClusterBindings/UnbindCluster, the
entire auth unbindcommand, and the "Cluster bindings" section ofentire auth contexts.
Tests updated accordingly; RemoveAllContexts still clears the legacy field (seeded directly now that BindCluster is gone).
Sessions
1a18bfd708fcView transcript
Changes
9
M.gitignore+1
cmd/entire/cli
Mauth.go-1
auth
Mcontext_store.go-30
Mcontexts_test.go+7/-2
Mauth_context.go+7/-64
Mauth_context_test.go-108
internal/entireclient
contexts
Mcontexts.go+8/-46
Mcontexts_test.go-94
discovery
Mcache.go+1/-1
66 unmodified lines
67
68
69
70
66 unmodified lines
tmp/
.tmp/
.superpowers/
/git-remote-entire
M.gitignore+1
153 unmodified lines
154
155
156
157
157
158
159
153 unmodified lines
cmd.AddCommand(newAuthRevokeCmd())
cmd.AddCommand(newAuthContextsCmd())
cmd.AddCommand(newAuthUseCmd())
cmd.AddCommand(newAuthUnbindCmd())
return cmd
}
Mcmd/entire/cli/auth.go-1
115 unmodified lines
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
119
120
121
115 unmodified lines
return f.Contexts, f.CurrentContext, nil
// ClusterBindings returns the cluster_host → context_name map from
// contexts.json. Surfaced by `entire auth contexts` so users can audit
// which cluster hosts auto-authenticate git operations with a stored
// context — and spot any they don't recognise.
func ClusterBindings() (map[string]string, error) {
f, err := contexts.Load(contexts.DefaultConfigDir())
if err != nil {
return nil, fmt.Errorf("load contexts: %w", err)
}
return f.ClusterContexts, nil
}
// UnbindCluster removes the cluster→context binding for host, reporting
// whether one existed. Used by `entire auth unbind` to revoke a binding
// without touching the underlying login context.
func UnbindCluster(host string) (bool, error) {
var existed bool
if err := contexts.Modify(contexts.DefaultConfigDir(), func(f *contexts.File) (bool, error) {
if _, ok := f.ClusterContexts[host]; !ok {
return false, nil
}
delete(f.ClusterContexts, host)
existed = true
return true, nil
}); err != nil {
return false, fmt.Errorf("unbind cluster: %w", err)
}
return existed, nil
}
// ContextStore wraps the legacy keyring Store so token *reads* prefer the
// active contexts.json context, falling back to the legacy
// entire-cli/<authBaseURL> entry. Writes are inherited from Store
Mcmd/entire/cli/auth/context_store.go-30
229 unmodified lines
230
231
232
233
234
233
234
235
236
237
238
239
240
241
242
229 unmodified lines
if _, err := RecordLoginContext(makeJWT(t, fmt.Sprintf(`{"iss":"https://b.example.com","handle":"bob","exp":%d}`, exp)), true); err != nil {
t.Fatalf("record b: %v", err)
}
if err := contexts.BindCluster(cfgDir, "cluster.example.com", a); err != nil {
t.Fatalf("bind cluster: %v", err)
}
// Seed a legacy cluster_contexts entry directly to prove RemoveAllContexts
// still clears the inert field on a full logout.
if err := contexts.Modify(cfgDir, func(f *contexts.File) (bool, error) {
f.ClusterContexts = map[string]string{"cluster.example.com": a}
return true, nil
}); err != nil {
t.Fatalf("seed legacy binding: %v", err)
}
n, err := RemoveAllContexts()
Mcmd/entire/cli/auth/contexts_test.go+7/-2
2 unmodified lines
3
4
5
6
6
7
8
65 unmodified lines
74
75
76
78
77
78
79
80
6 unmodified lines
87
88
89
91
92
93
94
90
91
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
92
93
124
125
126
94
95
96
97
98
99
100
128
129
130
131
132
133
101
102
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
2 unmodified lines
import (
"fmt"
"io"
"sort"
"github.com/entireio/cli/cmd/entire/cli/api"
"github.com/entireio/cli/cmd/entire/cli/auth"
65 unmodified lines
func newAuthContextsCmd() *cobra.Command {
return &cobra.Command{
Use: "contexts",
Short: "List stored login contexts and cluster bindings",
Short: "List stored login contexts",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
return runAuthContexts(cmd.OutOrStdout())
}
}
}
// printClusterBindings lists any cluster_contexts bindings so users can
// audit which hosts skip /.well-known discovery and resolve straight to a
// stored context. A binding does not hand that host your login JWT — that
// only ever goes to the bound context's core, and the host receives a
// repo-scoped, audience-pinned token (see repocreds). What a binding pins
// is which core authenticates that host, so an entry you don't recognise
// is still worth revoking with `entire auth unbind`.
func printClusterBindings(w io.Writer) error {
bindings, err := auth.ClusterBindings()
if err != nil {
return err //nolint:wrapcheck // already a user-facing message
}
if len(bindings) == 0 {
return nil
}
hosts := make([]string, 0, len(bindings))
for h := range bindings {
hosts = append(hosts, h)
}
sort.Strings(hosts)
fmt.Fprintln(w, "\nCluster bindings (these hosts auto-authenticate with a stored context):")
for _, h := range hosts {
fmt.Fprintf(w, " %s\t-> %s\n", h, bindings[h])
}
fmt.Fprintln(w, "\nRevoke a binding you don't recognise with 'entire auth unbind <host>'.")
return nil
}
// newAuthUnbindCmd removes a cluster→context binding. Purely local.
func newAuthUnbindCmd() *cobra.Command {
return &cobra.Command{
Use: "unbind <cluster-host>",
Short: "Remove a cluster→context binding",
Long: "Remove the stored binding that makes git operations against a cluster host\nauto-authenticate with a saved context. The login context itself is left\nintact. List current bindings with 'entire auth contexts'.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
existed, err := auth.UnbindCluster(args[0])
if err != nil {
return err //nolint:wrapcheck // already a user-facing message
}
if !existed {
fmt.Fprintf(cmd.OutOrStdout(), "No cluster binding for %q.\n", args[0])
return nil
}
fmt.Fprintf(cmd.OutOrStdout(), "Removed cluster binding for %q.\n", args[0])
return nil
}
}
}