Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fa38d63
permissions audit
Shivam-nagar23 Jan 26, 2025
b1c45c6
adpater and repo changes
Shivam-nagar23 Jan 27, 2025
a33c0c2
save audit
Shivam-nagar23 Jan 27, 2025
312f31f
audits for user and role group
Shivam-nagar23 Jan 27, 2025
118ef58
permissions audit
Shivam-nagar23 Jan 27, 2025
0327308
permissions schema for
Shivam-nagar23 Jan 27, 2025
90ae112
generic operation audit repo adn service
Shivam-nagar23 Jan 27, 2025
d96fbf5
renaming
Shivam-nagar23 Jan 27, 2025
9b0ff7c
operations audit service
Shivam-nagar23 Jan 28, 2025
c3bfc9c
schema added
Shivam-nagar23 Jan 28, 2025
9d1a7af
opeartions audit
Shivam-nagar23 Jan 28, 2025
9f9a6e8
audit generic changes
Shivam-nagar23 Jan 28, 2025
6460318
audit struct change
Shivam-nagar23 Jan 28, 2025
91dd0e8
permissions chanages
Shivam-nagar23 Jan 28, 2025
68f6193
sql script
Shivam-nagar23 Jan 28, 2025
7ff7801
Merge branch 'develop' into feat-permissions-audit
Shivam-nagar23 Jan 28, 2025
ddf313d
sql script
Shivam-nagar23 Jan 28, 2025
37acacb
sql fetch
Shivam-nagar23 Jan 29, 2025
e7ca131
Merge branch 'develop' into feat-permissions-audit
Shivam-nagar23 Jan 30, 2025
7a7ad89
role grouplisting fix
Shivam-nagar23 Jan 30, 2025
0d30167
Merge branch 'fix-role-group-listing' into feat-permissions-audit
Shivam-nagar23 Jan 31, 2025
948de83
Merge branch 'develop' into feat-permissions-audit
Shivam-nagar23 Jan 31, 2025
b955478
role group validation
Shivam-nagar23 Jan 31, 2025
f25a0c0
removed opeartion audit pkg from oss
Shivam-nagar23 Jan 31, 2025
e717e72
column change
Shivam-nagar23 Jan 31, 2025
5de2040
Merge branch 'develop' into feat-permissions-audit
Shivam-nagar23 Feb 3, 2025
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
38 changes: 38 additions & 0 deletions api/bean/UserRequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package bean
import (
"encoding/json"
"github.com/devtron-labs/devtron/pkg/auth/user/bean"
"github.com/devtron-labs/devtron/pkg/sql"
"time"
)

Expand Down Expand Up @@ -168,3 +169,40 @@ type BulkDeleteRequest struct {
type UserRoleGroup struct {
RoleGroup *RoleGroup `json:"roleGroup"`
}

type GroupPermissionsAuditDto struct {
RoleGroupInfo *RoleGroup `json:"roleGroupInfo,omitempty"`
EntityAudit sql.AuditLog `json:"entityAudit,omitempty"`
}

func NewGroupPermissionsAuditDto() *GroupPermissionsAuditDto {
return &GroupPermissionsAuditDto{}
}

func (pa *GroupPermissionsAuditDto) WithRoleGroupInfo(roleGroupInfo *RoleGroup) *GroupPermissionsAuditDto {
pa.RoleGroupInfo = roleGroupInfo
return pa
}
func (pa *GroupPermissionsAuditDto) WithEntityAudit(entityAudit sql.AuditLog) *GroupPermissionsAuditDto {
pa.EntityAudit = entityAudit
return pa
}

type UserPermissionsAuditDto struct {
UserInfo *UserInfo `json:"userInfo,omitempty"`
EntityAudit sql.AuditLog `json:"entityAudit,omitempty"`
}

func NewUserPermissionsAuditDto() *UserPermissionsAuditDto {
return &UserPermissionsAuditDto{}
}

func (pa *UserPermissionsAuditDto) WithUserInfo(userInfo *UserInfo) *UserPermissionsAuditDto {
pa.UserInfo = userInfo
return pa
}

func (pa *UserPermissionsAuditDto) WithEntityAudit(entityAudit sql.AuditLog) *UserPermissionsAuditDto {
pa.EntityAudit = entityAudit
return pa
}
70 changes: 70 additions & 0 deletions pkg/operationAudit/OperationsAuditService.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package operationAudit

import (
"github.com/devtron-labs/devtron/api/bean"
adapter2 "github.com/devtron-labs/devtron/pkg/operationAudit/adapter"
bean2 "github.com/devtron-labs/devtron/pkg/operationAudit/bean"
"github.com/devtron-labs/devtron/pkg/operationAudit/repository"
"go.uber.org/zap"
)

type OperationAuditService interface {
SaveAuditForUser(entityId int32, operationType bean2.OperationType,
permissionsAuditDto *bean.UserPermissionsAuditDto, userIdForAuditLog int32) error
SaveAuditForRoleGroup(entityId int32, operationType bean2.OperationType,
permissionsAuditDto *bean.GroupPermissionsAuditDto, userIdForAuditLog int32) error
SaveAudit(entityId int32, entityType bean2.EntityType,
operationType bean2.OperationType, entityValueDto interface{}, userIdForAuditLog int32, schemaFor bean2.SchemaFor) error
}

type OperationAuditServiceImpl struct {
logger *zap.SugaredLogger
operationAuditRepository repository.OperationAuditRepository
}

func NewOperationAuditServiceImpl(logger *zap.SugaredLogger, operationAuditRepository repository.OperationAuditRepository) *OperationAuditServiceImpl {
return &OperationAuditServiceImpl{
logger: logger,
operationAuditRepository: operationAuditRepository,
}
}

func (impl *OperationAuditServiceImpl) SaveAudit(entityId int32, entityType bean2.EntityType,
operationType bean2.OperationType, entityValueDto interface{}, userIdForAuditLog int32, schemaFor bean2.SchemaFor) error {
model, err := adapter2.BuildOperationAuditModel(entityId, entityType, operationType, entityValueDto,
userIdForAuditLog, schemaFor)
if err != nil {
impl.logger.Errorw("error in BuildOperationAuditModel", "entityId", entityId, "operationType", operationType, "entityValueDto", entityValueDto, "err", err)
return err
}
err = impl.operationAuditRepository.SaveAudit(model)
if err != nil {
impl.logger.Errorw("error in saving audit", "entityId", entityId, "operationType", operationType, "entityValueDto", entityValueDto, "err", err)
return err
}
return nil
}

func (impl *OperationAuditServiceImpl) SaveAuditForUser(entityId int32, operationType bean2.OperationType,
permissionsAuditDto *bean.UserPermissionsAuditDto, userIdForAuditLog int32) error {
// by default v1 schema for now, when new schema are added this can be changed
schemaFor := bean2.UserSchema
err := impl.SaveAudit(entityId, bean2.UserEntity, operationType, permissionsAuditDto, userIdForAuditLog, schemaFor)
if err != nil {
impl.logger.Errorw("error in SaveAuditForUser", "err", err)
return err
}
return nil
}

func (impl *OperationAuditServiceImpl) SaveAuditForRoleGroup(entityId int32, operationType bean2.OperationType,
permissionsAuditDto *bean.GroupPermissionsAuditDto, userIdForAuditLog int32) error {
// by default v1 schema for now, when new schema are added this can be changed
schemaFor := bean2.RoleGroupSchema
err := impl.SaveAudit(entityId, bean2.RoleGroupEntity, operationType, permissionsAuditDto, userIdForAuditLog, schemaFor)
if err != nil {
impl.logger.Errorw("error in SaveAuditForRoleGroup", "err", err)
return err
}
return nil
}
28 changes: 28 additions & 0 deletions pkg/operationAudit/adapter/adapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package adapter

import (
"encoding/json"
"errors"
"fmt"
bean2 "github.com/devtron-labs/devtron/pkg/operationAudit/bean"
"github.com/devtron-labs/devtron/pkg/operationAudit/repository"
"github.com/devtron-labs/devtron/pkg/sql"
)

func BuildOperationAuditModel(entityId int32, entityType bean2.EntityType, operationType bean2.OperationType,
entityValueDto interface{}, userIdForAuditLog int32, schemaFor bean2.SchemaFor) (*repository.OperationAudit, error) {
entityValueJson, err := json.Marshal(entityValueDto)
if err != nil {
errToReturn := fmt.Sprintf("error in marshalling permissions audit dto :%s", err.Error())
return nil, errors.New(errToReturn)
}
return &repository.OperationAudit{
EntityId: entityId,
EntityType: entityType,
OperationType: operationType,
EntityValueJson: string(entityValueJson),
SchemaFor: schemaFor,
AuditLog: sql.NewDefaultAuditLog(userIdForAuditLog),
}, nil

}
30 changes: 30 additions & 0 deletions pkg/operationAudit/bean/bean.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package bean

import "github.com/devtron-labs/devtron/api/bean"

type OperationType string

const (
CreateOperationType OperationType = "CREATE"
UpdateOperationType OperationType = "UPDATE"
DeleteOperationType OperationType = "DELETE"
)

type EntityType string

const (
UserEntity EntityType = "user"
RoleGroupEntity EntityType = "role-group" // this is similar to permissions group
)

type SchemaFor string

const (
UserSchema SchemaFor = "user/v1"
RoleGroupSchema SchemaFor = "role-group/v1"
)

var MapOfAuditSchemaForVsSchema = map[SchemaFor]interface{}{
UserSchema: bean.UserPermissionsAuditDto{},
RoleGroupSchema: bean.GroupPermissionsAuditDto{},
}
59 changes: 59 additions & 0 deletions pkg/operationAudit/repository/OperationsAuditRepository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package repository

import (
"errors"
"github.com/devtron-labs/devtron/pkg/operationAudit/bean"
"github.com/devtron-labs/devtron/pkg/sql"
"github.com/go-pg/pg"
"go.uber.org/zap"
)

type OperationAuditRepository interface {
SaveAudit(audit *OperationAudit) error
GetAllAuditsForEntityAndEntityIds(entityType bean.EntityType, entityIds []int, operationType bean.OperationType, schemaFor bean.SchemaFor) ([]*OperationAudit, error)
}
type OperationAuditRepositoryImpl struct {
dbConnection *pg.DB
logger *zap.SugaredLogger
}

func NewOperationAuditRepositoryImpl(dbConnection *pg.DB,
logger *zap.SugaredLogger) *OperationAuditRepositoryImpl {
return &OperationAuditRepositoryImpl{
dbConnection: dbConnection,
logger: logger,
}
}

type OperationAudit struct {
TableName struct{} `sql:"operation_audit" pg:",discard_unknown_columns"`
Id int `sql:"id,pk"`
EntityId int32 `sql:"entity_id,notnull"` // User Id or Role Group Id or any entity id
EntityType bean.EntityType `sql:"entity_type,notnull"` // user or role-group or etc
OperationType bean.OperationType `sql:"operation_type,notnull"` // create,update,delete
EntityValueJson string `sql:"entity_value_json,notnull"` // create - permissions to be created with user, update - we will keep final updated permissions and delete will have operation as delete with existing permissions captured
SchemaFor bean.SchemaFor `sql:"schema_for,notnull"` // refer SchemaFor
sql.AuditLog
}

func (repo *OperationAuditRepositoryImpl) SaveAudit(audit *OperationAudit) error {
err := repo.dbConnection.Insert(audit)
if err != nil {
repo.logger.Errorw("error in saving audit", "audit", audit, "err", err)
}
return err
}

func (repo *OperationAuditRepositoryImpl) GetAllAuditsForEntityAndEntityIds(entityType bean.EntityType, entityIds []int, operationType bean.OperationType, schemaFor bean.SchemaFor) ([]*OperationAudit, error) {
if len(entityIds) == 0 {
return nil, errors.New("no entity ids found")
}
var audits []*OperationAudit
err := repo.dbConnection.Model(&audits).
Where("entity_type = ?", entityType).
Where("entity_id in (?)", pg.In(entityIds)).
Where("operation_type = ?", operationType).
Where("schema_for = ?", schemaFor).
Select()
return audits, err
}
14 changes: 14 additions & 0 deletions pkg/operationAudit/wire_operationAudit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package operationAudit

import (
"github.com/devtron-labs/devtron/pkg/operationAudit/repository"
"github.com/google/wire"
)

var AuditWireSet = wire.NewSet(
NewOperationAuditServiceImpl,
wire.Bind(new(OperationAuditService), new(*OperationAuditServiceImpl)),

repository.NewOperationAuditRepositoryImpl,
wire.Bind(new(repository.OperationAuditRepository), new(*repository.OperationAuditRepositoryImpl)),
)
5 changes: 5 additions & 0 deletions scripts/sql/31902800_operation_audit.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- Drop Table: operation_audit
DROP TABLE IF EXISTS "public"."operation_audit";

-- Drop Sequence: id_seq_operation_audit
DROP SEQUENCE IF EXISTS id_seq_operation_audit;
21 changes: 21 additions & 0 deletions scripts/sql/31902800_operation_audit.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
BEGIN;

-- Create Sequence for operation_audit
CREATE SEQUENCE IF NOT EXISTS id_seq_operation_audit;

-- Table Definition: operation_audit
CREATE TABLE IF NOT EXISTS "public"."operation_audit" (
"id" int NOT NULL DEFAULT nextval('id_seq_operation_audit'::regclass),
"entity_id" int NOT NULL,
"entity_type" VARCHAR(50) NOT NULL ,
"operation_type" VARCHAR(20) NOT NULL,
"entity_value_json" jsonb NOT NULL,
"schema_for" VARCHAR(20) NOT NULL,
"created_on" timestamptz NOT NULL,
"created_by" int4 NOT NULL,
"updated_on" timestamptz NOT NULL,
"updated_by" int4 NOT NULL,
PRIMARY KEY ("id")
);

COMMIT;
2 changes: 1 addition & 1 deletion wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.