Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 35 additions & 61 deletions pkg/api/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ type Controller struct {
licenseManager license.Manager
}

type AuthOpts struct {
// this function is called in authorizeCallback when authorization fails
onFailure func(w http.ResponseWriter, r *http.Request, httpStatusCode int, error interface{})
}

var usageCounter = stats.NewUsageCounter()

func NewController(cfg config.Config, catalog *catalog.Catalog, authenticator auth.Authenticator, authService auth.Service, authenticationService authentication.Service, blockAdapter block.Adapter, metadataManager auth.MetadataManager, migrator Migrator, collector stats.Collector, actions actionsHandler, auditChecker AuditChecker, logger logging.Logger, sessionStore sessions.Store, pathProvider upload.PathProvider, usageReporter stats.UsageReporterOperations, licenseManager license.Manager) *Controller {
Expand Down Expand Up @@ -3377,12 +3382,7 @@ func (c *Controller) DeleteObject(w http.ResponseWriter, r *http.Request, reposi
}

func (c *Controller) UploadObjectPreflight(w http.ResponseWriter, r *http.Request, repository, branch string, params apigen.UploadObjectPreflightParams) {
if !c.authorize(w, r, permissions.Node{
Permission: permissions.Permission{
Action: permissions.WriteObjectAction,
Resource: permissions.ObjectArn(repository, params.Path),
},
}) {
if !c.authorizeReq(w, r, "UploadObjectPreflight", permissions.PermissionParams{Repository: &repository, Path: &params.Path}, nil) {
return
}

Expand All @@ -3393,12 +3393,7 @@ func (c *Controller) UploadObjectPreflight(w http.ResponseWriter, r *http.Reques
}

func (c *Controller) UploadObject(w http.ResponseWriter, r *http.Request, repository, branch string, params apigen.UploadObjectParams) {
if !c.authorize(w, r, permissions.Node{
Permission: permissions.Permission{
Action: permissions.WriteObjectAction,
Resource: permissions.ObjectArn(repository, params.Path),
},
}) {
if !c.authorizeReq(w, r, "UploadObject", permissions.PermissionParams{Repository: &repository, Path: &params.Path}, nil) {
return
}
ctx := r.Context()
Expand Down Expand Up @@ -3563,12 +3558,7 @@ func (c *Controller) UploadObject(w http.ResponseWriter, r *http.Request, reposi
}

func (c *Controller) StageObject(w http.ResponseWriter, r *http.Request, body apigen.StageObjectJSONRequestBody, repository, branch string, params apigen.StageObjectParams) {
if !c.authorize(w, r, permissions.Node{
Permission: permissions.Permission{
Action: permissions.WriteObjectAction,
Resource: permissions.ObjectArn(repository, params.Path),
},
}) {
if !c.authorizeReq(w, r, "StageObject", permissions.PermissionParams{Repository: &repository, Path: &params.Path}, nil) {
return
}
ctx := r.Context()
Expand Down Expand Up @@ -4383,12 +4373,7 @@ func (c *Controller) RestoreStatus(w http.ResponseWriter, r *http.Request, repos
}

func (c *Controller) CreateSymlinkFile(w http.ResponseWriter, r *http.Request, repository, branch string, params apigen.CreateSymlinkFileParams) {
if !c.authorize(w, r, permissions.Node{
Permission: permissions.Permission{
Action: permissions.WriteObjectAction,
Resource: permissions.ObjectArn(repository, branch),
},
}) {
if !c.authorizeReq(w, r, "CreateSymlinkFile", permissions.PermissionParams{Repository: &repository, Path: &branch}, nil) {
return
}
ctx := r.Context()
Expand Down Expand Up @@ -4582,14 +4567,10 @@ func (c *Controller) LogCommits(w http.ResponseWriter, r *http.Request, reposito
}

func (c *Controller) HeadObject(w http.ResponseWriter, r *http.Request, repository, ref string, params apigen.HeadObjectParams) {
if !c.authorizeCallback(w, r, permissions.Node{
Permission: permissions.Permission{
Action: permissions.ReadObjectAction,
Resource: permissions.ObjectArn(repository, params.Path),
},
}, func(w http.ResponseWriter, r *http.Request, code int, v interface{}) {
writeResponse(w, r, code, nil)
}) {
if !c.authorizeReq(w, r, "HeadObject", permissions.PermissionParams{Repository: &repository, Path: &params.Path},
&AuthOpts{onFailure: func(w http.ResponseWriter, r *http.Request, httpStatusCode int, error interface{}) {
writeResponse(w, r, httpStatusCode, nil)
}}) {
return
}
ctx := r.Context()
Expand Down Expand Up @@ -4724,12 +4705,7 @@ func (c *Controller) GetMetadataObject(w http.ResponseWriter, r *http.Request, r
}

func (c *Controller) GetObject(w http.ResponseWriter, r *http.Request, repository, ref string, params apigen.GetObjectParams) {
if !c.authorize(w, r, permissions.Node{
Permission: permissions.Permission{
Action: permissions.ReadObjectAction,
Resource: permissions.ObjectArn(repository, params.Path),
},
}) {
if !c.authorizeReq(w, r, "GetObject", permissions.PermissionParams{Repository: &repository, Path: &params.Path}, nil) {
return
}
ctx := r.Context()
Expand Down Expand Up @@ -4947,12 +4923,7 @@ func (c *Controller) ListObjects(w http.ResponseWriter, r *http.Request, reposit
}

func (c *Controller) StatObject(w http.ResponseWriter, r *http.Request, repository, ref string, params apigen.StatObjectParams) {
if !c.authorize(w, r, permissions.Node{
Permission: permissions.Permission{
Action: permissions.ReadObjectAction,
Resource: permissions.ObjectArn(repository, params.Path),
},
}) {
if !c.authorizeReq(w, r, "StatObject", permissions.PermissionParams{Repository: &repository, Path: &params.Path}, nil) {
return
}
ctx := r.Context()
Expand Down Expand Up @@ -5015,12 +4986,7 @@ func (c *Controller) StatObject(w http.ResponseWriter, r *http.Request, reposito
}

func (c *Controller) UpdateObjectUserMetadata(w http.ResponseWriter, r *http.Request, body apigen.UpdateObjectUserMetadataJSONRequestBody, repository, branch string, params apigen.UpdateObjectUserMetadataParams) {
if !c.authorize(w, r, permissions.Node{
Permission: permissions.Permission{
Action: permissions.WriteObjectAction,
Resource: permissions.ObjectArn(repository, params.Path),
},
}) {
if !c.authorizeReq(w, r, "UpdateObjectUserMetadata", permissions.PermissionParams{Repository: &repository, Path: &params.Path}, nil) {
return
}
ctx := r.Context()
Expand All @@ -5036,12 +5002,7 @@ func (c *Controller) UpdateObjectUserMetadata(w http.ResponseWriter, r *http.Req
}

func (c *Controller) GetUnderlyingProperties(w http.ResponseWriter, r *http.Request, repository, ref string, params apigen.GetUnderlyingPropertiesParams) {
if !c.authorize(w, r, permissions.Node{
Permission: permissions.Permission{
Action: permissions.ReadObjectAction,
Resource: permissions.ObjectArn(repository, params.Path),
},
}) {
if !c.authorizeReq(w, r, "GetUnderlyingProperties", permissions.PermissionParams{Repository: &repository, Path: &params.Path}, nil) {
return
}
ctx := r.Context()
Expand Down Expand Up @@ -5914,27 +5875,27 @@ func paginationFor(hasMore bool, results interface{}, fieldName string) apigen.P
return pagination
}

func (c *Controller) authorizeCallback(w http.ResponseWriter, r *http.Request, perms permissions.Node, cb func(w http.ResponseWriter, r *http.Request, code int, v interface{})) bool {
func (c *Controller) authorizeCallback(w http.ResponseWriter, r *http.Request, perms permissions.Node, onFailure func(w http.ResponseWriter, r *http.Request, httpStatusCode int, error interface{})) bool {
ctx := r.Context()
user, err := auth.GetUser(ctx)
if err != nil {
cb(w, r, http.StatusUnauthorized, ErrAuthenticatingRequest)
onFailure(w, r, http.StatusUnauthorized, ErrAuthenticatingRequest)
return false
}
resp, err := c.Auth.Authorize(ctx, &auth.AuthorizationRequest{
Username: user.Username,
RequiredPermissions: perms,
})
if err != nil {
cb(w, r, http.StatusInternalServerError, err)
onFailure(w, r, http.StatusInternalServerError, err)
return false
}
if resp.Error != nil {
cb(w, r, http.StatusUnauthorized, resp.Error)
onFailure(w, r, http.StatusUnauthorized, resp.Error)
return false
}
if !resp.Allowed {
cb(w, r, http.StatusInternalServerError, "User does not have the required permissions")
onFailure(w, r, http.StatusInternalServerError, "User does not have the required permissions")
return false
}
return true
Expand All @@ -5944,6 +5905,19 @@ func (c *Controller) authorize(w http.ResponseWriter, r *http.Request, perms per
return c.authorizeCallback(w, r, perms, writeError)
}

func (c *Controller) authorizeReq(w http.ResponseWriter, r *http.Request, operationId string, params permissions.PermissionParams, opts *AuthOpts) bool {
desc := permissions.GetPermissionDescriptor(operationId)
if desc == nil {
c.Logger.Error(fmt.Sprintf("missing permission descriptor for %s", operationId))
return false
}
onFailure := writeError
if opts != nil && opts.onFailure != nil {
onFailure = opts.onFailure
}
return c.authorizeCallback(w, r, desc.Permission(params), onFailure)
}

func (c *Controller) isNameValid(name, nameType string) (bool, string) {
// URLs are % encoded. Allowing % signs in entity names would
// limit the ability to use these entity names in the URL for both
Expand Down
41 changes: 41 additions & 0 deletions pkg/permissions/permission.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,44 @@ func PolicyArn(policyID string) string {
func ExternalPrincipalArn(principalID string) string {
return authArnPrefix + "externalPrincipal/" + principalID
}

type PermissionParams struct {
Repository *string
Path *string
}

type PermissionDescriptor interface {
Permission(PermissionParams) Node
}

type ObjectPermission struct {
Action string
}

func (o *ObjectPermission) Permission(params PermissionParams) Node {
return Node{
Permission: Permission{
Action: o.Action,
Resource: ObjectArn(*params.Repository, *params.Path),
},
}
}

var readObjectPermission = ObjectPermission{Action: ReadObjectAction}
var writeObjectPermission = ObjectPermission{Action: WriteObjectAction}

var permissionByOp = map[string]PermissionDescriptor{
"HeadObject": &readObjectPermission,
"GetObject": &readObjectPermission,
"StatObject": &readObjectPermission,
"GetUnderlyingProperties": &readObjectPermission,
"StageObject": &writeObjectPermission,
"CreateSymlinkFile": &writeObjectPermission,
"UpdateObjectUserMetadata": &writeObjectPermission,
"UploadObject": &writeObjectPermission,
"UploadObjectPreflight": &writeObjectPermission,
}

func GetPermissionDescriptor(operationId string) PermissionDescriptor {
return permissionByOp[operationId]
}
Loading