Skip to content

Commit 8b52681

Browse files
committed
fix(auth): support ABAC-enabled registries by removing wildcard scopes
1 parent 6be12f7 commit 8b52681

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
@@ -6,6 +6,7 @@ package api
66
import (
77
"bytes"
88
"context"
9+
"fmt"
910
"io/ioutil"
1011
"strings"
1112
"time"
@@ -93,7 +94,9 @@ func newAcrCLIClientWithBasicAuth(loginURL string, username string, password str
9394
func newAcrCLIClientWithBearerAuth(loginURL string, refreshToken string) (AcrCLIClient, error) {
9495
newAcrCLIClient := newAcrCLIClient(loginURL)
9596
ctx := context.Background()
96-
accessTokenResponse, err := newAcrCLIClient.AutorestClient.GetAcrAccessToken(ctx, loginURL, "registry:catalog:* repository:*:*", refreshToken)
97+
// For ABAC-enabled registries, only request catalog scope initially
98+
// Repository-specific scopes will be requested when needed
99+
accessTokenResponse, err := newAcrCLIClient.AutorestClient.GetAcrAccessToken(ctx, loginURL, "registry:catalog:*", refreshToken)
97100
if err != nil {
98101
return newAcrCLIClient, err
99102
}
@@ -152,9 +155,9 @@ func GetAcrCLIClientWithAuth(loginURL string, username string, password string,
152155
return &acrClient, nil
153156
}
154157

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

178+
// refreshTokenForRepository obtains a new token scoped to a specific repository with all permissions.
179+
// This supports both ABAC and non-ABAC registries.
180+
func refreshTokenForRepository(ctx context.Context, c *AcrCLIClient, repoName string) error {
181+
// For specific repository operations, request full permissions on that repository
182+
scope := fmt.Sprintf("repository:%s:*", repoName)
183+
return refreshAcrCLIClientToken(ctx, c, scope)
184+
}
185+
186+
// refreshTokenForCatalog obtains a new token with catalog access only.
187+
func refreshTokenForCatalog(ctx context.Context, c *AcrCLIClient) error {
188+
return refreshAcrCLIClientToken(ctx, c, "registry:catalog:*")
189+
}
190+
175191
// getExpiration is used to obtain the expiration out of a jwt token.
176192
func getExpiration(token string) (int64, error) {
177193
parser := jwt.Parser{SkipClaimsValidation: true}
@@ -200,7 +216,7 @@ func (c *AcrCLIClient) isExpired() bool {
200216
// GetAcrTags list the tags of a repository with their attributes.
201217
func (c *AcrCLIClient) GetAcrTags(ctx context.Context, repoName string, orderBy string, last string) (*acrapi.RepositoryTagsType, error) {
202218
if c.isExpired() {
203-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
219+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
204220
return nil, err
205221
}
206222
}
@@ -215,7 +231,7 @@ func (c *AcrCLIClient) GetAcrTags(ctx context.Context, repoName string, orderBy
215231
// DeleteAcrTag deletes the tag by reference.
216232
func (c *AcrCLIClient) DeleteAcrTag(ctx context.Context, repoName string, reference string) (*autorest.Response, error) {
217233
if c.isExpired() {
218-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
234+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
219235
return nil, err
220236
}
221237
}
@@ -229,7 +245,7 @@ func (c *AcrCLIClient) DeleteAcrTag(ctx context.Context, repoName string, refere
229245
// GetAcrManifests list all the manifest in a repository with their attributes.
230246
func (c *AcrCLIClient) GetAcrManifests(ctx context.Context, repoName string, orderBy string, last string) (*acrapi.Manifests, error) {
231247
if c.isExpired() {
232-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
248+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
233249
return nil, err
234250
}
235251
}
@@ -243,7 +259,7 @@ func (c *AcrCLIClient) GetAcrManifests(ctx context.Context, repoName string, ord
243259
// DeleteManifest deletes a manifest using the digest as a reference.
244260
func (c *AcrCLIClient) DeleteManifest(ctx context.Context, repoName string, reference string) (*autorest.Response, error) {
245261
if c.isExpired() {
246-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
262+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
247263
return nil, err
248264
}
249265
}
@@ -258,7 +274,7 @@ func (c *AcrCLIClient) DeleteManifest(ctx context.Context, repoName string, refe
258274
// This is used when a manifest list is wanted, first the bytes are obtained and then unmarshalled into a new struct.
259275
func (c *AcrCLIClient) GetManifest(ctx context.Context, repoName string, reference string) ([]byte, error) {
260276
if c.isExpired() {
261-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
277+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
262278
return nil, err
263279
}
264280
}
@@ -298,7 +314,7 @@ func (c *AcrCLIClient) GetManifest(ctx context.Context, repoName string, referen
298314
// GetAcrManifestAttributes gets the attributes of a manifest.
299315
func (c *AcrCLIClient) GetAcrManifestAttributes(ctx context.Context, repoName string, reference string) (*acrapi.ManifestAttributes, error) {
300316
if c.isExpired() {
301-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
317+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
302318
return nil, err
303319
}
304320
}
@@ -312,7 +328,7 @@ func (c *AcrCLIClient) GetAcrManifestAttributes(ctx context.Context, repoName st
312328
// UpdateAcrTagAttributes updates tag attributes to enable/disable deletion and writing.
313329
func (c *AcrCLIClient) UpdateAcrTagAttributes(ctx context.Context, repoName string, reference string, value *acrapi.ChangeableAttributes) (*autorest.Response, error) {
314330
if c.isExpired() {
315-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
331+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
316332
return nil, err
317333
}
318334
}
@@ -326,7 +342,7 @@ func (c *AcrCLIClient) UpdateAcrTagAttributes(ctx context.Context, repoName stri
326342
// UpdateAcrManifestAttributes updates manifest attributes to enable/disable deletion and writing.
327343
func (c *AcrCLIClient) UpdateAcrManifestAttributes(ctx context.Context, repoName string, reference string, value *acrapi.ChangeableAttributes) (*autorest.Response, error) {
328344
if c.isExpired() {
329-
if err := refreshAcrCLIClientToken(ctx, c); err != nil {
345+
if err := refreshTokenForRepository(ctx, c, repoName); err != nil {
330346
return nil, err
331347
}
332348
}

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)