diff --git a/pkg/services/accesscontrol/dualwrite/collectors.go b/pkg/services/accesscontrol/dualwrite/collectors.go index 0be8336069d..3d6b9b3e974 100644 --- a/pkg/services/accesscontrol/dualwrite/collectors.go +++ b/pkg/services/accesscontrol/dualwrite/collectors.go @@ -8,6 +8,7 @@ import ( "github.com/grafana/grafana/pkg/infra/db" authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1" "github.com/grafana/grafana/pkg/services/authz/zanzana" + "github.com/grafana/grafana/pkg/setting" ) func teamMembershipCollector(store db.DB) legacyTupleCollector { @@ -394,6 +395,35 @@ func rolePermissionsCollector(store db.DB) legacyTupleCollector { } } +// basicRoleBindingsCollector collects role bindings for basic roles +func anonymousRoleBindingsCollector(cfg *setting.Cfg, store db.DB) legacyTupleCollector { + return func(ctx context.Context, orgID int64) (map[string]map[string]*openfgav1.TupleKey, error) { + tuples := make(map[string]map[string]*openfgav1.TupleKey) + object := zanzana.NewTupleEntry(zanzana.TypeRole, zanzana.TranslateBasicRole(cfg.AnonymousOrgRole), "") + // Object should be set to delete obsolete permissions + tuples[object] = make(map[string]*openfgav1.TupleKey) + + o, err := getOrgByName(ctx, store, cfg.AnonymousOrgName) + if err != nil { + return tuples, nil + } + + if o.ID != orgID { + return tuples, nil + } + + tuple := &openfgav1.TupleKey{ + User: zanzana.NewTupleEntry(zanzana.TypeAnonymous, "0", ""), + Relation: zanzana.RelationAssignee, + Object: object, + } + + tuples[tuple.Object][tuple.String()] = tuple + + return tuples, nil + } +} + func zanzanaCollector(relations []string) zanzanaTupleCollector { return func(ctx context.Context, client zanzana.Client, object string, namespace string) (map[string]*openfgav1.TupleKey, error) { // list will use continuation token to collect all tuples for object and relation diff --git a/pkg/services/accesscontrol/dualwrite/reconciler.go b/pkg/services/accesscontrol/dualwrite/reconciler.go index 930327340fb..9a72f848cba 100644 --- a/pkg/services/accesscontrol/dualwrite/reconciler.go +++ b/pkg/services/accesscontrol/dualwrite/reconciler.go @@ -2,6 +2,7 @@ package dualwrite import ( "context" + "fmt" "strconv" "time" @@ -12,6 +13,7 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/services/authz/zanzana" + "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/setting" ) @@ -33,7 +35,7 @@ type ZanzanaReconciler struct { } func NewZanzanaReconciler(cfg *setting.Cfg, client zanzana.Client, store db.DB, lock *serverlock.ServerLockService) *ZanzanaReconciler { - return &ZanzanaReconciler{ + zanzanaReconciler := &ZanzanaReconciler{ cfg: cfg, log: log.New("zanzana.reconciler"), client: client, @@ -90,6 +92,19 @@ func NewZanzanaReconciler(cfg *setting.Cfg, client zanzana.Client, store db.DB, ), }, } + + if cfg.AnonymousEnabled { + zanzanaReconciler.reconcilers = append(zanzanaReconciler.reconcilers, + newResourceReconciler( + "anonymous role binding", + anonymousRoleBindingsCollector(cfg, store), + zanzanaCollector([]string{zanzana.RelationAssignee}), + client, + ), + ) + } + + return zanzanaReconciler } // Reconcile schedules as job that will run and reconcile resources between @@ -182,3 +197,22 @@ func (r *ZanzanaReconciler) getOrgs(ctx context.Context) ([]int64, error) { } return orgs, nil } + +func getOrgByName(ctx context.Context, store db.DB, name string) (*org.Org, error) { + var orga org.Org + err := store.WithDbSession(ctx, func(dbSession *db.Session) error { + exists, err := dbSession.Where("name=?", name).Get(&orga) + if err != nil { + return err + } + if !exists { + return fmt.Errorf("org does not exist: %s", name) + } + return nil + }) + + if err != nil { + return nil, err + } + return &orga, nil +} diff --git a/pkg/services/authz/zanzana/common/tuple.go b/pkg/services/authz/zanzana/common/tuple.go index cc94052438c..bc4839c0128 100644 --- a/pkg/services/authz/zanzana/common/tuple.go +++ b/pkg/services/authz/zanzana/common/tuple.go @@ -14,6 +14,7 @@ const ( TypeUser string = "user" TypeServiceAccount string = "service-account" TypeRenderService string = "render" + TypeAnonymous string = "anonymous" TypeTeam string = "team" TypeRole string = "role" ) diff --git a/pkg/services/authz/zanzana/schema/schema_core.fga b/pkg/services/authz/zanzana/schema/schema_core.fga index 8dd5f382279..80375ad2d4a 100644 --- a/pkg/services/authz/zanzana/schema/schema_core.fga +++ b/pkg/services/authz/zanzana/schema/schema_core.fga @@ -6,9 +6,11 @@ type service-account type render +type anonymous + type role relations - define assignee: [user, service-account, team#member, role#assignee] + define assignee: [user, service-account, anonymous, team#member, role#assignee] type team relations diff --git a/pkg/services/authz/zanzana/zanzana.go b/pkg/services/authz/zanzana/zanzana.go index 5160a3f83d9..cf7d74ddaf3 100644 --- a/pkg/services/authz/zanzana/zanzana.go +++ b/pkg/services/authz/zanzana/zanzana.go @@ -14,6 +14,7 @@ const ( TypeUser = common.TypeUser TypeServiceAccount = common.TypeServiceAccount TypeRenderService = common.TypeRenderService + TypeAnonymous = common.TypeAnonymous TypeTeam = common.TypeTeam TypeRole = common.TypeRole TypeFolder = common.TypeFolder