Add basic audit logging setup

This commit is contained in:
Abin Simon
2022-04-08 17:42:26 +05:30
committed by Abin Simon
parent 36bfda653b
commit 729009d7fb
15 changed files with 759 additions and 114 deletions
+44
View File
@@ -128,6 +128,22 @@ func GetNameById(ctx context.Context, db bun.IDB, id uuid.UUID, entity interface
return entity, nil
}
func GetNamesByIds(ctx context.Context, db bun.IDB, id []uuid.UUID, entity interface{}) ([]string, error) {
names := []string{}
if len(id) == 0 {
return names, nil
}
err := db.NewSelect().Column("name").Model(entity).
Where("id = (?)", bun.In(id)).
Where("trash = ?", false).
Scan(ctx, &names)
if err != nil {
return nil, err
}
return names, nil
}
func Update(ctx context.Context, db bun.IDB, id uuid.UUID, entity interface{}) (interface{}, error) {
if _, err := db.NewUpdate().Model(entity).Where("id = ?", id).Exec(ctx); err != nil {
return nil, err
@@ -147,6 +163,7 @@ func Delete(ctx context.Context, db bun.IDB, id uuid.UUID, entity interface{}) e
Model(entity).
Column("trash").
Where("id = ?", id).
Where("trash = false").
Set("trash = ?", true).
Exec(ctx)
return err
@@ -157,11 +174,38 @@ func DeleteX(ctx context.Context, db bun.IDB, field string, value interface{}, e
Model(entity).
Column("trash").
Where("? = ?", bun.Ident(field), value).
Where("trash = false").
Set("trash = ?", true).
Exec(ctx)
return err
}
// DeleteR delete and returns the changed items
func DeleteR(ctx context.Context, db bun.IDB, id uuid.UUID, entity interface{}) error {
_, err := db.NewUpdate().
Model(entity).
Column("trash").
Where("id = ?", id).
Where("trash = false").
Set("trash = ?", true).
Returning("*").
Exec(ctx)
return err
}
// DeleteXR delete with selector and returns the changed items
func DeleteXR(ctx context.Context, db bun.IDB, field string, value interface{}, entity interface{}) error {
_, err := db.NewUpdate().
Model(entity).
Column("trash").
Where("? = ?", bun.Ident(field), value).
Where("trash = false").
Set("trash = ?", true).
Returning("*").
Exec(ctx)
return err
}
// HardDeleteAll deletes all records in a table (primarily for use in scripts)
func HardDeleteAll(ctx context.Context, db bun.IDB, entity interface{}) error {
_, err := db.NewDelete().
+14
View File
@@ -129,3 +129,17 @@ func ListFilteredUsers(ctx context.Context, db bun.IDB, users *[]models.KratosId
}
return users, nil
}
func GetUserNamesByIds(ctx context.Context, db bun.IDB, id []uuid.UUID, entity interface{}) ([]string, error) {
names := []string{}
if len(id) == 0 {
return names, nil
}
err := db.NewSelect().ColumnExpr("traits ->> 'email' as name").Model(entity).
Where("id = (?)", bun.In(id)).
Scan(ctx, &names)
if err != nil {
return nil, err
}
return names, nil
}
+27
View File
@@ -315,11 +315,20 @@ GET :host/auth/v3/users
Content-Type: application/yaml
X-Session-Token: :token
# Get all users with query options
GET :host/auth/v3/users?partner=:partner&organization=:org&q=user&name=john&order_by=email&project=ALL,:project
Content-Type: application/yaml
X-Session-Token: :token
# Get single user
GET :host/auth/v3/user/:user
Content-Type: application/yaml
X-Session-Token: :token
# Get currently logged in user info
GET :host/auth/v3/userinfo
Content-Type: application/yaml
X-Session-Token: :token
# Delete single user
DELETE :host/auth/v3/user/:user
@@ -510,6 +519,24 @@ metadata:
spec:
active: true
# Update organization
PUT :host/auth/v3/partner/:partner/organization/:org
Content-Type: application/yaml
X-Session-Token: :token
metadata:
partner: :partner
name: :org
description: "Very first organizataion"
spec:
active: true
settings:
idleLogoutMin: 30
lockout:
enabled: true
period_min: 10
attempts: 6
# List organizations
GET :host/auth/v3/partner/:partner/organizations
Content-Type: application/yaml
+54 -21
View File
@@ -10,7 +10,6 @@ import (
logv2 "github.com/RafayLabs/rcloud-base/pkg/log"
commonv3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
"github.com/Shopify/sarama"
"google.golang.org/grpc/metadata"
)
@@ -84,7 +83,6 @@ type Event struct {
}
type createEventOptions struct {
producer sarama.AsyncProducer
version EventVersion
origin EventOrigin
category EventCategory
@@ -98,13 +96,6 @@ type createEventOptions struct {
groups []string
}
// WithProducer sets producer for audit event
func WithProducer(producer sarama.AsyncProducer) CreateEventOption {
return func(opts *createEventOptions) {
opts.producer = producer
}
}
// WithVersion sets version for audit event
func WithVersion(version EventVersion) CreateEventOption {
return func(opts *createEventOptions) {
@@ -193,11 +184,6 @@ func CreateEvent(event *Event, opts ...CreateEventOption) error {
opt(&cOpts)
}
if cOpts.producer == nil {
_log.Infow("audit event producer is nil")
return fmt.Errorf("audit even producer is nil")
}
t := time.Now()
dateArray := strings.Fields(t.String())
timestamp := fmt.Sprintf("%d-%02d-%02dT%02d:%02d:%02d.%06d%s",
@@ -226,11 +212,7 @@ func CreateEvent(event *Event, opts ...CreateEventOption) error {
_log.Infow("unable to marshal audit event", "error", err)
return err
}
rawMessage := &sarama.ProducerMessage{
Topic: string(cOpts.topic),
Value: sarama.ByteEncoder(payload),
}
cOpts.producer.Input() <- rawMessage
fmt.Println("event:", string(payload)) // TODO: Switch to writing to audit file
return nil
}
@@ -288,7 +270,7 @@ func getActor(cOpts createEventOptions) *EventActor {
}
func GetActorFromSessionData(sd *commonv3.SessionData) *EventActor {
pid := sd.GetPartner()
pid := sd.GetPartner() // TODO: have this pulled from headers
oid := sd.GetOrganization()
accountID := sd.GetAccount()
username := sd.GetUsername()
@@ -296,12 +278,15 @@ func GetActorFromSessionData(sd *commonv3.SessionData) *EventActor {
ID: accountID,
Username: username,
}
groups := sd.Groups
groups := sd.Groups // TODO: get groups (in interceptor?)
// Set org id to string "null" for users with PARTNER_ADMIN role
if oid == "" {
oid = "null"
}
if pid == "" {
pid = "null"
}
return &EventActor{
Type: "USER",
@@ -321,6 +306,15 @@ func GetClientFromRequest(r *http.Request) *EventClient {
}
}
func GetClientFromSessionData(sd *commonv3.SessionData) *EventClient {
return &EventClient{
Type: "BROWSER",
IP: sd.GetClientIp(),
UserAgent: sd.GetClientUa(),
Host: sd.GetClientHost(),
}
}
func GetEvent(r *http.Request, sd *commonv3.SessionData, detail *EventDetail, eventType string, projectID string) *Event {
event := &Event{
Actor: GetActorFromSessionData(sd),
@@ -333,3 +327,42 @@ func GetEvent(r *http.Request, sd *commonv3.SessionData, detail *EventDetail, ev
return event
}
func CreateV1Event(sd *commonv3.SessionData, detail *EventDetail, eventType string, projectID string) error {
actor := GetActorFromSessionData(sd)
client := GetClientFromSessionData(sd)
if projectID == "" {
projectID = "null"
}
event := &Event{
Version: VersionV1,
Category: AuditCategory,
Origin: OriginCore,
Actor: actor,
Client: client,
Detail: detail,
Type: eventType,
Portal: "OPS", // TODO: What is the portal?
ProjectID: projectID,
}
event.PartnerID = actor.PartnerID
event.OrganizationID = actor.OrganizationID
t := time.Now()
dateArray := strings.Fields(t.String())
timestamp := fmt.Sprintf("%d-%02d-%02dT%02d:%02d:%02d.%06d%s",
t.Year(), t.Month(), t.Day(),
t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), dateArray[2])
event.Timestamp = timestamp
payload, err := json.Marshal(event)
if err != nil {
_log.Infow("unable to marshal audit event", "error", err)
return err
}
fmt.Println("event:", string(payload)) // TODO: Switch to writing to audit file
return nil
}
+11 -1
View File
@@ -49,10 +49,14 @@ func (s *apiKeyService) Create(ctx context.Context, req *rpcv3.ApiKeyRequest) (*
Secret: crypto.GenerateSha256Secret(),
}
_, err := dao.Create(ctx, s.db, apikey)
entity, err := dao.Create(ctx, s.db, apikey)
if err != nil {
return nil, err
}
if ak, ok := entity.(*models.Group); ok {
CreateApiKeyAuditEvent(ctx, AuditActionCreate, ak.ID.String())
}
return apikey, nil
}
@@ -61,6 +65,12 @@ func (s *apiKeyService) Delete(ctx context.Context, req *rpcv3.ApiKeyRequest) (*
Set("trash = ?", true).
Where("account_id = ?", req.Username).
Where("key = ?", req.Id).Exec(ctx)
if err != nil {
return &rpcv3.DeleteUserResponse{}, err
}
CreateApiKeyAuditEvent(ctx, AuditActionDelete, req.Id)
return nil, err
}
+369
View File
@@ -0,0 +1,369 @@
package service
import (
"context"
"fmt"
"strings"
"github.com/RafayLabs/rcloud-base/internal/dao"
"github.com/RafayLabs/rcloud-base/internal/models"
"github.com/RafayLabs/rcloud-base/pkg/audit"
systemv3 "github.com/RafayLabs/rcloud-base/proto/types/systempb/v3"
"github.com/google/uuid"
"github.com/uptrace/bun"
)
const (
AuditActionCreate = "create"
AuditActionDelete = "delete"
AuditActionUpdate = "update"
)
// TODO: add audit events for user-role or user-group mappings
func CreateUserAuditEvent(ctx context.Context, db bun.IDB, action string, name string, id uuid.UUID, rolesBefore, rolesAfter []uuid.UUID) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("User %s %sd", name, action),
Meta: map[string]string{
"account_id": id.String(),
"username": name,
},
}
if err := audit.CreateV1Event(sd, detail, fmt.Sprintf("user.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
cr, _, dr := diffu(rolesBefore, rolesAfter)
ncr, err := dao.GetNamesByIds(ctx, db, cr, &models.Role{})
if err != nil {
_log.Warn("unable to create audit event", err)
}
ndr, err := dao.GetNamesByIds(ctx, db, dr, &models.Role{})
if err != nil {
_log.Warn("unable to create audit event", err)
}
for _, r := range ncr {
detail := &audit.EventDetail{
Message: fmt.Sprintf("Role %s added to user %s", r, name),
Meta: map[string]string{
"username": name,
"roles_name": r, // TODO: add info like namespace and project
},
}
// user.role.created is user.project.created in rcloud
if err := audit.CreateV1Event(sd, detail, "user.role.created", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
for _, r := range ndr {
detail := &audit.EventDetail{
Message: fmt.Sprintf("Role %s deleted from user %s", r, name),
Meta: map[string]string{
"username": name,
"role_name": r,
},
}
if err := audit.CreateV1Event(sd, detail, "user.role.deleted", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
}
func CreateGroupAuditEvent(ctx context.Context, db bun.IDB, action string, name string, id uuid.UUID, usersBefore, usersAfter, rolesBefore, rolesAfter []uuid.UUID) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Group %s %sd", name, action),
Meta: map[string]string{
"group_id": id.String(),
"group_name": name,
},
}
if err := audit.CreateV1Event(sd, detail, fmt.Sprintf("group.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
cu, _, du := diffu(usersBefore, usersAfter)
cun, err := dao.GetUserNamesByIds(ctx, db, cu, &models.KratosIdentities{})
if err != nil {
_log.Warn("unable to create audit event", err)
}
dun, err := dao.GetUserNamesByIds(ctx, db, du, &models.KratosIdentities{})
if err != nil {
_log.Warn("unable to create audit event", err)
}
for _, u := range cun {
detail := &audit.EventDetail{
Message: fmt.Sprintf("User %s added to group %s", u, name),
Meta: map[string]string{
"group_id": id.String(),
"group_name": name,
"username": u,
},
}
if err := audit.CreateV1Event(sd, detail, "group.user.created", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
for _, u := range dun {
detail := &audit.EventDetail{
Message: fmt.Sprintf("User %s deleted from group %s", u, name),
Meta: map[string]string{
"group_id": id.String(),
"group_name": name,
"username": u,
},
}
if err := audit.CreateV1Event(sd, detail, "group.user.deleted", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
cr, _, dr := diffu(rolesBefore, rolesAfter)
ncr, err := dao.GetNamesByIds(ctx, db, cr, &models.Role{})
if err != nil {
_log.Warn("unable to create audit event", err)
}
ndr, err := dao.GetNamesByIds(ctx, db, dr, &models.Role{})
if err != nil {
_log.Warn("unable to create audit event", err)
}
for _, r := range ncr {
detail := &audit.EventDetail{
Message: fmt.Sprintf("Role %s added to group %s", r, name),
Meta: map[string]string{
"group_id": id.String(),
"group_name": name,
"roles_name": r, // TODO: add info like namespace and project
},
}
// group.role.created is group.project.created in rcloud
if err := audit.CreateV1Event(sd, detail, "group.role.created", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
for _, r := range ndr {
detail := &audit.EventDetail{
Message: fmt.Sprintf("Role %s deleted from group %s", r, name),
Meta: map[string]string{
"group_id": id.String(),
"group_name": name,
"role_name": r,
},
}
if err := audit.CreateV1Event(sd, detail, "group.role.deleted", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
}
func CreateRoleAuditEvent(ctx context.Context, action string, name string, id uuid.UUID, permissions []string) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Role %s %sd", name, action),
Meta: map[string]string{
"role_id": id.String(),
"role_name": name,
"permissions": strings.Join(permissions, ","), // TODO: Should we split it into individual ones?
},
}
if err := audit.CreateV1Event(sd, detail, fmt.Sprintf("role.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
func CreateProjectAuditEvent(ctx context.Context, action string, name string, id uuid.UUID) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Project %s %sd", name, action),
Meta: map[string]string{
"project_id": id.String(),
"project_name": name,
},
}
if err := audit.CreateV1Event(sd, detail, fmt.Sprintf("project.%s.success", action), id.String()); err != nil {
_log.Warn("unable to create audit event", err)
}
}
func CreateOrganizationAuditEvent(ctx context.Context, action string, name string, id uuid.UUID, settingsBefore, settingsAfter *systemv3.OrganizationSettings) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Organization %s %sd", name, action),
Meta: map[string]string{
"organization_id": id.String(),
"organization_name": name,
},
}
if err := audit.CreateV1Event(sd, detail, fmt.Sprintf("organization.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
if settingsBefore == nil && settingsAfter == nil {
return
}
bavail := settingsBefore != nil && settingsAfter != nil
if !bavail || settingsBefore.IdleLogoutMin != settingsAfter.IdleLogoutMin {
detail := &audit.EventDetail{
Message: fmt.Sprintf("Idel logout settings updated for organization %s", name),
Meta: map[string]string{
"organization_id": id.String(),
"organization_name": name,
},
}
if settingsAfter != nil {
detail.Meta = map[string]string{
"organization_id": id.String(),
"organization_name": name,
"idle_logout_min": string(settingsAfter.IdleLogoutMin),
}
}
if err := audit.CreateV1Event(sd, detail, "organization.idle.timeout.settings.updated", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
bavail = bavail && settingsBefore.Lockout != nil && settingsAfter.Lockout != nil
if !bavail ||
settingsBefore.Lockout.Enabled != settingsAfter.Lockout.Enabled ||
settingsBefore.Lockout.PeriodMin != settingsAfter.Lockout.PeriodMin ||
settingsBefore.Lockout.Attempts != settingsAfter.Lockout.Attempts {
enabled := "false"
detail := &audit.EventDetail{
Message: fmt.Sprintf("Lockout settings updated for organization %s", name),
Meta: map[string]string{
"organization_id": id.String(),
"organization_name": name,
},
}
if settingsAfter != nil && settingsAfter.Lockout != nil {
if settingsAfter.Lockout.Enabled {
enabled = "true"
}
detail.Meta = map[string]string{
"organization_id": id.String(),
"organization_name": name,
"lockout_enabled": enabled,
"lockout_period_min": string(settingsAfter.Lockout.PeriodMin),
"lockout_attempts": string(settingsAfter.Lockout.Attempts),
}
}
if err := audit.CreateV1Event(sd, detail, "organization.lockout.settings.updated", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
}
func CreateIdpAuditEvent(ctx context.Context, action string, name string, id uuid.UUID) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Idp %s %sd", name, action),
Meta: map[string]string{
"idp_id": id.String(),
"idp_name": name,
},
}
// TODO: it is idp.config.created in rcloud
if err := audit.CreateV1Event(sd, detail, fmt.Sprintf("idp.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
func CreateApiKeyAuditEvent(ctx context.Context, action string, id string) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("ApiKey %s %sd", id, action),
Meta: map[string]string{
"apikey": id,
},
}
if err := audit.CreateV1Event(sd, detail, fmt.Sprintf("apikey.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
func CreateClusterAuditEvent(ctx context.Context, action string, name string, id uuid.UUID) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Cluster %s %sd", name, action),
Meta: map[string]string{
"cluster_id": id.String(),
"cluster_name": name,
},
}
if err := audit.CreateV1Event(sd, detail, fmt.Sprintf("cluster.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
// TODO: figure out how this is to be added
func CreateLocationAuditEvent(ctx context.Context, action string, name string, id uuid.UUID) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Location %s %sd", name, action),
Meta: map[string]string{
"location_id": id.String(),
"location_name": name,
},
}
if err := audit.CreateV1Event(sd, detail, fmt.Sprintf("location.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
+7
View File
@@ -309,6 +309,7 @@ func (es *clusterService) Create(ctx context.Context, cluster *infrav3.Cluster)
h.OnChange(ev)
}
CreateClusterAuditEvent(ctx, AuditActionCreate, clusterResp.GetMetadata().GetName(), edb.ID)
return clusterResp, nil
}
@@ -590,6 +591,8 @@ func (cs *clusterService) Update(ctx context.Context, cluster *infrav3.Cluster)
h.OnChange(ev)
}*/
CreateClusterAuditEvent(ctx, AuditActionUpdate, cluster.GetMetadata().GetName(), cdb.ID)
return cluster, nil
}
@@ -634,6 +637,10 @@ func (cs *clusterService) Delete(ctx context.Context, cluster *infrav3.Cluster)
h.OnChange(ev)
}
id, err := uuid.Parse(clusterId)
if err == nil {
CreateClusterAuditEvent(ctx, AuditActionDelete, cluster.GetMetadata().GetName(), id)
}
return nil
}
+73 -42
View File
@@ -51,42 +51,59 @@ func NewGroupService(db *bun.DB, azc AuthzService) GroupService {
return &groupService{db: db, azc: azc}
}
func (s *groupService) deleteGroupRoleRelaitons(ctx context.Context, db bun.IDB, groupId uuid.UUID, group *userv3.Group) (*userv3.Group, error) {
// delete previous entries
// deleteGroupRoleRelaitons deletes existing group-role relations
func (s *groupService) deleteGroupRoleRelaitons(ctx context.Context, db bun.IDB, groupId uuid.UUID, group *userv3.Group) (*userv3.Group, []uuid.UUID, error) {
// TODO: single delete command
err := dao.DeleteX(ctx, db, "group_id", groupId, &models.GroupRole{})
ids := []uuid.UUID{}
gr := []models.GroupRole{}
err := dao.DeleteXR(ctx, db, "group_id", groupId, &gr)
if err != nil {
return &userv3.Group{}, err
return &userv3.Group{}, nil, err
}
err = dao.DeleteX(ctx, db, "group_id", groupId, &models.ProjectGroupRole{})
if err != nil {
return &userv3.Group{}, err
for _, r := range gr {
ids = append(ids, r.RoleId)
}
err = dao.DeleteX(ctx, db, "group_id", groupId, &models.ProjectGroupNamespaceRole{})
pgr := []models.ProjectGroupRole{}
err = dao.DeleteX(ctx, db, "group_id", groupId, &pgr)
if err != nil {
return &userv3.Group{}, err
return &userv3.Group{}, nil, err
}
for _, r := range pgr {
ids = append(ids, r.RoleId)
}
pgnr := []models.ProjectGroupNamespaceRole{}
err = dao.DeleteX(ctx, db, "group_id", groupId, &pgnr)
if err != nil {
return &userv3.Group{}, nil, err
}
for _, r := range pgnr {
ids = append(ids, r.RoleId)
}
_, err = s.azc.DeletePolicies(ctx, &authzv1.Policy{Sub: "g:" + group.GetMetadata().GetName()})
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to delete group-role relations from authz; %v", err)
return &userv3.Group{}, nil, fmt.Errorf("unable to delete group-role relations from authz; %v", err)
}
return group, nil
return group, ids, nil
}
// Map roles to groups
func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB, group *userv3.Group, ids parsedIds) (*userv3.Group, error) {
func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB, group *userv3.Group, ids parsedIds) (*userv3.Group, []uuid.UUID, error) {
// TODO: add transactions
projectNamespaceRoles := group.GetSpec().GetProjectNamespaceRoles()
var pgrs []models.ProjectGroupRole
var grs []models.GroupRole
var ps []*authzv1.Policy
var rids []uuid.UUID
for _, pnr := range projectNamespaceRoles {
role := pnr.GetRole()
entity, err := dao.GetByName(ctx, db, role, &models.Role{})
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to find role '%v'", role)
return &userv3.Group{}, nil, fmt.Errorf("unable to find role '%v'", role)
}
var roleId uuid.UUID
var roleName string
@@ -94,9 +111,10 @@ func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB,
if rle, ok := entity.(*models.Role); ok {
roleId = rle.ID
roleName = rle.Name
rids = append(rids, rle.ID)
scope = strings.ToLower(rle.Scope)
} else {
return &userv3.Group{}, fmt.Errorf("unable to find role '%v'", role)
return &userv3.Group{}, nil, fmt.Errorf("unable to find role '%v'", role)
}
project := pnr.GetProject()
@@ -122,7 +140,7 @@ func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB,
})
case "organization":
if org == "" {
return &userv3.Group{}, fmt.Errorf("no org name provided for role '%v'", roleName)
return &userv3.Group{}, nil, fmt.Errorf("no org name provided for role '%v'", roleName)
}
gr := models.GroupRole{
Trash: false,
@@ -142,14 +160,14 @@ func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB,
})
case "project":
if org == "" {
return &userv3.Group{}, fmt.Errorf("no org name provided for role '%v'", roleName)
return &userv3.Group{}, nil, fmt.Errorf("no org name provided for role '%v'", roleName)
}
if project == "" {
return &userv3.Group{}, fmt.Errorf("no project name provided for role '%v'", roleName)
return &userv3.Group{}, nil, fmt.Errorf("no project name provided for role '%v'", roleName)
}
projectId, err := dao.GetProjectId(ctx, s.db, project)
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to find project '%v'", project)
return &userv3.Group{}, nil, fmt.Errorf("unable to find project '%v'", project)
}
pgr := models.ProjectGroupRole{
Trash: false,
@@ -174,49 +192,56 @@ func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB,
if len(pgrs) > 0 {
_, err := dao.Create(ctx, db, &pgrs)
if err != nil {
return &userv3.Group{}, err
return &userv3.Group{}, nil, err
}
}
if len(grs) > 0 {
_, err := dao.Create(ctx, db, &grs)
if err != nil {
return &userv3.Group{}, err
return &userv3.Group{}, nil, err
}
}
if len(ps) > 0 {
success, err := s.azc.CreatePolicies(ctx, &authzv1.Policies{Policies: ps})
if err != nil || !success.Res {
return &userv3.Group{}, fmt.Errorf("unable to create mapping in authz; %v", err)
return &userv3.Group{}, nil, fmt.Errorf("unable to create mapping in authz; %v", err)
}
}
return group, nil
return group, rids, nil
}
func (s *groupService) deleteGroupAccountRelations(ctx context.Context, db bun.IDB, groupId uuid.UUID, group *userv3.Group) (*userv3.Group, error) {
err := dao.DeleteX(ctx, db, "group_id", groupId, &models.GroupAccount{})
func (s *groupService) deleteGroupAccountRelations(ctx context.Context, db bun.IDB, groupId uuid.UUID, group *userv3.Group) (*userv3.Group, []uuid.UUID, error) {
ga := []models.GroupAccount{}
err := dao.DeleteXR(ctx, db, "group_id", groupId, &ga)
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to delete user; %v", err)
return &userv3.Group{}, nil, fmt.Errorf("unable to remove user from group user; %v", err)
}
_, err = s.azc.DeleteUserGroups(ctx, &authzv1.UserGroup{Grp: "g:" + group.GetMetadata().GetName()})
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to delete group-user relations from authz; %v", err)
return &userv3.Group{}, nil, fmt.Errorf("unable to delete group-user relations from authz; %v", err)
}
return group, nil
ids := []uuid.UUID{}
for _, r := range ga {
ids = append(ids, r.AccountId)
}
return group, ids, nil
}
// Update the users(account) mapped to each group
func (s *groupService) createGroupAccountRelations(ctx context.Context, db bun.IDB, groupId uuid.UUID, group *userv3.Group) (*userv3.Group, error) {
func (s *groupService) createGroupAccountRelations(ctx context.Context, db bun.IDB, groupId uuid.UUID, group *userv3.Group) (*userv3.Group, []uuid.UUID, error) {
// TODO: add transactions
var grpaccs []models.GroupAccount
var ugs []*authzv1.UserGroup
var uids []uuid.UUID
for _, account := range unique(group.GetSpec().GetUsers()) {
// FIXME: do combined lookup
entity, err := dao.GetIdByTraits(ctx, db, account, &models.KratosIdentities{})
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to find user '%v'", account)
return &userv3.Group{}, nil, fmt.Errorf("unable to find user '%v'", account)
}
if acc, ok := entity.(*models.KratosIdentities); ok {
grp := models.GroupAccount{
@@ -227,6 +252,7 @@ func (s *groupService) createGroupAccountRelations(ctx context.Context, db bun.I
GroupId: groupId,
Active: true,
}
uids = append(uids, acc.ID)
grpaccs = append(grpaccs, grp)
ugs = append(ugs, &authzv1.UserGroup{
Grp: "g:" + group.GetMetadata().GetName(),
@@ -235,21 +261,21 @@ func (s *groupService) createGroupAccountRelations(ctx context.Context, db bun.I
}
}
if len(grpaccs) == 0 {
return group, nil
return group, nil, nil
}
_, err := dao.Create(ctx, db, &grpaccs)
if err != nil {
return &userv3.Group{}, err
return &userv3.Group{}, nil, err
}
// TODO: revert our db inserts if this fails
// Just FYI, the success can be false if we delete the db directly but casbin has it available internally
_, err = s.azc.CreateUserGroups(ctx, &authzv1.UserGroups{UserGroups: ugs})
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to create mapping in authz; %v", err)
return &userv3.Group{}, nil, fmt.Errorf("unable to create mapping in authz; %v", err)
}
return group, nil
return group, uids, nil
}
// TODO: move this to utils, make it accept two strings (names)
@@ -303,13 +329,13 @@ func (s *groupService) Create(ctx context.Context, group *userv3.Group) (*userv3
//update v3 spec
if grp, ok := entity.(*models.Group); ok {
// we can get previous group using the id, find users/roles from that and delete those
group, err = s.createGroupAccountRelations(ctx, tx, grp.ID, group)
group, usersAfter, err := s.createGroupAccountRelations(ctx, tx, grp.ID, group)
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
}
group, err = s.createGroupRoleRelations(ctx, tx, group, parsedIds{Id: grp.ID, Partner: partnerId, Organization: organizationId})
group, rolesAfter, err := s.createGroupRoleRelations(ctx, tx, group, parsedIds{Id: grp.ID, Partner: partnerId, Organization: organizationId})
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
@@ -320,9 +346,10 @@ func (s *groupService) Create(ctx context.Context, group *userv3.Group) (*userv3
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateGroupAuditEvent(ctx, s.db, AuditActionCreate, group.GetMetadata().GetName(), grp.ID, []uuid.UUID{}, usersAfter, []uuid.UUID{}, rolesAfter)
return group, nil
}
tx.Rollback()
return &userv3.Group{}, fmt.Errorf("unable to create group")
}
@@ -423,22 +450,22 @@ func (s *groupService) Update(ctx context.Context, group *userv3.Group) (*userv3
}
// update account/role links
group, err = s.deleteGroupAccountRelations(ctx, tx, grp.ID, group)
group, usersBefore, err := s.deleteGroupAccountRelations(ctx, tx, grp.ID, group)
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
}
group, err = s.createGroupAccountRelations(ctx, tx, grp.ID, group)
group, usersAfter, err := s.createGroupAccountRelations(ctx, tx, grp.ID, group)
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
}
group, err = s.deleteGroupRoleRelaitons(ctx, tx, grp.ID, group)
group, rolesBefore, err := s.deleteGroupRoleRelaitons(ctx, tx, grp.ID, group)
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
}
group, err = s.createGroupRoleRelations(ctx, tx, group, parsedIds{Id: grp.ID, Partner: partnerId, Organization: organizationId})
group, rolesAfter, err := s.createGroupRoleRelations(ctx, tx, group, parsedIds{Id: grp.ID, Partner: partnerId, Organization: organizationId})
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
@@ -462,6 +489,8 @@ func (s *groupService) Update(ctx context.Context, group *userv3.Group) (*userv3
Users: group.Spec.Users, // TODO: update from db resp or no update?
ProjectNamespaceRoles: group.Spec.ProjectNamespaceRoles,
}
CreateGroupAuditEvent(ctx, s.db, AuditActionUpdate, group.GetMetadata().GetName(), grp.ID, usersBefore, usersAfter, rolesBefore, rolesAfter)
}
return group, nil
@@ -484,12 +513,12 @@ func (s *groupService) Delete(ctx context.Context, group *userv3.Group) (*userv3
return &userv3.Group{}, err
}
group, err = s.deleteGroupRoleRelaitons(ctx, tx, grp.ID, group)
group, rolesBefore, err := s.deleteGroupRoleRelaitons(ctx, tx, grp.ID, group)
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
}
group, err = s.deleteGroupAccountRelations(ctx, tx, grp.ID, group)
group, usersBefore, err := s.deleteGroupAccountRelations(ctx, tx, grp.ID, group)
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
@@ -505,6 +534,8 @@ func (s *groupService) Delete(ctx context.Context, group *userv3.Group) (*userv3
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateGroupAuditEvent(ctx, s.db, AuditActionDelete, group.GetMetadata().GetName(), grp.ID, usersBefore, []uuid.UUID{}, rolesBefore, []uuid.UUID{})
return group, nil
}
+7
View File
@@ -205,6 +205,9 @@ func (s *idpService) Create(ctx context.Context, idp *systemv3.Idp) (*systemv3.I
SpEntityId: acsURL,
},
}
CreateIdpAuditEvent(ctx, AuditActionCreate, rv.GetMetadata().GetName(), entity.Id)
return rv, nil
}
@@ -381,6 +384,8 @@ func (s *idpService) Update(ctx context.Context, idp *systemv3.Idp) (*systemv3.I
SpEntityId: acsURL,
},
}
CreateIdpAuditEvent(ctx, AuditActionUpdate, rv.GetMetadata().GetName(), entity.Id)
return rv, nil
}
@@ -452,5 +457,7 @@ func (s *idpService) Delete(ctx context.Context, idp *systemv3.Idp) error {
if err != nil {
return err
}
CreateIdpAuditEvent(ctx, AuditActionDelete, name, entity.Id)
return nil
}
+13 -2
View File
@@ -99,6 +99,8 @@ func (s *organizationService) Create(ctx context.Context, org *systemv3.Organiza
if createdOrg, ok := entity.(*models.Organization); ok {
//update v3 spec
org.Metadata.Id = createdOrg.ID.String()
CreateOrganizationAuditEvent(ctx, AuditActionCreate, org.GetMetadata().GetName(), createdOrg.ID, nil, org.GetSpec().GetSettings())
}
return org, nil
@@ -195,8 +197,11 @@ func (s *organizationService) Update(ctx context.Context, organization *systemv3
}
if org, ok := entity.(*models.Organization); ok {
settingsAfter := organization.GetSpec().GetSettings()
settingsBefore := systemv3.OrganizationSettings{}
_ = json.Unmarshal(org.Settings, &settingsBefore) // ignore any unmarshelling issues
sb, err := json.MarshalIndent(organization.GetSpec().GetSettings(), "", "\t")
sb, err := json.MarshalIndent(settingsAfter, "", "\t")
if err != nil {
return &systemv3.Organization{}, err
}
@@ -226,6 +231,8 @@ func (s *organizationService) Update(ctx context.Context, organization *systemv3
if err != nil {
return &systemv3.Organization{}, err
}
CreateOrganizationAuditEvent(ctx, AuditActionUpdate, organization.GetMetadata().GetName(), org.ID, &settingsBefore, settingsAfter)
}
return organization, nil
@@ -239,7 +246,7 @@ func (s *organizationService) Delete(ctx context.Context, organization *systemv3
}
if org, ok := entity.(*models.Organization); ok {
err := dao.Delete(ctx, s.db, org.ID, org)
err := dao.DeleteR(ctx, s.db, org.ID, org)
if err != nil {
return &systemv3.Organization{}, err
}
@@ -247,6 +254,10 @@ func (s *organizationService) Delete(ctx context.Context, organization *systemv3
//update v3 status
organization.Metadata.Name = org.Name
organization.Metadata.ModifiedAt = timestamppb.New(org.ModifiedAt)
orgSettings := systemv3.OrganizationSettings{}
_ = json.Unmarshal(org.Settings, &orgSettings) // ignore any unmarshelling issues
CreateOrganizationAuditEvent(ctx, AuditActionDelete, organization.GetMetadata().GetName(), org.ID, &orgSettings, nil)
}
return organization, nil
+8 -3
View File
@@ -8,9 +8,7 @@ import (
"github.com/RafayLabs/rcloud-base/internal/dao"
"github.com/RafayLabs/rcloud-base/internal/models"
"github.com/RafayLabs/rcloud-base/pkg/common"
authzv1 "github.com/RafayLabs/rcloud-base/proto/types/authz"
commonv3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
v3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
systemv3 "github.com/RafayLabs/rcloud-base/proto/types/systempb/v3"
"github.com/google/uuid"
@@ -105,6 +103,8 @@ func (s *projectService) Create(ctx context.Context, project *systemv3.Project)
project.Spec = &systemv3.ProjectSpec{
Default: createdProject.Default,
}
CreateProjectAuditEvent(ctx, AuditActionCreate, project.GetMetadata().GetName(), createdProject.ID)
}
err = tx.Commit()
if err != nil {
@@ -282,6 +282,8 @@ func (s *projectService) Update(ctx context.Context, project *systemv3.Project)
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateProjectAuditEvent(ctx, AuditActionUpdate, project.GetMetadata().GetName(), proj.ID)
}
return project, nil
@@ -324,14 +326,17 @@ func (s *projectService) Delete(ctx context.Context, project *systemv3.Project)
if err != nil {
tx.Rollback()
_log.Warn("unable to commit changes", err)
return &systemv3.Project{}, err
}
CreateProjectAuditEvent(ctx, AuditActionDelete, project.GetMetadata().GetName(), proj.ID)
}
return project, nil
}
func (s *projectService) List(ctx context.Context, project *systemv3.Project) (*systemv3.ProjectList, error) {
sd, ok := ctx.Value(common.SessionDataKey).(*commonv3.SessionData)
sd, ok := GetSessionDataFromContext(ctx)
username := ""
if !ok {
return &systemv3.ProjectList{}, fmt.Errorf("cannot perform project listing without auth")
+16 -9
View File
@@ -166,17 +166,20 @@ func (s *roleService) Create(ctx context.Context, role *rolev3.Role) (*rolev3.Ro
tx.Rollback()
return &rolev3.Role{}, err
}
} else {
tx.Rollback()
return &rolev3.Role{}, fmt.Errorf("unable to create role '%v'", role.GetMetadata().GetName())
err = tx.Commit()
if err != nil {
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateRoleAuditEvent(ctx, AuditActionCreate, role.GetMetadata().GetName(), createdRole.ID, role.GetSpec().GetRolepermissions())
return role, nil
}
err = tx.Commit()
if err != nil {
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
return role, nil
tx.Rollback()
return &rolev3.Role{}, fmt.Errorf("unable to create role '%v'", role.GetMetadata().GetName())
}
@@ -279,6 +282,8 @@ func (s *roleService) Update(ctx context.Context, role *rolev3.Role) (*rolev3.Ro
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateRoleAuditEvent(ctx, AuditActionUpdate, role.GetMetadata().GetName(), rle.ID, role.GetSpec().GetRolepermissions())
return role, nil
}
return &rolev3.Role{}, fmt.Errorf("unable to update role '%v'", role.GetMetadata().GetName())
@@ -321,6 +326,8 @@ func (s *roleService) Delete(ctx context.Context, role *rolev3.Role) (*rolev3.Ro
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateRoleAuditEvent(ctx, AuditActionDelete, role.GetMetadata().GetName(), rle.ID, []string{})
return role, nil
}
+58 -34
View File
@@ -102,18 +102,19 @@ func getUserTraits(traits map[string]interface{}) userTraits {
}
// Map roles to accounts
func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, user *userv3.User, ids parsedIds) (*userv3.User, error) {
func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, user *userv3.User, ids parsedIds) (*userv3.User, []uuid.UUID, error) {
projectNamespaceRoles := user.GetSpec().GetProjectNamespaceRoles()
// TODO: add transactions
var pars []models.ProjectAccountResourcerole
var ars []models.AccountResourcerole
var ps []*authzv1.Policy
var rids []uuid.UUID
for _, pnr := range projectNamespaceRoles {
role := pnr.GetRole()
entity, err := dao.GetByName(ctx, db, role, &models.Role{})
if err != nil {
return &userv3.User{}, fmt.Errorf("unable to find role '%v'", role)
return &userv3.User{}, nil, fmt.Errorf("unable to find role '%v'", role)
}
var roleId uuid.UUID
var roleName string
@@ -121,9 +122,10 @@ func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, u
if rle, ok := entity.(*models.Role); ok {
roleId = rle.ID
roleName = rle.Name
rids = append(rids, rle.ID)
scope = strings.ToLower(rle.Scope)
} else {
return &userv3.User{}, fmt.Errorf("unable to find role '%v'", role)
return &userv3.User{}, nil, fmt.Errorf("unable to find role '%v'", role)
}
project := pnr.GetProject()
@@ -153,7 +155,7 @@ func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, u
})
case "organization":
if org == "" {
return &userv3.User{}, fmt.Errorf("no org name provided for role '%v'", roleName)
return &userv3.User{}, nil, fmt.Errorf("no org name provided for role '%v'", roleName)
}
ar := models.AccountResourcerole{
@@ -178,14 +180,14 @@ func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, u
})
case "project":
if org == "" {
return &userv3.User{}, fmt.Errorf("no org name provided for role '%v'", roleName)
return &userv3.User{}, nil, fmt.Errorf("no org name provided for role '%v'", roleName)
}
if project == "" {
return &userv3.User{}, fmt.Errorf("no project name provided for role '%v'", roleName)
return &userv3.User{}, nil, fmt.Errorf("no project name provided for role '%v'", roleName)
}
projectId, err := dao.GetProjectId(ctx, db, project)
if err != nil {
return user, fmt.Errorf("unable to find project '%v'", project)
return user, nil, fmt.Errorf("unable to find project '%v'", project)
}
par := models.ProjectAccountResourcerole{
CreatedAt: time.Now(),
@@ -210,31 +212,31 @@ func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, u
})
default:
if err != nil {
return user, fmt.Errorf("namespace specific roles are not handled")
return user, nil, fmt.Errorf("namespace specific roles are not handled")
}
}
}
if len(pars) > 0 {
_, err := dao.Create(ctx, db, &pars)
if err != nil {
return &userv3.User{}, err
return &userv3.User{}, nil, err
}
}
if len(ars) > 0 {
_, err := dao.Create(ctx, db, &ars)
if err != nil {
return &userv3.User{}, err
return &userv3.User{}, nil, err
}
}
if len(ps) > 0 {
success, err := s.azc.CreatePolicies(ctx, &authzv1.Policies{Policies: ps})
if err != nil || !success.Res {
return &userv3.User{}, fmt.Errorf("unable to create mapping in authz; %v", err)
return &userv3.User{}, nil, fmt.Errorf("unable to create mapping in authz; %v", err)
}
}
return user, nil
return user, rids, nil
}
// Update the groups mapped to each user(account)
@@ -336,7 +338,7 @@ func (s *userService) Create(ctx context.Context, user *userv3.User) (*userv3.Us
return &userv3.User{}, err
}
user, err = s.createUserRoleRelations(ctx, tx, user, parsedIds{Id: uid, Partner: partnerId, Organization: organizationId})
user, rolesAfter, err := s.createUserRoleRelations(ctx, tx, user, parsedIds{Id: uid, Partner: partnerId, Organization: organizationId})
if err != nil {
tx.Rollback()
return &userv3.User{}, err
@@ -361,6 +363,7 @@ func (s *userService) Create(ctx context.Context, user *userv3.User) (*userv3.Us
}
user.Spec.RecoveryUrl = &rl
CreateUserAuditEvent(ctx, s.db, AuditActionCreate, user.GetMetadata().GetName(), uid, []uuid.UUID{}, rolesAfter)
return user, nil
}
@@ -452,10 +455,10 @@ func (s *userService) GetByName(ctx context.Context, user *userv3.User) (*userv3
}
func (s *userService) GetUserInfo(ctx context.Context, user *userv3.User) (*userv3.UserInfo, error) {
sd, ok := ctx.Value(common.SessionDataKey).(*commonv3.SessionData)
sd, ok := GetSessionDataFromContext(ctx)
username := ""
if !ok {
return &userv3.UserInfo{}, fmt.Errorf("cannot perform project listing without auth")
return &userv3.UserInfo{}, fmt.Errorf("cannot get user info without auth")
}
username = sd.Username
@@ -517,26 +520,42 @@ func (s *userService) GetUserInfo(ctx context.Context, user *userv3.User) (*user
return &userv3.UserInfo{}, fmt.Errorf("unable to get user info")
}
func (s *userService) deleteUserRoleRelations(ctx context.Context, db bun.IDB, userId uuid.UUID, user *userv3.User) error {
err := dao.DeleteX(ctx, db, "account_id", userId, &models.AccountResourcerole{})
func (s *userService) deleteUserRoleRelations(ctx context.Context, db bun.IDB, userId uuid.UUID, user *userv3.User) ([]uuid.UUID, error) {
ids := []uuid.UUID{}
ar := []models.AccountResourcerole{}
err := dao.DeleteXR(ctx, db, "account_id", userId, &ar)
if err != nil {
return err
return nil, err
}
err = dao.DeleteX(ctx, db, "account_id", userId, &models.ProjectAccountResourcerole{})
if err != nil {
return err
for _, r := range ar {
ids = append(ids, r.RoleId)
}
err = dao.DeleteX(ctx, db, "account_id", userId, &models.ProjectAccountNamespaceRole{})
par := []models.ProjectAccountResourcerole{}
err = dao.DeleteX(ctx, db, "account_id", userId, &par)
if err != nil {
return err
return nil, err
}
for _, r := range par {
ids = append(ids, r.RoleId)
}
panr := []models.ProjectAccountNamespaceRole{}
err = dao.DeleteX(ctx, db, "account_id", userId, &panr)
if err != nil {
return nil, err
}
for _, r := range panr {
ids = append(ids, r.RoleId)
}
_, err = s.azc.DeletePolicies(ctx, &authzv1.Policy{Sub: "u:" + user.GetMetadata().GetName()})
if err != nil {
return fmt.Errorf("unable to delete user-role relations from authz; %v", err)
return nil, fmt.Errorf("unable to delete user-role relations from authz; %v", err)
}
return nil
return ids, nil
}
func (s *userService) Update(ctx context.Context, user *userv3.User) (*userv3.User, error) {
@@ -566,7 +585,7 @@ func (s *userService) Update(ctx context.Context, user *userv3.User) (*userv3.Us
return &userv3.User{}, err
}
err = s.deleteUserRoleRelations(ctx, tx, usr.ID, user)
rolesBefore, err := s.deleteUserRoleRelations(ctx, tx, usr.ID, user)
if err != nil {
tx.Rollback()
return &userv3.User{}, err
@@ -578,7 +597,8 @@ func (s *userService) Update(ctx context.Context, user *userv3.User) (*userv3.Us
return &userv3.User{}, err
}
user, err = s.createUserRoleRelations(ctx, tx, user, parsedIds{Id: usr.ID, Partner: partnerId, Organization: organizationId})
// TODO: add user-group relations to audit
user, rolesAfter, err := s.createUserRoleRelations(ctx, tx, user, parsedIds{Id: usr.ID, Partner: partnerId, Organization: organizationId})
if err != nil {
tx.Rollback()
return &userv3.User{}, err
@@ -595,6 +615,8 @@ func (s *userService) Update(ctx context.Context, user *userv3.User) (*userv3.Us
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateUserAuditEvent(ctx, s.db, AuditActionUpdate, user.GetMetadata().GetName(), usr.ID, rolesBefore, rolesAfter)
return user, nil
} else {
@@ -617,13 +639,7 @@ func (s *userService) Delete(ctx context.Context, user *userv3.User) (*userrpcv3
return &userrpcv3.DeleteUserResponse{}, err
}
err = s.deleteUserRoleRelations(ctx, tx, usr.ID, user)
if err != nil {
tx.Rollback()
return &userrpcv3.DeleteUserResponse{}, err
}
err = s.ap.Delete(ctx, usr.ID.String())
rolesBefore, err := s.deleteUserRoleRelations(ctx, tx, usr.ID, user)
if err != nil {
tx.Rollback()
return &userrpcv3.DeleteUserResponse{}, err
@@ -635,11 +651,19 @@ func (s *userService) Delete(ctx context.Context, user *userv3.User) (*userrpcv3
return &userrpcv3.DeleteUserResponse{}, fmt.Errorf("unable to delete user; %v", err)
}
err = s.ap.Delete(ctx, usr.ID.String())
if err != nil {
tx.Rollback()
return &userrpcv3.DeleteUserResponse{}, err
}
err = tx.Commit()
if err != nil {
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateUserAuditEvent(ctx, s.db, AuditActionDelete, user.GetMetadata().GetName(), usr.ID, rolesBefore, []uuid.UUID{})
return &userrpcv3.DeleteUserResponse{}, nil
}
return &userrpcv3.DeleteUserResponse{}, fmt.Errorf("unable to delete user '%v'", user.Metadata.Name)
+57
View File
@@ -4,6 +4,8 @@ import (
"context"
"github.com/RafayLabs/rcloud-base/internal/dao"
"github.com/RafayLabs/rcloud-base/pkg/common"
commonv3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
"github.com/google/uuid"
"github.com/uptrace/bun"
)
@@ -29,6 +31,15 @@ func contains(s []string, str string) bool {
return false
}
func containsu(s []uuid.UUID, id uuid.UUID) bool {
for _, v := range s {
if v == id {
return true
}
}
return false
}
func remove(l []string, item string) []string {
for i, other := range l {
if other == item {
@@ -38,6 +49,47 @@ func remove(l []string, item string) []string {
return l
}
func diff(before, after []string) ([]string, []string, []string) {
cu := []string{}
uu := []string{}
du := []string{}
for _, u := range after {
if contains(before, u) {
uu = append(uu, u)
} else {
cu = append(du, u)
}
}
for _, u := range before {
if !contains(uu, u) && !contains(du, u) {
du = append(cu, u)
}
}
return cu, uu, du
}
// Given two lists, return newly created, unchanged and deleted items
func diffu(before, after []uuid.UUID) ([]uuid.UUID, []uuid.UUID, []uuid.UUID) {
cu := []uuid.UUID{}
uu := []uuid.UUID{}
du := []uuid.UUID{}
for _, u := range after {
if containsu(before, u) {
uu = append(uu, u)
} else {
cu = append(du, u)
}
}
for _, u := range before {
if !containsu(uu, u) && !containsu(du, u) {
du = append(cu, u)
}
}
return cu, uu, du
}
func getPartnerOrganization(ctx context.Context, db bun.IDB, partner, org string) (uuid.UUID, uuid.UUID, error) {
partnerId, err := dao.GetPartnerId(ctx, db, partner)
if err != nil {
@@ -50,3 +102,8 @@ func getPartnerOrganization(ctx context.Context, db bun.IDB, partner, org string
return partnerId, organizationId, nil
}
func GetSessionDataFromContext(ctx context.Context) (*commonv3.SessionData, bool) {
s, ok := ctx.Value(common.SessionDataKey).(*commonv3.SessionData)
return s, ok
}
+1 -2
View File
@@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"github.com/RafayLabs/rcloud-base/pkg/common"
"github.com/RafayLabs/rcloud-base/pkg/query"
"github.com/RafayLabs/rcloud-base/pkg/service"
rpcv3 "github.com/RafayLabs/rcloud-base/proto/rpc/user"
@@ -75,7 +74,7 @@ func (s *userServer) UpdateUser(ctx context.Context, req *userpbv3.User) (*userp
}
func (s *userServer) DownloadCliConfig(ctx context.Context, req *rpcv3.CliConfigRequest) (*commonv3.HttpBody, error) {
sessData, ok := ctx.Value(common.SessionDataKey).(*commonv3.SessionData)
sessData, ok := service.GetSessionDataFromContext(ctx)
if !ok {
return nil, fmt.Errorf("unable to retrieve session data")
}