Skip to content

Commit 112ab91

Browse files
committed
fix(auth): support ABAC-enabled registries by removing wildcard scopes
1 parent 5b6774d commit 112ab91

File tree

2 files changed

+29
-13
lines changed

2 files changed

+29
-13
lines changed

internal/api/acrsdk.go

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package api
77
import (
88
"bytes"
99
"context"
10+
"fmt"
1011
"io/ioutil"
1112
"strings"
1213
"time"
@@ -94,7 +95,9 @@ func newAcrCLIClientWithBasicAuth(loginURL string, username string, password str
9495
func newAcrCLIClientWithBearerAuth(loginURL string, refreshToken string) (AcrCLIClient, error) {
9596
newAcrCLIClient := newAcrCLIClient(loginURL)
9697
ctx := context.Background()
97-
accessTokenResponse, err := newAcrCLIClient.AutorestClient.GetAcrAccessToken(ctx, loginURL, "registry:catalog:* repository:*:*", refreshToken)
98+
// For ABAC-enabled registries, only request catalog scope initially
99+
// Repository-specific scopes will be requested when needed
100+
accessTokenResponse, err := newAcrCLIClient.AutorestClient.GetAcrAccessToken(ctx, loginURL, "registry:catalog:*", refreshToken)
98101
if err != nil {
99102
return newAcrCLIClient, err
100103
}
@@ -153,9 +156,9 @@ func GetAcrCLIClientWithAuth(loginURL string, username string, password string,
153156
return &acrClient, nil
154157
}
155158

156-
// refreshAcrCLIClientToken obtains a new token and gets its expiration time.
157-
func refreshAcrCLIClientToken(ctx context.Context, c *AcrCLIClient) error {
158-
accessTokenResponse, err := c.AutorestClient.GetAcrAccessToken(ctx, c.loginURL, "repository:*:*", c.token.RefreshToken)
159+
// refreshAcrCLIClientToken obtains a new token with the specified scope and gets its expiration time.
160+
func refreshAcrCLIClientToken(ctx context.Context, c *AcrCLIClient, scope string) error {
161+
accessTokenResponse, err := c.AutorestClient.GetAcrAccessToken(ctx, c.loginURL, scope, c.token.RefreshToken)
159162
if err != nil {
160163
return err
161164
}
@@ -173,6 +176,19 @@ func refreshAcrCLIClientToken(ctx context.Context, c *AcrCLIClient) error {
173176
return nil
174177
}
175178

179+
// refreshTokenForRepository obtains a new token scoped to a specific repository with all permissions.
180+
// This supports both ABAC and non-ABAC registries.
181+
func refreshTokenForRepository(ctx context.Context, c *AcrCLIClient, repoName string) error {
182+
// For specific repository operations, request full permissions on that repository
183+
scope := fmt.Sprintf("repository:%s:*", repoName)
184+
return refreshAcrCLIClientToken(ctx, c, scope)
185+
}
186+
187+
// refreshTokenForCatalog obtains a new token with catalog access only.
188+
func refreshTokenForCatalog(ctx context.Context, c *AcrCLIClient) error {
189+
return refreshAcrCLIClientToken(ctx, c, "registry:catalog:*")
190+
}
191+
176192
// getExpiration is used to obtain the expiration out of a jwt token.
177193
func getExpiration(token string) (int64, error) {
178194
parser := jwt.Parser{SkipClaimsValidation: true}
@@ -201,7 +217,7 @@ func (c *AcrCLIClient) isExpired() bool {
201217
// GetAcrTags list the tags of a repository with their attributes.
202218
func (c *AcrCLIClient) GetAcrTags(ctx context.Context, repoName string, orderBy string, last string) (*acrapi.RepositoryTagsType, error) {
203219
if c.isExpired() {
204-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
220+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
205221
return nil, err
206222
}
207223
}
@@ -216,7 +232,7 @@ func (c *AcrCLIClient) GetAcrTags(ctx context.Context, repoName string, orderBy
216232
// DeleteAcrTag deletes the tag by reference.
217233
func (c *AcrCLIClient) DeleteAcrTag(ctx context.Context, repoName string, reference string) (*autorest.Response, error) {
218234
if c.isExpired() {
219-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
235+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
220236
return nil, err
221237
}
222238
}
@@ -230,7 +246,7 @@ func (c *AcrCLIClient) DeleteAcrTag(ctx context.Context, repoName string, refere
230246
// GetAcrManifests list all the manifest in a repository with their attributes.
231247
func (c *AcrCLIClient) GetAcrManifests(ctx context.Context, repoName string, orderBy string, last string) (*acrapi.Manifests, error) {
232248
if c.isExpired() {
233-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
249+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
234250
return nil, err
235251
}
236252
}
@@ -244,7 +260,7 @@ func (c *AcrCLIClient) GetAcrManifests(ctx context.Context, repoName string, ord
244260
// DeleteManifest deletes a manifest using the digest as a reference.
245261
func (c *AcrCLIClient) DeleteManifest(ctx context.Context, repoName string, reference string) (*autorest.Response, error) {
246262
if c.isExpired() {
247-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
263+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
248264
return nil, err
249265
}
250266
}
@@ -259,7 +275,7 @@ func (c *AcrCLIClient) DeleteManifest(ctx context.Context, repoName string, refe
259275
// This is used when a manifest list is wanted, first the bytes are obtained and then unmarshalled into a new struct.
260276
func (c *AcrCLIClient) GetManifest(ctx context.Context, repoName string, reference string) ([]byte, error) {
261277
if c.isExpired() {
262-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
278+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
263279
return nil, err
264280
}
265281
}
@@ -299,7 +315,7 @@ func (c *AcrCLIClient) GetManifest(ctx context.Context, repoName string, referen
299315
// GetAcrManifestAttributes gets the attributes of a manifest.
300316
func (c *AcrCLIClient) GetAcrManifestAttributes(ctx context.Context, repoName string, reference string) (*acrapi.ManifestAttributes, error) {
301317
if c.isExpired() {
302-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
318+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
303319
return nil, err
304320
}
305321
}
@@ -313,7 +329,7 @@ func (c *AcrCLIClient) GetAcrManifestAttributes(ctx context.Context, repoName st
313329
// UpdateAcrTagAttributes updates tag attributes to enable/disable deletion and writing.
314330
func (c *AcrCLIClient) UpdateAcrTagAttributes(ctx context.Context, repoName string, reference string, value *acrapi.ChangeableAttributes) (*autorest.Response, error) {
315331
if c.isExpired() {
316-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
332+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
317333
return nil, err
318334
}
319335
}
@@ -327,7 +343,7 @@ func (c *AcrCLIClient) UpdateAcrTagAttributes(ctx context.Context, repoName stri
327343
// UpdateAcrManifestAttributes updates manifest attributes to enable/disable deletion and writing.
328344
func (c *AcrCLIClient) UpdateAcrManifestAttributes(ctx context.Context, repoName string, reference string, value *acrapi.ChangeableAttributes) (*autorest.Response, error) {
329345
if c.isExpired() {
330-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
346+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
331347
return nil, err
332348
}
333349
}

internal/api/acrsdk_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func TestGetExpiration(t *testing.T) {
7878

7979
func TestGetAcrCLIClientWithAuth(t *testing.T) {
8080
var testLoginURL string
81-
testTokenScope := "registry:catalog:* repository:*:*"
81+
testTokenScope := "registry:catalog:*"
8282
testAccessToken := strings.Join([]string{
8383
base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"RS256"}`)),
8484
base64.RawURLEncoding.EncodeToString([]byte(`{"exp":1563910981}`)),

0 commit comments

Comments
 (0)