package rbac import ( "database/sql" "slices" adapter "github.com/Blank-Xu/sql-adapter" "github.com/casbin/casbin/v2" "github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/util" "tangled.org/core/api/tangled" ) const ( TnglSpindle = "tngl_spindle" Model = ` [request_definition] r = sub, dom, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub, r.dom) && r.obj == p.obj && r.act == p.act ` ) type Enforcer struct { e *casbin.SyncedEnforcer } func NewEnforcer(path string) (*Enforcer, error) { m, err := model.NewModelFromString(Model) if err != nil { return nil, err } db, err := sql.Open("sqlite3", path+"?_foreign_keys=1&_journal_mode=WAL&_busy_timeout=5000") if err != nil { return nil, err } a, err := adapter.NewAdapter(db, "sqlite3", "acl_2") if err != nil { return nil, err } e, err := casbin.NewSyncedEnforcer(m, a) if err != nil { return nil, err } if err := seedTangledPolicies(e); err != nil { return nil, err } return &Enforcer{e}, nil } func seedTangledPolicies(e *casbin.SyncedEnforcer) error { // policies _, err := e.AddPoliciesEx([][]string{ // sub | obj | act {"repo:collaborator", TnglSpindle, tangled.RepoAddSecretNSID}, {"repo:collaborator", TnglSpindle, tangled.RepoRemoveSecretNSID}, {"repo:collaborator", TnglSpindle, tangled.RepoListSecretsNSID}, {"repo:collaborator", TnglSpindle, tangled.CiCancelPipelineNSID}, {"repo:collaborator", TnglSpindle, tangled.CiTriggerPipelineNSID}, }) if err != nil { return err } // grouping policies e.AddNamedDomainMatchingFunc("g", "keyMatch", util.KeyMatch) _, err = e.AddGroupingPoliciesEx([][]string{ // sub | role | dom {"repo:owner", "repo:collaborator", "*"}, // repo owner is collaborator }) return err } func (e *Enforcer) EnableAutoSave(autoSave bool) { e.e.EnableAutoSave(autoSave) } func (e *Enforcer) hasImplicitRoleForUser(name string, role string, domain ...string) (bool, error) { roles, err := e.e.GetImplicitRolesForUser(name, domain...) if err != nil { return false, err } return slices.Contains(roles, role), nil } // setRoleForUser sets single user role for specified domain. // All existing users with that role will be removed. func (e *Enforcer) setRoleForUser(name string, role string, domain ...string) error { currentUsers, err := e.e.GetUsersForRole(role, domain...) if err != nil { return err } for _, oldUser := range currentUsers { _, err = e.e.DeleteRoleForUser(oldUser, role, domain...) if err != nil { return err } } _, err = e.e.AddRoleForUser(name, role, domain...) return err }