mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-23 00:57:44 +01:00
enable audit log for robot (#20843)
1, add creation audit log for robot account 2, add deletion audit log for robot account Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
parent
8107f47e12
commit
8ad8827e28
@ -45,7 +45,8 @@ func (h *Handler) Handle(ctx context.Context, value interface{}) error {
|
||||
switch v := value.(type) {
|
||||
case *event.PushArtifactEvent, *event.DeleteArtifactEvent,
|
||||
*event.DeleteRepositoryEvent, *event.CreateProjectEvent, *event.DeleteProjectEvent,
|
||||
*event.DeleteTagEvent, *event.CreateTagEvent:
|
||||
*event.DeleteTagEvent, *event.CreateTagEvent,
|
||||
*event.CreateRobotEvent, *event.DeleteRobotEvent:
|
||||
addAuditLog = true
|
||||
case *event.PullArtifactEvent:
|
||||
addAuditLog = !config.PullAuditLogDisable(ctx)
|
||||
|
@ -65,6 +65,8 @@ func init() {
|
||||
_ = notifier.Subscribe(event.TopicDeleteRepository, &auditlog.Handler{})
|
||||
_ = notifier.Subscribe(event.TopicCreateTag, &auditlog.Handler{})
|
||||
_ = notifier.Subscribe(event.TopicDeleteTag, &auditlog.Handler{})
|
||||
_ = notifier.Subscribe(event.TopicCreateRobot, &auditlog.Handler{})
|
||||
_ = notifier.Subscribe(event.TopicDeleteRobot, &auditlog.Handler{})
|
||||
|
||||
// internal
|
||||
_ = notifier.Subscribe(event.TopicPullArtifact, &internal.ArtifactEventHandler{})
|
||||
|
73
src/controller/event/metadata/robot.go
Normal file
73
src/controller/event/metadata/robot.go
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/security"
|
||||
event2 "github.com/goharbor/harbor/src/controller/event"
|
||||
"github.com/goharbor/harbor/src/lib/config"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/event"
|
||||
"github.com/goharbor/harbor/src/pkg/robot/model"
|
||||
)
|
||||
|
||||
// CreateRobotEventMetadata is the metadata from which the create robot event can be resolved
|
||||
type CreateRobotEventMetadata struct {
|
||||
Ctx context.Context
|
||||
Robot *model.Robot
|
||||
}
|
||||
|
||||
// Resolve to the event from the metadata
|
||||
func (c *CreateRobotEventMetadata) Resolve(event *event.Event) error {
|
||||
data := &event2.CreateRobotEvent{
|
||||
EventType: event2.TopicCreateRobot,
|
||||
Robot: c.Robot,
|
||||
OccurAt: time.Now(),
|
||||
}
|
||||
cx, exist := security.FromContext(c.Ctx)
|
||||
if exist {
|
||||
data.Operator = cx.GetUsername()
|
||||
}
|
||||
data.Robot.Name = fmt.Sprintf("%s%s", config.RobotPrefix(c.Ctx), data.Robot.Name)
|
||||
event.Topic = event2.TopicCreateRobot
|
||||
event.Data = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteRobotEventMetadata is the metadata from which the delete robot event can be resolved
|
||||
type DeleteRobotEventMetadata struct {
|
||||
Ctx context.Context
|
||||
Robot *model.Robot
|
||||
}
|
||||
|
||||
// Resolve to the event from the metadata
|
||||
func (d *DeleteRobotEventMetadata) Resolve(event *event.Event) error {
|
||||
data := &event2.DeleteRobotEvent{
|
||||
EventType: event2.TopicDeleteRobot,
|
||||
Robot: d.Robot,
|
||||
OccurAt: time.Now(),
|
||||
}
|
||||
cx, exist := security.FromContext(d.Ctx)
|
||||
if exist {
|
||||
data.Operator = cx.GetUsername()
|
||||
}
|
||||
data.Robot.Name = fmt.Sprintf("%s%s", config.RobotPrefix(d.Ctx), data.Robot.Name)
|
||||
event.Topic = event2.TopicDeleteRobot
|
||||
event.Data = data
|
||||
return nil
|
||||
}
|
83
src/controller/event/metadata/robot_test.go
Normal file
83
src/controller/event/metadata/robot_test.go
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
event2 "github.com/goharbor/harbor/src/controller/event"
|
||||
"github.com/goharbor/harbor/src/lib/config"
|
||||
_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/event"
|
||||
"github.com/goharbor/harbor/src/pkg/robot/model"
|
||||
)
|
||||
|
||||
type robotEventTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func (t *tagEventTestSuite) TestResolveOfCreateRobotEventMetadata() {
|
||||
cfg := map[string]interface{}{
|
||||
common.RobotPrefix: "robot$",
|
||||
}
|
||||
config.InitWithSettings(cfg)
|
||||
|
||||
e := &event.Event{}
|
||||
metadata := &CreateRobotEventMetadata{
|
||||
Ctx: context.Background(),
|
||||
Robot: &model.Robot{
|
||||
ID: 1,
|
||||
Name: "test",
|
||||
},
|
||||
}
|
||||
err := metadata.Resolve(e)
|
||||
t.Require().Nil(err)
|
||||
t.Equal(event2.TopicCreateRobot, e.Topic)
|
||||
t.Require().NotNil(e.Data)
|
||||
data, ok := e.Data.(*event2.CreateRobotEvent)
|
||||
t.Require().True(ok)
|
||||
t.Equal(int64(1), data.Robot.ID)
|
||||
t.Equal("robot$test", data.Robot.Name)
|
||||
}
|
||||
|
||||
func (t *tagEventTestSuite) TestResolveOfDeleteRobotEventMetadata() {
|
||||
cfg := map[string]interface{}{
|
||||
common.RobotPrefix: "robot$",
|
||||
}
|
||||
config.InitWithSettings(cfg)
|
||||
|
||||
e := &event.Event{}
|
||||
metadata := &DeleteRobotEventMetadata{
|
||||
Ctx: context.Background(),
|
||||
Robot: &model.Robot{
|
||||
ID: 1,
|
||||
},
|
||||
}
|
||||
err := metadata.Resolve(e)
|
||||
t.Require().Nil(err)
|
||||
t.Equal(event2.TopicDeleteRobot, e.Topic)
|
||||
t.Require().NotNil(e.Data)
|
||||
data, ok := e.Data.(*event2.DeleteRobotEvent)
|
||||
t.Require().True(ok)
|
||||
t.Equal(int64(1), data.Robot.ID)
|
||||
}
|
||||
|
||||
func TestRobotEventTestSuite(t *testing.T) {
|
||||
suite.Run(t, &robotEventTestSuite{})
|
||||
}
|
@ -23,6 +23,7 @@ import (
|
||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||
"github.com/goharbor/harbor/src/pkg/audit/model"
|
||||
proModels "github.com/goharbor/harbor/src/pkg/project/models"
|
||||
robotModel "github.com/goharbor/harbor/src/pkg/robot/model"
|
||||
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
||||
)
|
||||
|
||||
@ -47,6 +48,8 @@ const (
|
||||
TopicReplication = "REPLICATION"
|
||||
TopicArtifactLabeled = "ARTIFACT_LABELED"
|
||||
TopicTagRetention = "TAG_RETENTION"
|
||||
TopicCreateRobot = "CREATE_ROBOT"
|
||||
TopicDeleteRobot = "DELETE_ROBOT"
|
||||
)
|
||||
|
||||
// CreateProjectEvent is the creating project event
|
||||
@ -369,3 +372,53 @@ func (r *RetentionEvent) String() string {
|
||||
return fmt.Sprintf("TaskID-%d Status-%s Deleted-%s OccurAt-%s",
|
||||
r.TaskID, r.Status, candidates, r.OccurAt.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
// CreateRobotEvent is the creating robot event
|
||||
type CreateRobotEvent struct {
|
||||
EventType string
|
||||
Robot *robotModel.Robot
|
||||
Operator string
|
||||
OccurAt time.Time
|
||||
}
|
||||
|
||||
// ResolveToAuditLog ...
|
||||
func (c *CreateRobotEvent) ResolveToAuditLog() (*model.AuditLog, error) {
|
||||
auditLog := &model.AuditLog{
|
||||
ProjectID: c.Robot.ProjectID,
|
||||
OpTime: c.OccurAt,
|
||||
Operation: rbac.ActionCreate.String(),
|
||||
Username: c.Operator,
|
||||
ResourceType: "robot",
|
||||
Resource: c.Robot.Name}
|
||||
return auditLog, nil
|
||||
}
|
||||
|
||||
func (c *CreateRobotEvent) String() string {
|
||||
return fmt.Sprintf("Name-%s Operator-%s OccurAt-%s",
|
||||
c.Robot.Name, c.Operator, c.OccurAt.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
// DeleteRobotEvent is the deleting robot event
|
||||
type DeleteRobotEvent struct {
|
||||
EventType string
|
||||
Robot *robotModel.Robot
|
||||
Operator string
|
||||
OccurAt time.Time
|
||||
}
|
||||
|
||||
// ResolveToAuditLog ...
|
||||
func (c *DeleteRobotEvent) ResolveToAuditLog() (*model.AuditLog, error) {
|
||||
auditLog := &model.AuditLog{
|
||||
ProjectID: c.Robot.ProjectID,
|
||||
OpTime: c.OccurAt,
|
||||
Operation: rbac.ActionDelete.String(),
|
||||
Username: c.Operator,
|
||||
ResourceType: "robot",
|
||||
Resource: c.Robot.Name}
|
||||
return auditLog, nil
|
||||
}
|
||||
|
||||
func (c *DeleteRobotEvent) String() string {
|
||||
return fmt.Sprintf("Name-%s Operator-%s OccurAt-%s",
|
||||
c.Robot.Name, c.Operator, c.OccurAt.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
@ -23,12 +23,14 @@ import (
|
||||
|
||||
rbac_project "github.com/goharbor/harbor/src/common/rbac/project"
|
||||
"github.com/goharbor/harbor/src/common/utils"
|
||||
"github.com/goharbor/harbor/src/controller/event/metadata"
|
||||
"github.com/goharbor/harbor/src/lib/config"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/lib/log"
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
"github.com/goharbor/harbor/src/lib/retry"
|
||||
"github.com/goharbor/harbor/src/pkg"
|
||||
"github.com/goharbor/harbor/src/pkg/notification"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
"github.com/goharbor/harbor/src/pkg/project"
|
||||
"github.com/goharbor/harbor/src/pkg/rbac"
|
||||
@ -121,7 +123,7 @@ func (d *controller) Create(ctx context.Context, r *Robot) (int64, string, error
|
||||
if r.Level == LEVELPROJECT {
|
||||
name = fmt.Sprintf("%s+%s", r.ProjectName, r.Name)
|
||||
}
|
||||
robotID, err := d.robotMgr.Create(ctx, &model.Robot{
|
||||
rCreate := &model.Robot{
|
||||
Name: name,
|
||||
Description: r.Description,
|
||||
ProjectID: r.ProjectID,
|
||||
@ -130,7 +132,8 @@ func (d *controller) Create(ctx context.Context, r *Robot) (int64, string, error
|
||||
Duration: r.Duration,
|
||||
Salt: salt,
|
||||
Visible: r.Visible,
|
||||
})
|
||||
}
|
||||
robotID, err := d.robotMgr.Create(ctx, rCreate)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
@ -138,17 +141,31 @@ func (d *controller) Create(ctx context.Context, r *Robot) (int64, string, error
|
||||
if err := d.createPermission(ctx, r); err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
// fire event
|
||||
notification.AddEvent(ctx, &metadata.CreateRobotEventMetadata{
|
||||
Ctx: ctx,
|
||||
Robot: rCreate,
|
||||
})
|
||||
return robotID, pwd, nil
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
func (d *controller) Delete(ctx context.Context, id int64) error {
|
||||
rDelete, err := d.robotMgr.Get(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.robotMgr.Delete(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.rbacMgr.DeletePermissionsByRole(ctx, ROBOTTYPE, id); err != nil {
|
||||
return err
|
||||
}
|
||||
// fire event
|
||||
notification.AddEvent(ctx, &metadata.DeleteRobotEventMetadata{
|
||||
Ctx: ctx,
|
||||
Robot: rDelete,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -145,6 +145,12 @@ func (suite *ControllerTestSuite) TestDelete() {
|
||||
c := controller{robotMgr: robotMgr, rbacMgr: rbacMgr, proMgr: projectMgr}
|
||||
ctx := context.TODO()
|
||||
|
||||
robotMgr.On("Get", mock.Anything, mock.Anything).Return(&model.Robot{
|
||||
Name: "library+test",
|
||||
Description: "test get method",
|
||||
ProjectID: 1,
|
||||
Secret: utils.RandStringBytes(10),
|
||||
}, nil)
|
||||
robotMgr.On("Delete", mock.Anything, mock.Anything).Return(nil)
|
||||
rbacMgr.On("DeletePermissionsByRole", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user