Skip to content

Commit acc3aaa

Browse files
committed
chore(registry): more granular "access denied" messages
1 parent cf689d6 commit acc3aaa

File tree

5 files changed

+71
-37
lines changed

5 files changed

+71
-37
lines changed

internal/registry/authz/authorizer.go

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,27 @@ func NewAuthorizer() Authorizer {
3636
func (a *authorizer) Authorize(ctx context.Context, nameStr string, action Action) error {
3737
auth := auth.ArtifactsAuthentication.Require(ctx)
3838

39-
if action == ActionWrite &&
40-
(auth.CurrentCustomerOrgID() != nil ||
41-
auth.CurrentUserRole() == nil ||
42-
*auth.CurrentUserRole() == types.UserRoleReadOnly) {
43-
return ErrAccessDenied
39+
if action == ActionWrite {
40+
if auth.CurrentCustomerOrgID() != nil {
41+
return NewErrAccessDenied("customer user can not perform write action")
42+
}
43+
44+
if auth.CurrentUserRole() == nil {
45+
return NewErrAccessDenied("user with no role can not perform write action")
46+
}
47+
48+
if *auth.CurrentUserRole() == types.UserRoleReadOnly {
49+
return NewErrAccessDenied("read-only user can not perform write action")
50+
}
4451
}
4552

4653
org := auth.CurrentOrg()
4754
if name, err := name.Parse(nameStr); err != nil {
4855
return err
49-
} else if org.Slug == nil || *org.Slug != name.OrgName {
50-
return ErrAccessDenied
56+
} else if org.Slug == nil {
57+
return NewErrAccessDenied("organization has no slug")
58+
} else if *org.Slug != name.OrgName {
59+
return NewErrAccessDenied("organization slug does not match reference")
5160
}
5261

5362
return nil
@@ -57,18 +66,27 @@ func (a *authorizer) Authorize(ctx context.Context, nameStr string, action Actio
5766
func (a *authorizer) AuthorizeReference(ctx context.Context, nameStr string, reference string, action Action) error {
5867
auth := auth.ArtifactsAuthentication.Require(ctx)
5968

60-
if action == ActionWrite &&
61-
(auth.CurrentCustomerOrgID() != nil ||
62-
auth.CurrentUserRole() == nil ||
63-
*auth.CurrentUserRole() == types.UserRoleReadOnly) {
64-
return ErrAccessDenied
69+
if action == ActionWrite {
70+
if auth.CurrentCustomerOrgID() != nil {
71+
return NewErrAccessDenied("customer user can not perform write action")
72+
}
73+
74+
if auth.CurrentUserRole() == nil {
75+
return NewErrAccessDenied("user with no role can not perform write action")
76+
}
77+
78+
if *auth.CurrentUserRole() == types.UserRoleReadOnly {
79+
return NewErrAccessDenied("read-only user can not perform write action")
80+
}
6581
}
6682

6783
org := auth.CurrentOrg()
6884
if name, err := name.Parse(nameStr); err != nil {
6985
return err
70-
} else if org.Slug == nil || *org.Slug != name.OrgName {
71-
return ErrAccessDenied
86+
} else if org.Slug == nil {
87+
return NewErrAccessDenied("organization has no slug")
88+
} else if *org.Slug != name.OrgName {
89+
return NewErrAccessDenied("organization slug does not match reference")
7290
} else if action != ActionWrite && auth.CurrentCustomerOrgID() != nil {
7391
if org.HasFeature(types.FeatureLicensing) {
7492
err := db.CheckLicenseForArtifact(ctx,
@@ -79,7 +97,7 @@ func (a *authorizer) AuthorizeReference(ctx context.Context, nameStr string, ref
7997
*auth.CurrentOrgID(),
8098
)
8199
if errors.Is(err, apierrors.ErrForbidden) {
82-
return ErrAccessDenied
100+
return NewErrAccessDenied("license required")
83101
} else if err != nil {
84102
return err
85103
}
@@ -93,17 +111,24 @@ func (a *authorizer) AuthorizeReference(ctx context.Context, nameStr string, ref
93111
func (a *authorizer) AuthorizeBlob(ctx context.Context, digest digest.Digest, action Action) error {
94112
auth := auth.ArtifactsAuthentication.Require(ctx)
95113

96-
if action == ActionWrite &&
97-
(auth.CurrentCustomerOrgID() != nil ||
98-
auth.CurrentUserRole() == nil ||
99-
*auth.CurrentUserRole() == types.UserRoleReadOnly) {
100-
return ErrAccessDenied
114+
if action == ActionWrite {
115+
if auth.CurrentCustomerOrgID() != nil {
116+
return NewErrAccessDenied("customer user can not perform write action")
117+
}
118+
119+
if auth.CurrentUserRole() == nil {
120+
return NewErrAccessDenied("user with no role can not perform write action")
121+
}
122+
123+
if *auth.CurrentUserRole() == types.UserRoleReadOnly {
124+
return NewErrAccessDenied("read-only user can not perform write action")
125+
}
101126
}
102127

103128
if auth.CurrentCustomerOrgID() != nil && auth.CurrentOrg().HasFeature(types.FeatureLicensing) {
104129
err := db.CheckLicenseForArtifactBlob(ctx, digest.String(), *auth.CurrentCustomerOrgID(), *auth.CurrentOrgID())
105130
if errors.Is(err, apierrors.ErrForbidden) {
106-
return ErrAccessDenied
131+
return NewErrAccessDenied("license required")
107132
} else if err != nil {
108133
return err
109134
}

internal/registry/authz/error.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
package authz
22

3-
import "errors"
3+
import (
4+
"errors"
5+
"fmt"
6+
)
47

58
var ErrAccessDenied = errors.New("access denied")
9+
10+
func NewErrAccessDenied(message string) error {
11+
return fmt.Errorf("%w: %s", ErrAccessDenied, message)
12+
}

internal/registry/blob.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func (b *blobs) handle(resp http.ResponseWriter, req *http.Request) *regError {
8888
return regErrDigestInvalid
8989
} else if err := b.authz.AuthorizeBlob(req.Context(), h, authz.ActionStat); err != nil {
9090
if errors.Is(err, authz.ErrAccessDenied) {
91-
return regErrDenied
91+
return regErrDenied(err.Error())
9292
} else if errors.Is(err, registryerror.ErrInvalidArtifactName) {
9393
return regErrNameInvalid
9494
}
@@ -99,7 +99,7 @@ func (b *blobs) handle(resp http.ResponseWriter, req *http.Request) *regError {
9999
if h, err := digest.Parse(target); err == nil {
100100
if err := b.authz.AuthorizeBlob(req.Context(), h, authz.ActionRead); err != nil {
101101
if errors.Is(err, authz.ErrAccessDenied) {
102-
return regErrDenied
102+
return regErrDenied(err.Error())
103103
} else if errors.Is(err, registryerror.ErrInvalidArtifactName) {
104104
return regErrNameInvalid
105105
}
@@ -112,7 +112,7 @@ func (b *blobs) handle(resp http.ResponseWriter, req *http.Request) *regError {
112112
case http.MethodPost:
113113
if err := b.authz.Authorize(req.Context(), repo, authz.ActionWrite); err != nil {
114114
if errors.Is(err, authz.ErrAccessDenied) {
115-
return regErrDenied
115+
return regErrDenied(err.Error())
116116
} else if errors.Is(err, registryerror.ErrInvalidArtifactName) {
117117
return regErrNameInvalid
118118
}
@@ -122,7 +122,7 @@ func (b *blobs) handle(resp http.ResponseWriter, req *http.Request) *regError {
122122
case http.MethodPatch:
123123
if err := b.authz.Authorize(req.Context(), repo, authz.ActionWrite); err != nil {
124124
if errors.Is(err, authz.ErrAccessDenied) {
125-
return regErrDenied
125+
return regErrDenied(err.Error())
126126
} else if errors.Is(err, registryerror.ErrInvalidArtifactName) {
127127
return regErrNameInvalid
128128
}
@@ -134,7 +134,7 @@ func (b *blobs) handle(resp http.ResponseWriter, req *http.Request) *regError {
134134
return regErrDigestInvalid
135135
} else if err := b.authz.AuthorizeBlob(req.Context(), h, authz.ActionWrite); err != nil {
136136
if errors.Is(err, authz.ErrAccessDenied) {
137-
return regErrDenied
137+
return regErrDenied(err.Error())
138138
} else if errors.Is(err, registryerror.ErrInvalidArtifactName) {
139139
return regErrNameInvalid
140140
}

internal/registry/error.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,12 @@ var regErrMethodUnknown = &regError{
113113
Message: "We don't understand your method + url",
114114
}
115115

116-
var regErrDenied = &regError{
117-
Status: http.StatusForbidden,
118-
Code: "DENIED",
119-
Message: "Access to the resource has been denied",
116+
func regErrDenied(message string) *regError {
117+
return &regError{
118+
Status: http.StatusForbidden,
119+
Code: "DENIED",
120+
Message: message,
121+
}
120122
}
121123

122124
var regErrDeniedQuotaExceeded = &regError{

internal/registry/manifest.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func (handler *manifests) handle(resp http.ResponseWriter, req *http.Request) *r
109109
case http.MethodGet:
110110
if err := handler.authz.AuthorizeReference(req.Context(), repo, target, authz.ActionRead); err != nil {
111111
if errors.Is(err, authz.ErrAccessDenied) {
112-
return regErrDenied
112+
return regErrDenied(err.Error())
113113
} else if errors.Is(err, registryerror.ErrInvalidArtifactName) {
114114
return regErrNameInvalid
115115
}
@@ -119,7 +119,7 @@ func (handler *manifests) handle(resp http.ResponseWriter, req *http.Request) *r
119119
case http.MethodHead:
120120
if err := handler.authz.AuthorizeReference(req.Context(), repo, target, authz.ActionStat); err != nil {
121121
if errors.Is(err, authz.ErrAccessDenied) {
122-
return regErrDenied
122+
return regErrDenied(err.Error())
123123
} else if errors.Is(err, registryerror.ErrInvalidArtifactName) {
124124
return regErrNameInvalid
125125
}
@@ -129,7 +129,7 @@ func (handler *manifests) handle(resp http.ResponseWriter, req *http.Request) *r
129129
case http.MethodPut:
130130
if err := handler.authz.AuthorizeReference(req.Context(), repo, target, authz.ActionWrite); err != nil {
131131
if errors.Is(err, authz.ErrAccessDenied) {
132-
return regErrDenied
132+
return regErrDenied(err.Error())
133133
} else if errors.Is(err, registryerror.ErrInvalidArtifactName) {
134134
return regErrNameInvalid
135135
}
@@ -139,7 +139,7 @@ func (handler *manifests) handle(resp http.ResponseWriter, req *http.Request) *r
139139
case http.MethodDelete:
140140
if err := handler.authz.AuthorizeReference(req.Context(), repo, target, authz.ActionWrite); err != nil {
141141
if errors.Is(err, authz.ErrAccessDenied) {
142-
return regErrDenied
142+
return regErrDenied(err.Error())
143143
} else if errors.Is(err, registryerror.ErrInvalidArtifactName) {
144144
return regErrNameInvalid
145145
}
@@ -159,7 +159,7 @@ func (m *manifests) handleTags(resp http.ResponseWriter, req *http.Request) *reg
159159
if req.Method == http.MethodGet {
160160
if err := m.authz.Authorize(req.Context(), repo, authz.ActionRead); err != nil {
161161
if errors.Is(err, authz.ErrAccessDenied) {
162-
return regErrDenied
162+
return regErrDenied(err.Error())
163163
} else if errors.Is(err, registryerror.ErrInvalidArtifactName) {
164164
return regErrNameInvalid
165165
}
@@ -252,7 +252,7 @@ func (m *manifests) handleReferrers(resp http.ResponseWriter, req *http.Request)
252252

253253
if err := m.authz.AuthorizeReference(req.Context(), repo, target, authz.ActionRead); err != nil {
254254
if errors.Is(err, authz.ErrAccessDenied) {
255-
return regErrDenied
255+
return regErrDenied(err.Error())
256256
} else if errors.Is(err, registryerror.ErrInvalidArtifactName) {
257257
return regErrNameInvalid
258258
}

0 commit comments

Comments
 (0)