mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-27 12:46:03 +01:00
Add permission check for audit logs API (#11154)
add a base method to require system admin permission Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
parent
15d2a93aa2
commit
168637a743
@ -62,10 +62,10 @@ func (d *dao) Count(ctx context.Context, query *q.Query) (int64, error) {
|
|||||||
func (d *dao) List(ctx context.Context, query *q.Query) ([]*model.AuditLog, error) {
|
func (d *dao) List(ctx context.Context, query *q.Query) ([]*model.AuditLog, error) {
|
||||||
audit := []*model.AuditLog{}
|
audit := []*model.AuditLog{}
|
||||||
qs, err := orm.QuerySetter(ctx, &model.AuditLog{}, query)
|
qs, err := orm.QuerySetter(ctx, &model.AuditLog{}, query)
|
||||||
qs = qs.OrderBy("-op_time")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
qs = qs.OrderBy("-op_time")
|
||||||
if _, err = qs.All(&audit); err != nil {
|
if _, err = qs.All(&audit); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ func (d *daoTestSuite) SetupSuite() {
|
|||||||
artifactID, err := d.dao.Create(d.ctx, &model.AuditLog{
|
artifactID, err := d.dao.Create(d.ctx, &model.AuditLog{
|
||||||
Operation: "Create",
|
Operation: "Create",
|
||||||
ResourceType: "artifact",
|
ResourceType: "artifact",
|
||||||
Resource: "library/hello-world",
|
Resource: "library/test-audit",
|
||||||
Username: "admin",
|
Username: "admin",
|
||||||
})
|
})
|
||||||
d.Require().Nil(err)
|
d.Require().Nil(err)
|
||||||
@ -59,7 +59,7 @@ func (d *daoTestSuite) TestCount() {
|
|||||||
d.True(total > 0)
|
d.True(total > 0)
|
||||||
total, err = d.dao.Count(d.ctx, &q.Query{
|
total, err = d.dao.Count(d.ctx, &q.Query{
|
||||||
Keywords: map[string]interface{}{
|
Keywords: map[string]interface{}{
|
||||||
"ResourceType": "artifact",
|
"Resource": "library/test-audit",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
d.Require().Nil(err)
|
d.Require().Nil(err)
|
||||||
@ -74,7 +74,7 @@ func (d *daoTestSuite) TestList() {
|
|||||||
// query by repository ID and name
|
// query by repository ID and name
|
||||||
audits, err = d.dao.List(d.ctx, &q.Query{
|
audits, err = d.dao.List(d.ctx, &q.Query{
|
||||||
Keywords: map[string]interface{}{
|
Keywords: map[string]interface{}{
|
||||||
"ResourceType": "artifact",
|
"Resource": "library/test-audit",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
d.Require().Nil(err)
|
d.Require().Nil(err)
|
||||||
@ -94,6 +94,50 @@ func (d *daoTestSuite) TestGet() {
|
|||||||
d.Equal(d.auditID, audit.ID)
|
d.Equal(d.auditID, audit.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *daoTestSuite) TestListPIDs() {
|
||||||
|
// get the non-exist tag
|
||||||
|
id1, err := d.dao.Create(d.ctx, &model.AuditLog{
|
||||||
|
Operation: "Create",
|
||||||
|
ResourceType: "artifact",
|
||||||
|
Resource: "library/hello-world",
|
||||||
|
Username: "admin",
|
||||||
|
ProjectID: 11,
|
||||||
|
})
|
||||||
|
d.Require().Nil(err)
|
||||||
|
id2, err := d.dao.Create(d.ctx, &model.AuditLog{
|
||||||
|
Operation: "Create",
|
||||||
|
ResourceType: "artifact",
|
||||||
|
Resource: "library/hello-world",
|
||||||
|
Username: "admin",
|
||||||
|
ProjectID: 12,
|
||||||
|
})
|
||||||
|
d.Require().Nil(err)
|
||||||
|
id3, err := d.dao.Create(d.ctx, &model.AuditLog{
|
||||||
|
Operation: "Delete",
|
||||||
|
ResourceType: "artifact",
|
||||||
|
Resource: "library/hello-world",
|
||||||
|
Username: "admin",
|
||||||
|
ProjectID: 13,
|
||||||
|
})
|
||||||
|
d.Require().Nil(err)
|
||||||
|
|
||||||
|
// query by repository ID and name
|
||||||
|
ol := &q.OrList{}
|
||||||
|
for _, item := range []int64{11, 12, 13} {
|
||||||
|
ol.Values = append(ol.Values, item)
|
||||||
|
}
|
||||||
|
audits, err := d.dao.List(d.ctx, &q.Query{
|
||||||
|
Keywords: map[string]interface{}{
|
||||||
|
"ProjectID": ol,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
d.Require().Nil(err)
|
||||||
|
d.Require().Equal(3, len(audits))
|
||||||
|
d.dao.Delete(d.ctx, id1)
|
||||||
|
d.dao.Delete(d.ctx, id2)
|
||||||
|
d.dao.Delete(d.ctx, id3)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *daoTestSuite) TestCreate() {
|
func (d *daoTestSuite) TestCreate() {
|
||||||
// conflict
|
// conflict
|
||||||
audit := &model.AuditLog{
|
audit := &model.AuditLog{
|
||||||
|
@ -2,8 +2,14 @@ package handler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/go-openapi/runtime/middleware"
|
"github.com/go-openapi/runtime/middleware"
|
||||||
|
"github.com/goharbor/harbor/src/common/rbac"
|
||||||
|
"github.com/goharbor/harbor/src/common/security"
|
||||||
|
ierror "github.com/goharbor/harbor/src/internal/error"
|
||||||
"github.com/goharbor/harbor/src/pkg/audit"
|
"github.com/goharbor/harbor/src/pkg/audit"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/q"
|
||||||
"github.com/goharbor/harbor/src/server/v2.0/models"
|
"github.com/goharbor/harbor/src/server/v2.0/models"
|
||||||
"github.com/goharbor/harbor/src/server/v2.0/restapi/operations/auditlog"
|
"github.com/goharbor/harbor/src/server/v2.0/restapi/operations/auditlog"
|
||||||
operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/auditlog"
|
operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/auditlog"
|
||||||
@ -21,14 +27,34 @@ type auditlogAPI struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *auditlogAPI) ListAuditLogs(ctx context.Context, params auditlog.ListAuditLogsParams) middleware.Responder {
|
func (a *auditlogAPI) ListAuditLogs(ctx context.Context, params auditlog.ListAuditLogsParams) middleware.Responder {
|
||||||
// ToDo enable permission check
|
secCtx, ok := security.FromContext(ctx)
|
||||||
// if !a.HasPermission(ctx, rbac.ActionList, rbac.ResourceLog) {
|
if !ok {
|
||||||
// return a.SendError(ctx, ierror.ForbiddenError(nil))
|
return a.SendError(ctx, ierror.UnauthorizedError(errors.New("security context not found")))
|
||||||
// }
|
}
|
||||||
|
if !secCtx.IsAuthenticated() {
|
||||||
|
return a.SendError(ctx, ierror.UnauthorizedError(nil).WithMessage(secCtx.GetUsername()))
|
||||||
|
}
|
||||||
query, err := a.BuildQuery(ctx, params.Q, params.Page, params.PageSize)
|
query, err := a.BuildQuery(ctx, params.Q, params.Page, params.PageSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return a.SendError(ctx, err)
|
return a.SendError(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !secCtx.IsSysAdmin() {
|
||||||
|
// ToDo remove the dependency on GetMyProjects()
|
||||||
|
projects, err := secCtx.GetMyProjects()
|
||||||
|
if err != nil {
|
||||||
|
return a.SendError(ctx, fmt.Errorf(
|
||||||
|
"failed to get projects of user %s: %v", secCtx.GetUsername(), err))
|
||||||
|
}
|
||||||
|
ol := &q.OrList{}
|
||||||
|
for _, project := range projects {
|
||||||
|
if a.HasProjectPermission(ctx, project.ProjectID, rbac.ActionList, rbac.ResourceLog) {
|
||||||
|
ol.Values = append(ol.Values, project.ProjectID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query.Keywords["ProjectID"] = ol
|
||||||
|
}
|
||||||
|
|
||||||
total, err := a.auditMgr.Count(ctx, query)
|
total, err := a.auditMgr.Count(ctx, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return a.SendError(ctx, err)
|
return a.SendError(ctx, err)
|
||||||
|
@ -99,6 +99,21 @@ func (b *BaseAPI) RequireProjectAccess(ctx context.Context, projectIDOrName inte
|
|||||||
return ierror.ForbiddenError(nil)
|
return ierror.ForbiddenError(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequireSysAdmin checks the system admin permission according to the security context
|
||||||
|
func (b *BaseAPI) RequireSysAdmin(ctx context.Context) error {
|
||||||
|
secCtx, ok := security.FromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return ierror.UnauthorizedError(errors.New("security context not found"))
|
||||||
|
}
|
||||||
|
if !secCtx.IsAuthenticated() {
|
||||||
|
return ierror.UnauthorizedError(nil)
|
||||||
|
}
|
||||||
|
if !secCtx.IsSysAdmin() {
|
||||||
|
return ierror.ForbiddenError(nil).WithMessage(secCtx.GetUsername())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// BuildQuery builds the query model according to the query string
|
// BuildQuery builds the query model according to the query string
|
||||||
func (b *BaseAPI) BuildQuery(ctx context.Context, query *string, pageNumber, pageSize *int64) (*q.Query, error) {
|
func (b *BaseAPI) BuildQuery(ctx context.Context, query *string, pageNumber, pageSize *int64) (*q.Query, error) {
|
||||||
var (
|
var (
|
||||||
|
Loading…
Reference in New Issue
Block a user