mirror of
https://github.com/goharbor/harbor
synced 2025-04-13 12:33:51 +00:00
Implement audit log ext API (#21414)
Signed-off-by: stonezdj <stone.zhang@broadcom.com>
This commit is contained in:
parent
3b655213c0
commit
4d5bc19866
|
@ -6940,6 +6940,7 @@ definitions:
|
||||||
description: The operation's detail description
|
description: The operation's detail description
|
||||||
operation_result:
|
operation_result:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
x-omitempty: false
|
||||||
description: the operation's result, true for success, false for fail
|
description: the operation's result, true for success, false for fail
|
||||||
op_time:
|
op_time:
|
||||||
type: string
|
type: string
|
||||||
|
|
|
@ -127,8 +127,6 @@ var (
|
||||||
|
|
||||||
{Resource: rbac.ResourceMetadata, Action: rbac.ActionRead},
|
{Resource: rbac.ResourceMetadata, Action: rbac.ActionRead},
|
||||||
|
|
||||||
{Resource: rbac.ResourceLog, Action: rbac.ActionList},
|
|
||||||
|
|
||||||
{Resource: rbac.ResourceQuota, Action: rbac.ActionRead},
|
{Resource: rbac.ResourceQuota, Action: rbac.ActionRead},
|
||||||
|
|
||||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionCreate},
|
{Resource: rbac.ResourceLabel, Action: rbac.ActionCreate},
|
||||||
|
@ -199,8 +197,6 @@ var (
|
||||||
{Resource: rbac.ResourceMember, Action: rbac.ActionRead},
|
{Resource: rbac.ResourceMember, Action: rbac.ActionRead},
|
||||||
{Resource: rbac.ResourceMember, Action: rbac.ActionList},
|
{Resource: rbac.ResourceMember, Action: rbac.ActionList},
|
||||||
|
|
||||||
{Resource: rbac.ResourceLog, Action: rbac.ActionList},
|
|
||||||
|
|
||||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
|
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
|
||||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionList},
|
{Resource: rbac.ResourceLabel, Action: rbac.ActionList},
|
||||||
|
|
||||||
|
@ -254,8 +250,6 @@ var (
|
||||||
{Resource: rbac.ResourceMember, Action: rbac.ActionRead},
|
{Resource: rbac.ResourceMember, Action: rbac.ActionRead},
|
||||||
{Resource: rbac.ResourceMember, Action: rbac.ActionList},
|
{Resource: rbac.ResourceMember, Action: rbac.ActionList},
|
||||||
|
|
||||||
{Resource: rbac.ResourceLog, Action: rbac.ActionList},
|
|
||||||
|
|
||||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
|
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
|
||||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionList},
|
{Resource: rbac.ResourceLabel, Action: rbac.ActionList},
|
||||||
|
|
||||||
|
|
|
@ -28,21 +28,25 @@ import (
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
"github.com/goharbor/harbor/src/pkg/audit"
|
"github.com/goharbor/harbor/src/pkg/audit"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/auditext"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/auditext/model"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newAuditLogAPI() *auditlogAPI {
|
func newAuditLogAPI() *auditlogAPI {
|
||||||
return &auditlogAPI{
|
return &auditlogAPI{
|
||||||
auditMgr: audit.Mgr,
|
auditMgr: audit.Mgr,
|
||||||
projectCtl: project.Ctl,
|
auditextMgr: auditext.Mgr,
|
||||||
|
projectCtl: project.Ctl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type auditlogAPI struct {
|
type auditlogAPI struct {
|
||||||
BaseAPI
|
BaseAPI
|
||||||
auditMgr audit.Manager
|
auditMgr audit.Manager
|
||||||
projectCtl project.Controller
|
auditextMgr auditext.Manager
|
||||||
|
projectCtl project.Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *auditlogAPI) ListAuditLogs(ctx context.Context, params auditlog.ListAuditLogsParams) middleware.Responder {
|
func (a *auditlogAPI) ListAuditLogs(ctx context.Context, params auditlog.ListAuditLogsParams) middleware.Responder {
|
||||||
|
@ -111,14 +115,85 @@ func (a *auditlogAPI) ListAuditLogs(ctx context.Context, params auditlog.ListAud
|
||||||
WithPayload(auditLogs)
|
WithPayload(auditLogs)
|
||||||
}
|
}
|
||||||
func (a *auditlogAPI) ListAuditLogExts(ctx context.Context, params auditlog.ListAuditLogExtsParams) middleware.Responder {
|
func (a *auditlogAPI) ListAuditLogExts(ctx context.Context, params auditlog.ListAuditLogExtsParams) middleware.Responder {
|
||||||
// TODO: implement this method
|
secCtx, ok := security.FromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return a.SendError(ctx, errors.UnauthorizedError(errors.New("security context not found")))
|
||||||
|
}
|
||||||
|
if !secCtx.IsAuthenticated() {
|
||||||
|
return a.SendError(ctx, errors.UnauthorizedError(nil).WithMessage(secCtx.GetUsername()))
|
||||||
|
}
|
||||||
|
query, err := a.BuildQuery(ctx, params.Q, params.Sort, params.Page, params.PageSize)
|
||||||
|
if err != nil {
|
||||||
|
return a.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceAuditLog); err != nil {
|
||||||
|
ol := &q.OrList{}
|
||||||
|
if sc, ok := secCtx.(*local.SecurityContext); ok && sc.IsAuthenticated() {
|
||||||
|
user := sc.User()
|
||||||
|
member := &project.MemberQuery{
|
||||||
|
UserID: user.UserID,
|
||||||
|
GroupIDs: user.GroupIDs,
|
||||||
|
}
|
||||||
|
|
||||||
|
projects, err := a.projectCtl.List(ctx, q.New(q.KeyWords{"member": member}), project.Metadata(false))
|
||||||
|
if err != nil {
|
||||||
|
return a.SendError(ctx, fmt.Errorf(
|
||||||
|
"failed to get projects of user %s: %v", secCtx.GetUsername(), err))
|
||||||
|
}
|
||||||
|
for _, project := range projects {
|
||||||
|
if a.HasProjectPermission(ctx, project.ProjectID, rbac.ActionList, rbac.ResourceLog) {
|
||||||
|
ol.Values = append(ol.Values, project.ProjectID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// make sure no project will be selected with the query
|
||||||
|
if len(ol.Values) == 0 {
|
||||||
|
ol.Values = append(ol.Values, -1)
|
||||||
|
}
|
||||||
|
query.Keywords["ProjectID"] = ol
|
||||||
|
}
|
||||||
|
|
||||||
|
total, err := a.auditextMgr.Count(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return a.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
logs, err := a.auditextMgr.List(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return a.SendError(ctx, err)
|
||||||
|
}
|
||||||
return auditlog.NewListAuditLogExtsOK().
|
return auditlog.NewListAuditLogExtsOK().
|
||||||
WithXTotalCount(0).
|
WithXTotalCount(total).
|
||||||
WithLink(a.Links(ctx, params.HTTPRequest.URL, 0, 0, 0).String()).
|
WithLink(a.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
|
||||||
WithPayload(nil)
|
WithPayload(convertToModelAuditLogExt(logs))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *auditlogAPI) ListAuditLogEventTypes(_ context.Context, _ auditlog.ListAuditLogEventTypesParams) middleware.Responder {
|
func (a *auditlogAPI) ListAuditLogEventTypes(ctx context.Context, _ auditlog.ListAuditLogEventTypesParams) middleware.Responder {
|
||||||
// TODO: implement this method
|
if err := a.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceAuditLog); err != nil {
|
||||||
return auditlog.NewListAuditLogEventTypesOK().WithPayload(nil)
|
return a.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
var eventTypes []*models.AuditLogEventType
|
||||||
|
for _, v := range model.EventTypes {
|
||||||
|
eventTypes = append(eventTypes, &models.AuditLogEventType{
|
||||||
|
EventType: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return auditlog.NewListAuditLogEventTypesOK().WithPayload(eventTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertToModelAuditLogExt(logs []*model.AuditLogExt) []*models.AuditLogExt {
|
||||||
|
var auditLogs []*models.AuditLogExt
|
||||||
|
for _, log := range logs {
|
||||||
|
auditLogs = append(auditLogs, &models.AuditLogExt{
|
||||||
|
ID: log.ID,
|
||||||
|
Resource: log.Resource,
|
||||||
|
ResourceType: log.ResourceType,
|
||||||
|
Username: log.Username,
|
||||||
|
Operation: log.Operation,
|
||||||
|
OperationDescription: log.OperationDescription,
|
||||||
|
OperationResult: log.OperationResult,
|
||||||
|
OpTime: strfmt.DateTime(log.OpTime),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return auditLogs
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ import (
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
"github.com/goharbor/harbor/src/pkg"
|
"github.com/goharbor/harbor/src/pkg"
|
||||||
"github.com/goharbor/harbor/src/pkg/audit"
|
"github.com/goharbor/harbor/src/pkg/audit"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/auditext"
|
||||||
"github.com/goharbor/harbor/src/pkg/member"
|
"github.com/goharbor/harbor/src/pkg/member"
|
||||||
"github.com/goharbor/harbor/src/pkg/project/metadata"
|
"github.com/goharbor/harbor/src/pkg/project/metadata"
|
||||||
pkgModels "github.com/goharbor/harbor/src/pkg/project/models"
|
pkgModels "github.com/goharbor/harbor/src/pkg/project/models"
|
||||||
|
@ -66,6 +67,7 @@ func newProjectAPI() *projectAPI {
|
||||||
return &projectAPI{
|
return &projectAPI{
|
||||||
auditMgr: audit.Mgr,
|
auditMgr: audit.Mgr,
|
||||||
artCtl: artifact.Ctl,
|
artCtl: artifact.Ctl,
|
||||||
|
auditextMgr: auditext.Mgr,
|
||||||
metadataMgr: pkg.ProjectMetaMgr,
|
metadataMgr: pkg.ProjectMetaMgr,
|
||||||
userCtl: user.Ctl,
|
userCtl: user.Ctl,
|
||||||
repositoryCtl: repository.Ctl,
|
repositoryCtl: repository.Ctl,
|
||||||
|
@ -82,6 +84,7 @@ func newProjectAPI() *projectAPI {
|
||||||
type projectAPI struct {
|
type projectAPI struct {
|
||||||
BaseAPI
|
BaseAPI
|
||||||
auditMgr audit.Manager
|
auditMgr audit.Manager
|
||||||
|
auditextMgr auditext.Manager
|
||||||
artCtl artifact.Controller
|
artCtl artifact.Controller
|
||||||
metadataMgr metadata.Manager
|
metadataMgr metadata.Manager
|
||||||
userCtl user.Controller
|
userCtl user.Controller
|
||||||
|
@ -940,9 +943,29 @@ func highestRole(roles []int) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *projectAPI) GetLogExts(ctx context.Context, params operation.GetLogExtsParams) middleware.Responder {
|
func (a *projectAPI) GetLogExts(ctx context.Context, params operation.GetLogExtsParams) middleware.Responder {
|
||||||
// TODO: implement the function
|
if err := a.RequireProjectAccess(ctx, params.ProjectName, rbac.ActionList, rbac.ResourceLog); err != nil {
|
||||||
|
return a.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
pro, err := a.projectCtl.GetByName(ctx, params.ProjectName)
|
||||||
|
if err != nil {
|
||||||
|
return a.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
query, err := a.BuildQuery(ctx, params.Q, params.Sort, params.Page, params.PageSize)
|
||||||
|
if err != nil {
|
||||||
|
return a.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
query.Keywords["ProjectID"] = pro.ProjectID
|
||||||
|
|
||||||
|
total, err := a.auditextMgr.Count(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return a.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
logs, err := a.auditextMgr.List(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return a.SendError(ctx, err)
|
||||||
|
}
|
||||||
return operation.NewGetLogExtsOK().
|
return operation.NewGetLogExtsOK().
|
||||||
WithXTotalCount(0).
|
WithXTotalCount(total).
|
||||||
WithLink(a.Links(ctx, params.HTTPRequest.URL, 0, 0, 15).String()).
|
WithLink(a.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
|
||||||
WithPayload(nil)
|
WithPayload(convertToModelAuditLogExt(logs))
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ class TestAssignRoleToLdapGroup(unittest.TestCase):
|
||||||
self.project.add_project_members(project_id, member_role_id = 3, _ldap_group_dn = "cn=harbor_guest,ou=groups,dc=example,dc=com", **ADMIN_CLIENT)
|
self.project.add_project_members(project_id, member_role_id = 3, _ldap_group_dn = "cn=harbor_guest,ou=groups,dc=example,dc=com", **ADMIN_CLIENT)
|
||||||
|
|
||||||
projects = self.project.get_projects(dict(name=project_name), **USER_ADMIN)
|
projects = self.project.get_projects(dict(name=project_name), **USER_ADMIN)
|
||||||
self.assertTrue(len(projects) == 1)
|
self.assertTrue(len(projects) == 1)
|
||||||
self.assertEqual(1, projects[0].current_user_role_id)
|
self.assertEqual(1, projects[0].current_user_role_id)
|
||||||
|
|
||||||
#Mike has logged in harbor in previous test.
|
#Mike has logged in harbor in previous test.
|
||||||
|
@ -80,8 +80,8 @@ class TestAssignRoleToLdapGroup(unittest.TestCase):
|
||||||
self.assertTrue(len(artifacts) == 0)
|
self.assertTrue(len(artifacts) == 0)
|
||||||
|
|
||||||
self.assertTrue(self.project.query_user_logs(project_name, **USER_ADMIN)>0, "admin user can see logs")
|
self.assertTrue(self.project.query_user_logs(project_name, **USER_ADMIN)>0, "admin user can see logs")
|
||||||
self.assertTrue(self.project.query_user_logs(project_name, **USER_DEV)>0, "dev user can see logs")
|
self.assertTrue(self.project.query_user_logs(project_name, status_code=403, **USER_DEV)==0, "dev user can not see any logs")
|
||||||
self.assertTrue(self.project.query_user_logs(project_name, **USER_GUEST)>0, "guest user can see logs")
|
self.assertTrue(self.project.query_user_logs(project_name, status_code=403, **USER_GUEST)==0, "guest user can not see any logs")
|
||||||
self.assertTrue(self.project.query_user_logs(project_name, status_code=403, **USER_TEST)==0, "test user can not see any logs")
|
self.assertTrue(self.project.query_user_logs(project_name, status_code=403, **USER_TEST)==0, "test user can not see any logs")
|
||||||
|
|
||||||
self.repo.delete_repository(project_name, repo_name_admin.split('/')[1], **USER_ADMIN)
|
self.repo.delete_repository(project_name, repo_name_admin.split('/')[1], **USER_ADMIN)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user