Skip to content
Draft
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
57b8412
Add authorization handler for openfga
maxwellL21 Aug 20, 2025
d4ddc65
fix(authz): Remove authorization handler, implement interface
maxwellL21 Aug 25, 2025
94d1fe7
feat(authorization): add basic tests to authz methods
maxwellL21 Aug 26, 2025
fbad848
feat(openfga): implemented openfga store & model creation from model …
BlakePatterson Aug 27, 2025
618732c
feat(openfga): updated authz interface and implementation and wrote u…
BlakePatterson Aug 29, 2025
51a2a57
chore: merged changes from main and resolved conflicts
BlakePatterson Aug 29, 2025
b46d63a
feat(openfga): Add create user resource relationships, and testing
maxwellL21 Sep 9, 2025
32ad80c
fix(openfga): refactored variables, Added duplicate check for tuples
maxwellL21 Sep 11, 2025
d7679c7
feat(authz): various updates to authz setup and added handler context…
BlakePatterson Sep 19, 2025
a75884e
feat(authz): added doc on openfga implementation
BlakePatterson Sep 19, 2025
a9fb3e4
feat(openfga): merge changes from parent, Add basic update functionality
maxwellL21 Sep 23, 2025
146a509
feat(openfga): removed old placeholder and updated openfga env variables
BlakePatterson Sep 24, 2025
39dc896
chore: updating with changes from main
BlakePatterson Sep 25, 2025
34daeea
chore: added license header
BlakePatterson Sep 25, 2025
1a47c11
fix(openfga): merge additional changes from parent branch
maxwellL21 Sep 29, 2025
7eb6e91
fix(openfga): add basic delete and create tuple functionality
maxwellL21 Sep 29, 2025
d01576e
fix(openfga): app tests should pass when no authz is enabled
maxwellL21 Sep 29, 2025
a9e481f
feat(openfga): add delete functionality to openfga tuples
maxwellL21 Oct 6, 2025
3ffe7cc
fix(openfga): finish create function for openfga tuples
maxwellL21 Oct 8, 2025
4a44a65
Merge branch 'main' into origin/MaxLuong/issue-842/create_user_resour…
maxwellL21 Oct 14, 2025
c514bc1
feat(openfga): create update openfga tuple event handlers, update doc…
maxwellL21 Oct 15, 2025
0c1cbf3
feat(openfga): add DeleteUser handler, add test
maxwellL21 Oct 16, 2025
becb6ee
Merge branch 'main' into origin/MaxLuong/issue-842/create_user_resour…
maxwellL21 Oct 17, 2025
546289f
fix(openfga): fix failing user test
maxwellL21 Oct 17, 2025
c69d50b
chore(openfga): update go.sum
maxwellL21 Oct 17, 2025
aff82c3
fix(openfga): update failing tests
maxwellL21 Oct 17, 2025
00ea8b5
fix(openfga): fix component handler testing
maxwellL21 Oct 17, 2025
79dbf44
fix(openfga): update component handler test
maxwellL21 Oct 17, 2025
864a73e
fix: updated no authz check permission to return true
BlakePatterson Oct 24, 2025
27bf3c3
fix: updated auth config in app test suite and removed unnecessary lines
BlakePatterson Oct 24, 2025
2a1f273
fix: added openfga service and config to test suite gh action
BlakePatterson Oct 24, 2025
1f16483
fix: fixed openfga model file paths in app tests
BlakePatterson Oct 28, 2025
7a5dcaf
fix: removed unused default prio and reponame variables
BlakePatterson Oct 28, 2025
1bbc575
fix: fixed error with removed issue repo name and default prio fields…
BlakePatterson Nov 12, 2025
44fa1fa
fix: removed unfitting conditionals from authz unit tests
BlakePatterson Nov 12, 2025
50c9979
fix: updated link to auth model in docs
BlakePatterson Dec 9, 2025
a15738d
fix: added better error messages for auth event handlers
BlakePatterson Dec 12, 2025
8850709
fix: removed unnecessary loop
BlakePatterson Dec 12, 2025
edcc03f
fix: turn objectid and userid into function
maxwellL21 Jan 7, 2026
3aff582
fix: updated authz event handlers to source user id from proper context
BlakePatterson Jan 8, 2026
2b357d9
fix(openfga): Refactor inline declarations, loops, and string convers…
maxwellL21 Jan 14, 2026
e1c126a
fix: moved openfga test config setup to common function
BlakePatterson Jan 14, 2026
d6834ff
fix: added missing error handling to auth events
BlakePatterson Jan 14, 2026
2ab8b34
fix: added more precise checks for delete auth event tests
BlakePatterson Jan 15, 2026
aa57c9c
fix: added checks to prevent unnecessary updates in ci auth update event
BlakePatterson Jan 16, 2026
33304bb
fix: rewrote add multiple relations function in auth to perform bulk …
BlakePatterson Jan 21, 2026
247b118
fix: reforganized/formatted auth implementation and added comments
BlakePatterson Jan 21, 2026
ecf1d8b
fix(openfga): refactor for clarity, additional helper functions and e…
maxwellL21 Jan 22, 2026
825a483
fix(openfga): update helper functions
maxwellL21 Jan 22, 2026
ff4acda
fix: rewrote authz listRelations and updated/fixed various auth events
BlakePatterson Jan 26, 2026
93f6279
fix(openfga): improve logging clarity
maxwellL21 Jan 26, 2026
f68d5d8
fix(openfga): Added comments for clarity
maxwellL21 Jan 27, 2026
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ internal/mocks/*.go
internal/api/graphql/graph/model/models_gen.go
internal/api/graphql/graph/generated.go

.vscode/launch.json
.vscode/launch.json
19 changes: 19 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,25 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug handler_test.go",
"type": "go",
"request": "launch",
"mode": "test",
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/internal/app/component/component_handler_test.go",
"env": {},
"args": []
},
{
"name": "Debug All Tests (ginkgo -r)",
"type": "go",
"request": "launch",
"mode": "test",
"program": "${workspaceFolder}",
"args": ["-ginkgo.v", "-ginkgo.r"], // -ginkgo.r for recursive, -ginkgo.v for verbose
"env": {}
},
{
"name": "Launch K8sScanner",
"type": "go",
Expand Down
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"go.testEnvFile": "${workspaceFolder}/.testenv-devcontainer",
"ginkgotools.ginkgoPath": "go run github.com/onsi/ginkgo/v2/ginkgo"
"ginkgotools.ginkgoPath": "go run github.com/onsi/ginkgo/v2/ginkgo",
"go.delveConfig": {
"showGlobalVariables": true,
},
}
37 changes: 36 additions & 1 deletion docs/openfga_auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,50 @@ The interface consists of four main functions
- Checks if a given user has a given level of permission on a given resource (based on relation between user and resource)
- AddRelation(r RelationInput)
- Adds a specified relation between a given user and a given resource
- RemoveRelationBulk (r []RelationInput)
- Remove all relations that match any given RelationInput as filters
- RemoveRelation(r RelationInput)
- Removes a specified relation between a given user and a given resource (if such a relation exists)
- Removes a single relation between a given user and a given resource (if such a relation exists)
- ListRelations(filters []RelationInput)
- Returns a list of relations that match any given RelationInput as filters
- ListAccessibleResources(p PermissionInput)
- Returns a list of all objects of a specified type that a given user has a given relation with
- GetCurrentUser()
- Placeholder function to be implemented for future user context functionality

PermissionInput and RelationInput are structs defined in the interface that contain all the parameters for the above functions.

For more info on how OpenFGA handles users, objects, and relations: https://openfga.dev/docs/concepts

## Handlers

Using the event handling system, openfga tuples are modified based on the event handled. Based on the [Auth Model](https://github.com/cloudoperators/heureka/pull/829/files) defined, the OpenFGA tuples are implemented as follows here.

| Event | Event Handler | Tuples Implemented |
|-------------------------|-------------------------------|--------------------------------------------------------------------------------------------------|
| AddOwnerToService | OnAddOwnerToService | add service - user |
| RemoveOwnerFromService | OnRemoveOwnerFromService | remove service - user |
| CreateService | OnServiceCreateAuthz | add role - service |
| DeleteService | OnServiceDeleteAuthz | remove user - service<br>remove role - service<br>remove support_group - service<br>remove service - component_instance |
| UpdateComponentVersion | OnComponentVersionUpdateAuthz | update component_version - component |
| CreateComponentVersion | OnComponentVersionCreateAuthz | add role - component_version |
| DeleteComponentVersion | OnComponentVersionDeleteAuthz | remove user - component_version<br>remove component_instance - component_version<br>remove role - component_version<br>remove component - component_version |
| UpdateIssueMatch | OnIssueMatchUpdateAuthz | update issue_match - component_instance |
| CreateIssueMatch | OnIssueMatchCreateAuthz | add role - issue_match |
| DeleteIssueMatch | OnIssueMatchDeleteAuthz | delete user - issue_match<br>delete issue_match - component_instance<br>delete role - issue_match |
| UpdateComponentInstance | OnComponentInstanceUpdateAuthz | update component_instance - component_version_id<br>update component_instance - service |
| CreateComponentInstance | OnComponentInstanceCreateAuthz | add service - component_instance<br>add role - component_instance<br>add component_instance - component_version |
| DeleteComponentInstance | OnComponentInstanceDeleteAuthz | delete user - component_instance<br>delete service - component_instance<br>delete role - component_instance<br>delete component_instance - component_version<br>delete component_instance - issue_match |
| DeleteSupportGroup | OnSupportGroupDeleteAuthz | delete user - support_group<br>delete support_group - support_group<br>delete role - support_group<br>delete support_group - service |
| CreateSupportGroup | OnSupportGroupCreateAuthz | add role - support_group |
| AddServicetoSupportGroup| OnAddServiceToSupportGroup | add support_group - service |
| RemoveServiceFromSupportGroup | OnRemoveServiceFromSupportGroup | remove support_group - service |
| AddUsertoSupportGroup | OnAddUserToSupportGroup | add support_group - user |
| RemoveUserFromSupportGroup | OnRemoveUserFromSupportGroup | remove support_group - user |
| DeleteUser | OnUserDeleteAuthz | delete user - role<br>delete user - service<br>delete user - component_instance<br>delete user - support_group<br>delete user - issue_match<br>delete user - component_version<br>delete user - component |
| CreateComponent | OnComponentCreateAuthz | add role - component |
| DeleteComponent | OnComponentDeleteAuthz | delete user - component<br>delete component_version - component<br>delete role - component |

## Usage

The following four environment variables must be set to use OpenFGA
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/square/go-jose/v3 v3.0.0-20200630053402-0a67ce9b0693
github.com/stretchr/testify v1.11.1
github.com/valkey-io/valkey-go v1.0.67
github.com/valkey-io/valkey-go v1.0.66
github.com/vektah/gqlparser/v2 v2.5.30
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
golang.org/x/text v0.30.0
golang.org/x/time v0.14.0
Expand All @@ -43,7 +44,6 @@ require (
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
github.com/openfga/api/proto v0.0.0-20240905181937-3583905f61a6 // indirect
github.com/vektah/gqlparser/v2 v2.5.30 // indirect
go.uber.org/multierr v1.11.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,8 @@ github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
github.com/valkey-io/valkey-go v1.0.67 h1:QPaRcuBmazhyoWTxk7I2XcSALhoL7UhAReR5o/rh1Po=
github.com/valkey-io/valkey-go v1.0.67/go.mod h1:bHmwjIEOrGq/ubOJfh5uMRs7Xj6mV3mQ/ZXUbmqpjqY=
github.com/valkey-io/valkey-go v1.0.66 h1:DIEF1XpwbO78xK2sMTghYE3Bz6pePWJTNxKtgoAuA3A=
github.com/valkey-io/valkey-go v1.0.66/go.mod h1:bHmwjIEOrGq/ubOJfh5uMRs7Xj6mV3mQ/ZXUbmqpjqY=
github.com/vektah/gqlparser/v2 v2.5.30 h1:EqLwGAFLIzt1wpx1IPpY67DwUujF1OfzgEyDsLrN6kE=
github.com/vektah/gqlparser/v2 v2.5.30/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
Expand Down
2 changes: 1 addition & 1 deletion internal/app/activity/activity_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ var authz openfga.Authorization

var _ = BeforeSuite(func() {
db := mocks.NewMockDatabase(GinkgoT())
er = event.NewEventRegistry(db)
er = event.NewEventRegistry(db, authz)
})

func activityFilter() *entity.ActivityFilter {
Expand Down
67 changes: 67 additions & 0 deletions internal/app/component/component_handler_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@
package component

import (
"strconv"

"github.com/cloudoperators/heureka/internal/app/event"
"github.com/cloudoperators/heureka/internal/database"
"github.com/cloudoperators/heureka/internal/entity"
"github.com/cloudoperators/heureka/internal/openfga"
"github.com/sirupsen/logrus"
)

const (
Expand Down Expand Up @@ -69,3 +74,65 @@ type GetComponentIssueSeverityCountsEvent struct {
func (e *GetComponentIssueSeverityCountsEvent) Name() event.EventName {
return GetComponentIssueSeverityCountsEventName
}

// OnComponentCreateAuthz is a handler for the CreateComponentEvent
// It creates an OpenFGA relation tuple for the component and the current user
func OnComponentCreateAuthz(db database.Database, e event.Event, authz openfga.Authorization) {
defaultPrio := db.GetDefaultIssuePriority()
defaultRepoName := db.GetDefaultRepositoryName()

l := logrus.WithFields(logrus.Fields{
"event": "OnComponentCreateAuthz",
"payload": e,
"default_priority": defaultPrio,
"default_repo_name": defaultRepoName,
})

if createEvent, ok := e.(*CreateComponentEvent); ok {
componentId := strconv.FormatInt(createEvent.Component.Id, 10)
userId := authz.GetCurrentUser()

rlist := []openfga.RelationInput{
{
UserType: "role",
UserId: openfga.UserId(userId),
Relation: "role",
ObjectType: "component",
ObjectId: openfga.ObjectId(componentId),
},
}

for _, rel := range rlist {
authz.AddRelation(rel)
}
} else {
l.Error("Wrong event")
}
}

// OnComponentDeleteAuthz is a handler for the DeleteComponentEvent
func OnComponentDeleteAuthz(db database.Database, e event.Event, authz openfga.Authorization) {
defaultPrio := db.GetDefaultIssuePriority()
defaultRepoName := db.GetDefaultRepositoryName()
deleteInput := []openfga.RelationInput{}

l := logrus.WithFields(logrus.Fields{
"event": "OnComponentDeleteAuthz",
"payload": e,
"default_priority": defaultPrio,
"default_repo_name": defaultRepoName,
})

if deleteEvent, ok := e.(*DeleteComponentEvent); ok {
deleteElement := openfga.RelationInput{}
objectId := strconv.FormatInt(deleteEvent.ComponentID, 10)

deleteElement.ObjectType = "component"
deleteElement.ObjectId = openfga.ObjectId(objectId)
deleteInput = append(deleteInput, deleteElement)

authz.RemoveRelationBulk(deleteInput)
} else {
l.Error("Wrong event")
}
}
Loading
Loading