Skip to content

Commit 12fd801

Browse files
committed
secrets: scope keyring by endpoint
1 parent c549192 commit 12fd801

File tree

3 files changed

+79
-18
lines changed

3 files changed

+79
-18
lines changed

internal/oauth/flow.go

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ const (
3434
ScopeOfflineAccess string = "offline_access"
3535
ScopeUserAll string = "user:all"
3636

37-
// storeKeyFmt is the format of the key name that will be used to store a value
38-
// typically the last element is the endpoint the value is for ie. src:oauth:https://sourcegraph.sourcegraph.com
39-
storeKeyFmt string = "src:oauth:%s"
37+
oauthKey string = "src:oauth"
4038
)
4139

4240
var defaultScopes = []string{ScopeEmail, ScopeOfflineAccess, ScopeOpenID, ScopeProfile, ScopeUserAll}
@@ -394,12 +392,12 @@ func (t *Token) ExpiringIn(d time.Duration) bool {
394392
return future.After(t.ExpiresAt)
395393
}
396394

397-
func oauthKey(endpoint string) string {
398-
return fmt.Sprintf(storeKeyFmt, endpoint)
399-
}
400-
401395
func StoreToken(ctx context.Context, token *Token) error {
402-
store, err := secrets.Open(ctx)
396+
if token.Endpoint == "" {
397+
return errors.New("token endpoint cannot be empty when storing the token")
398+
}
399+
400+
store, err := secrets.Open(ctx, token.Endpoint)
403401
if err != nil {
404402
return err
405403
}
@@ -408,21 +406,16 @@ func StoreToken(ctx context.Context, token *Token) error {
408406
return errors.Wrap(err, "failed to marshal token")
409407
}
410408

411-
if token.Endpoint == "" {
412-
return errors.New("token endpoint cannot be empty when storing the token")
413-
}
414-
415-
return store.Put(oauthKey(token.Endpoint), data)
409+
return store.Put(oauthKey, data)
416410
}
417411

418412
func LoadToken(ctx context.Context, endpoint string) (*Token, error) {
419-
store, err := secrets.Open(ctx)
413+
store, err := secrets.Open(ctx, endpoint)
420414
if err != nil {
421415
return nil, err
422416
}
423417

424-
key := oauthKey(endpoint)
425-
data, err := store.Get(key)
418+
data, err := store.Get(oauthKey)
426419
if err != nil {
427420
return nil, err
428421
}

internal/secrets/keyring.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,31 @@ package secrets
22

33
import (
44
"context"
5+
"strings"
56

67
"github.com/sourcegraph/sourcegraph/lib/errors"
78
"github.com/zalando/go-keyring"
89
)
910

1011
var ErrSecretNotFound = errors.New("secret not found")
1112

13+
const serviceNamePrefix = "Sourcegraph CLI"
14+
1215
type keyringStore struct {
1316
ctx context.Context
1417
serviceName string
1518
}
1619

1720
// Open opens the system keyring for the Sourcegraph CLI.
18-
func Open(ctx context.Context) (*keyringStore, error) {
19-
return &keyringStore{ctx: ctx, serviceName: "Sourcegraph CLI"}, nil
21+
func Open(ctx context.Context, endpoint string) (*keyringStore, error) {
22+
endpoint = strings.TrimRight(strings.TrimSpace(endpoint), "/")
23+
if endpoint == "" {
24+
return nil, errors.New("endpoint cannot be empty")
25+
}
26+
27+
serviceName := serviceNamePrefix + " <" + endpoint + ">"
28+
29+
return &keyringStore{ctx: ctx, serviceName: serviceName}, nil
2030
}
2131

2232
// withContext runs fn in a goroutine and returns its result, or ctx.Err() if the context is cancelled first.

internal/secrets/keyring_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package secrets
2+
3+
import (
4+
"context"
5+
"testing"
6+
)
7+
8+
func TestOpen(t *testing.T) {
9+
tests := []struct {
10+
name string
11+
endpoint string
12+
wantServiceName string
13+
wantErr bool
14+
}{
15+
{
16+
name: "normalized endpoint",
17+
endpoint: " https://sourcegraph.example.com/ ",
18+
wantServiceName: "Sourcegraph CLI <https://sourcegraph.example.com>",
19+
},
20+
{
21+
name: "normalized endpoint with path",
22+
endpoint: " https://sourcegraph.example.com/sourcegraph/ ",
23+
wantServiceName: "Sourcegraph CLI <https://sourcegraph.example.com/sourcegraph>",
24+
},
25+
{
26+
name: "normalized endpoint with nested path",
27+
endpoint: "https://sourcegraph.example.com/custom/path///",
28+
wantServiceName: "Sourcegraph CLI <https://sourcegraph.example.com/custom/path>",
29+
},
30+
{
31+
name: "empty endpoint",
32+
endpoint: " / ",
33+
wantErr: true,
34+
},
35+
}
36+
37+
for _, test := range tests {
38+
t.Run(test.name, func(t *testing.T) {
39+
store, err := Open(context.Background(), test.endpoint)
40+
if test.wantErr {
41+
if err == nil {
42+
t.Fatal("Open() error = nil, want non-nil")
43+
}
44+
if store != nil {
45+
t.Fatalf("Open() store = %v, want nil", store)
46+
}
47+
return
48+
}
49+
50+
if err != nil {
51+
t.Fatalf("Open() error = %v, want nil", err)
52+
}
53+
if got := store.serviceName; got != test.wantServiceName {
54+
t.Fatalf("Open() serviceName = %q, want %q", got, test.wantServiceName)
55+
}
56+
})
57+
}
58+
}

0 commit comments

Comments
 (0)