Skip to content

Commit

Permalink
feat: notification as permission subject
Browse files Browse the repository at this point in the history
  • Loading branch information
adityathebe authored and moshloop committed Dec 19, 2024
1 parent 2b83c80 commit defd0ea
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 38 deletions.
20 changes: 20 additions & 0 deletions context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,26 @@ func (k Context) WithUser(user *models.Person) Context {
return k.WithValue("user", user)
}

// Rbac subject
func (k Context) WithSubject(subject string) Context {
k.GetSpan().SetAttributes(attribute.String("rbac-subject", subject))
return k.WithValue("rbac-subject", subject)
}

func (k Context) Subject() string {
subject := k.Value("rbac-subject")
if subject != "" {
return subject.(string)
}

user := k.User()
if user != nil {
return user.ID.String()
}

return ""
}

func (k Context) WithoutName() Context {
k.Logger = logger.GetLogger()
return k
Expand Down
11 changes: 11 additions & 0 deletions models/notifications.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ const (
NotificationStatusSilenced = "silenced"
NotificationStatusRepeatInterval = "repeat-interval"

// an event was triggered and the notification is waiting for the playbook run to be triggered.
NotificationStatusPendingPlaybookRun = "pending_playbook_run"

// A playbook is currently in progress
NotificationStatusPendingPlaybookCompletion = "pending_playbook_completion"

// health related notifications of kubernetes config items get into this state
// to wait for the incremental scraper to re-evaluate the health.
NotificationStatusEvaluatingWaitFor = "evaluating-waitfor"
Expand Down Expand Up @@ -137,6 +143,11 @@ func (t *NotificationSendHistory) Sending() *NotificationSendHistory {
return t
}

func (t *NotificationSendHistory) PendingPlaybookRun() *NotificationSendHistory {
t.Status = NotificationStatusPendingPlaybookRun
return t.End()
}

func (t *NotificationSendHistory) Sent() *NotificationSendHistory {
t.Status = NotificationStatusSent
return t.End()
Expand Down
45 changes: 25 additions & 20 deletions models/permission.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,23 @@ import (
)

type Permission struct {
ID uuid.UUID `json:"id" gorm:"default:generate_ulid()"`
Action string `json:"action"`
ConnectionID *uuid.UUID `json:"connection_id,omitempty"`
CanaryID *uuid.UUID `json:"canary_id,omitempty"`
ComponentID *uuid.UUID `json:"component_id,omitempty"`
ConfigID *uuid.UUID `json:"config_id,omitempty"`
CreatedAt time.Time `json:"created_at"`
CreatedBy uuid.UUID `json:"created_by"`
Deny bool `json:"deny"`
Description string `json:"description"`
PersonID *uuid.UUID `json:"person_id,omitempty"`
PlaybookID *uuid.UUID `json:"playbook_id,omitempty"`
TeamID *uuid.UUID `json:"team_id,omitempty"`
Until *time.Time `json:"until"`
UpdatedAt *time.Time `json:"updated_at"`
UpdatedBy *uuid.UUID `json:"updated_by"`
ID uuid.UUID `json:"id" gorm:"default:generate_ulid()"`
Action string `json:"action"`
ConnectionID *uuid.UUID `json:"connection_id,omitempty"`
CanaryID *uuid.UUID `json:"canary_id,omitempty"`
ComponentID *uuid.UUID `json:"component_id,omitempty"`
ConfigID *uuid.UUID `json:"config_id,omitempty"`
CreatedAt time.Time `json:"created_at"`
CreatedBy uuid.UUID `json:"created_by"`
Deny bool `json:"deny"`
Description string `json:"description"`
PersonID *uuid.UUID `json:"person_id,omitempty"`
PlaybookID *uuid.UUID `json:"playbook_id,omitempty"`
TeamID *uuid.UUID `json:"team_id,omitempty"`
NotificationID *uuid.UUID `json:"notification_id,omitempty"`
Until *time.Time `json:"until"`
UpdatedAt *time.Time `json:"updated_at"`
UpdatedBy *uuid.UUID `json:"updated_by"`
}

func (t *Permission) Principal() string {
Expand All @@ -36,26 +37,30 @@ func (t *Permission) Principal() string {
return t.TeamID.String()
}

if t.NotificationID != nil {
return t.NotificationID.String()
}

return ""
}

func (t *Permission) Condition() string {
var rule []string

if t.ComponentID != nil {
rule = append(rule, fmt.Sprintf("r.obj.component != undefined && r.obj.component.id == %q", t.ComponentID.String()))
rule = append(rule, fmt.Sprintf("r.obj.component.id == %q", t.ComponentID.String()))
}

if t.ConfigID != nil {
rule = append(rule, fmt.Sprintf("r.obj.config != undefined && r.obj.config.id == %q", t.ConfigID.String()))
rule = append(rule, fmt.Sprintf("r.obj.config.id == %q", t.ConfigID.String()))
}

if t.CanaryID != nil {
rule = append(rule, fmt.Sprintf("r.obj.canary != undefined && r.obj.canary.id == %q", t.CanaryID.String()))
rule = append(rule, fmt.Sprintf("r.obj.canary.id == %q", t.CanaryID.String()))
}

if t.PlaybookID != nil {
rule = append(rule, fmt.Sprintf("r.obj.playbook != undefined && r.obj.playbook.id == %q", t.PlaybookID.String()))
rule = append(rule, fmt.Sprintf("r.obj.playbook.id == %q", t.PlaybookID.String()))
}

return strings.Join(rule, " && ")
Expand Down
4 changes: 2 additions & 2 deletions models/permission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ func TestPermission_Condition(t *testing.T) {
perm: Permission{
PlaybookID: lo.ToPtr(uuid.MustParse("33333333-3333-3333-3333-333333333333")),
},
expected: `r.obj.playbook != undefined && r.obj.playbook.id == "33333333-3333-3333-3333-333333333333"`,
expected: `r.obj.playbook.id == "33333333-3333-3333-3333-333333333333"`,
},
{
name: "Multiple fields II",
perm: Permission{
ConfigID: lo.ToPtr(uuid.MustParse("88888888-8888-8888-8888-888888888888")),
PlaybookID: lo.ToPtr(uuid.MustParse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")),
},
expected: `r.obj.config != undefined && r.obj.config.id == "88888888-8888-8888-8888-888888888888" && r.obj.playbook != undefined && r.obj.playbook.id == "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"`,
expected: `r.obj.config.id == "88888888-8888-8888-8888-888888888888" && r.obj.playbook.id == "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"`,
},
{
name: "No fields set",
Expand Down
32 changes: 26 additions & 6 deletions models/playbooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ type PlaybookRun struct {
Parameters types.JSONStringMap `json:"parameters,omitempty" gorm:"default:null"`
Request types.JSONMap `json:"request,omitempty" gorm:"default:null"`
AgentID *uuid.UUID `json:"agent_id,omitempty"`

NotificationSendID *uuid.UUID `json:"notification_send_id,omitempty"`
}

func (p PlaybookRun) TableName() string {
Expand Down Expand Up @@ -193,10 +195,28 @@ func (p PlaybookRun) End(db *gorm.DB) error {
status = PlaybookRunStatusFailed
}

return p.Update(db, map[string]any{
if err := p.Update(db, map[string]any{
"status": status,
"end_time": gorm.Expr("CLOCK_TIMESTAMP()"),
})
}); err != nil {
return err
}

if p.NotificationSendID != nil {
updates := map[string]any{}
if status == PlaybookRunStatusFailed {
updates["status"] = NotificationStatusError
updates["error"] = "playbook failed"
} else {
updates["status"] = NotificationStatusSent
}

if err := db.Model(&NotificationSendHistory{}).Where("id = ?", *p.NotificationSendID).Updates(updates).Error; err != nil {
return err
}
}

return nil
}

func (p PlaybookRun) Assign(db *gorm.DB, agent *Agent, action string) error {
Expand Down Expand Up @@ -348,30 +368,30 @@ func (run *PlaybookRun) GetRBACAttributes(db *gorm.DB) (map[string]any, error) {
if err := db.First(&playbook, run.PlaybookID).Error; err != nil {
return nil, err
}
output["playbook"] = playbook
output["playbook"] = playbook.AsMap()

if run.ComponentID != nil {
var component Component
if err := db.First(&component, run.ComponentID).Error; err != nil {
return nil, err
}
output["component"] = component
output["component"] = component.AsMap()
}

if run.CheckID != nil {
var check Check
if err := db.First(&check, run.CheckID).Error; err != nil {
return nil, err
}
output["check"] = check
output["check"] = check.AsMap()
}

if run.ConfigID != nil {
var config ConfigItem
if err := db.First(&config, run.ConfigID).Error; err != nil {
return nil, err
}
output["config"] = config
output["config"] = config.AsMap()
}

return output, nil
Expand Down
2 changes: 1 addition & 1 deletion postq/sync_consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (t *SyncEventConsumer) Handle(ctx context.Context) (int, error) {
}

event.Attempts++
// event.SetError(err.Error())
event.SetError(err.Error())
const query = `UPDATE event_queue SET error=$1, attempts=$2, last_attempt=NOW() WHERE id=$3`
if _, err := ctx.Pool().Exec(ctx, query, event.Error, event.Attempts, event.ID); err != nil {
ctx.Debugf("error saving event attempt updates to event_queue: %v\n", err)
Expand Down
12 changes: 11 additions & 1 deletion schema/permissions.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ table "permissions" {
type = uuid
}

column "notification_id" {
null = true
type = uuid
}

column "updated_by" {
null = true
type = uuid
Expand Down Expand Up @@ -130,7 +135,12 @@ table "permissions" {
on_update = NO_ACTION
on_delete = NO_ACTION
}

foreign_key "permissions_notification_fkey" {
columns = [column.notification_id]
ref_columns = [table.notifications.column.id]
on_update = NO_ACTION
on_delete = NO_ACTION
}
foreign_key "permissions_person_fkey" {
columns = [column.person_id]
ref_columns = [table.people.column.id]
Expand Down
27 changes: 19 additions & 8 deletions schema/playbooks.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ table "playbooks" {
default = sql("generate_ulid()")
}
column "namespace" {
null = false
type = text
null = false
type = text
default = "default"
}
column "name" {
null = false
type = text
}
column "title" {
null = false
type = text
null = false
type = text
default = ""
}
column "icon" {
Expand All @@ -40,8 +40,8 @@ table "playbooks" {
type = enum.source
}
column "category" {
null = false
type = text
null = false
type = text
default = ""
}
column "created_at" {
Expand Down Expand Up @@ -136,8 +136,8 @@ table "playbook_runs" {
type = uuid
}
column "spec" {
null = false
type = jsonb
null = false
type = jsonb
default = "{}" # temporary default value to make the migration possible. we can remove this later.
}
column "status" {
Expand Down Expand Up @@ -169,6 +169,11 @@ table "playbook_runs" {
null = true
type = uuid
}
column "notification_send_id" {
null = true
column = "the notification dispatch that triggered this run"
type = uuid
}
column "check_id" {
null = true
type = uuid
Expand Down Expand Up @@ -207,6 +212,12 @@ table "playbook_runs" {
on_update = NO_ACTION
on_delete = CASCADE
}
foreign_key "playbook_run_notification_send_id_fkey" {
columns = [column.notification_send_id]
ref_columns = [table.notification_send_history.column.id]
on_update = NO_ACTION
on_delete = NO_ACTION
}
foreign_key "playbook_run_created_by_fkey" {
columns = [column.created_by]
ref_columns = [table.people.column.id]
Expand Down

0 comments on commit defd0ea

Please sign in to comment.