mirror of
https://github.com/SigNoz/signoz.git
synced 2026-02-03 08:33:26 +00:00
feat(authz): openfga sql migration (#9580)
* feat(authz): openfga sql migration * feat(authz): formatting and naming * feat(authz): formatting and naming * feat(authz): extract function for store and model id * feat(authz): reorder the provider
This commit is contained in:
@@ -21,12 +21,12 @@ type role
|
||||
define update: [user, role#assignee]
|
||||
define delete: [user, role#assignee]
|
||||
|
||||
type resources
|
||||
type metaresources
|
||||
relations
|
||||
define create: [user, role#assignee]
|
||||
define list: [user, role#assignee]
|
||||
|
||||
type resource
|
||||
type metaresource
|
||||
relations
|
||||
define read: [user, anonymous, role#assignee]
|
||||
define update: [user, role#assignee]
|
||||
@@ -35,6 +35,6 @@ type resource
|
||||
define block: [user, role#assignee]
|
||||
|
||||
|
||||
type telemetry
|
||||
type telemetryresource
|
||||
relations
|
||||
define read: [user, anonymous, role#assignee]
|
||||
|
||||
@@ -94,6 +94,130 @@ func (provider *provider) Stop(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *provider) Check(ctx context.Context, tupleReq *openfgav1.TupleKey) error {
|
||||
storeID, modelID := provider.getStoreIDandModelID()
|
||||
checkResponse, err := provider.openfgaServer.Check(
|
||||
ctx,
|
||||
&openfgav1.CheckRequest{
|
||||
StoreId: storeID,
|
||||
AuthorizationModelId: modelID,
|
||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||
User: tupleReq.User,
|
||||
Relation: tupleReq.Relation,
|
||||
Object: tupleReq.Object,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Newf(errors.TypeInternal, authtypes.ErrCodeAuthZUnavailable, "authorization server is unavailable").WithAdditional(err.Error())
|
||||
}
|
||||
|
||||
if !checkResponse.Allowed {
|
||||
return errors.Newf(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "subject %s cannot %s object %s", tupleReq.User, tupleReq.Relation, tupleReq.Object)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *provider) BatchCheck(ctx context.Context, tupleReq []*openfgav1.TupleKey) error {
|
||||
storeID, modelID := provider.getStoreIDandModelID()
|
||||
batchCheckItems := make([]*openfgav1.BatchCheckItem, 0)
|
||||
for _, tuple := range tupleReq {
|
||||
batchCheckItems = append(batchCheckItems, &openfgav1.BatchCheckItem{
|
||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||
User: tuple.User,
|
||||
Relation: tuple.Relation,
|
||||
Object: tuple.Object,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
checkResponse, err := provider.openfgaServer.BatchCheck(
|
||||
ctx,
|
||||
&openfgav1.BatchCheckRequest{
|
||||
StoreId: storeID,
|
||||
AuthorizationModelId: modelID,
|
||||
Checks: batchCheckItems,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Newf(errors.TypeInternal, authtypes.ErrCodeAuthZUnavailable, "authorization server is unavailable").WithAdditional(err.Error())
|
||||
}
|
||||
|
||||
for _, checkResponse := range checkResponse.Result {
|
||||
if checkResponse.GetAllowed() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "")
|
||||
|
||||
}
|
||||
|
||||
func (provider *provider) CheckWithTupleCreation(ctx context.Context, claims authtypes.Claims, orgID valuer.UUID, _ authtypes.Relation, translation authtypes.Relation, _ authtypes.Typeable, _ []authtypes.Selector) error {
|
||||
subject, err := authtypes.NewSubject(authtypes.TypeUser, claims.UserID, authtypes.Relation{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tuples, err := authtypes.TypeableOrganization.Tuples(subject, translation, []authtypes.Selector{authtypes.MustNewSelector(authtypes.TypeOrganization, orgID.StringValue())}, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = provider.BatchCheck(ctx, tuples)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *provider) Write(ctx context.Context, additions []*openfgav1.TupleKey, deletions []*openfgav1.TupleKey) error {
|
||||
storeID, modelID := provider.getStoreIDandModelID()
|
||||
deletionTuplesWithoutCondition := make([]*openfgav1.TupleKeyWithoutCondition, len(deletions))
|
||||
for idx, tuple := range deletions {
|
||||
deletionTuplesWithoutCondition[idx] = &openfgav1.TupleKeyWithoutCondition{User: tuple.User, Object: tuple.Object, Relation: tuple.Relation}
|
||||
}
|
||||
|
||||
_, err := provider.openfgaServer.Write(ctx, &openfgav1.WriteRequest{
|
||||
StoreId: storeID,
|
||||
AuthorizationModelId: modelID,
|
||||
Writes: func() *openfgav1.WriteRequestWrites {
|
||||
if len(additions) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &openfgav1.WriteRequestWrites{
|
||||
TupleKeys: additions,
|
||||
}
|
||||
}(),
|
||||
Deletes: func() *openfgav1.WriteRequestDeletes {
|
||||
if len(deletionTuplesWithoutCondition) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &openfgav1.WriteRequestDeletes{
|
||||
TupleKeys: deletionTuplesWithoutCondition,
|
||||
}
|
||||
}(),
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (provider *provider) ListObjects(ctx context.Context, subject string, relation authtypes.Relation, typeable authtypes.Typeable) ([]*authtypes.Object, error) {
|
||||
storeID, modelID := provider.getStoreIDandModelID()
|
||||
response, err := provider.openfgaServer.ListObjects(ctx, &openfgav1.ListObjectsRequest{
|
||||
StoreId: storeID,
|
||||
AuthorizationModelId: modelID,
|
||||
User: subject,
|
||||
Relation: relation.StringValue(),
|
||||
Type: typeable.Type().StringValue(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.TypeInternal, authtypes.ErrCodeAuthZUnavailable, "cannot list objects for subject %s with relation %s for type %s", subject, relation.StringValue(), typeable.Type().StringValue())
|
||||
}
|
||||
|
||||
return authtypes.MustNewObjectsFromStringSlice(response.Objects), nil
|
||||
}
|
||||
|
||||
func (provider *provider) getOrCreateStore(ctx context.Context, name string) (string, error) {
|
||||
stores, err := provider.openfgaServer.ListStores(ctx, &openfgav1.ListStoresRequest{})
|
||||
if err != nil {
|
||||
@@ -176,112 +300,12 @@ func (provider *provider) isModelEqual(expected *openfgav1.AuthorizationModel, a
|
||||
|
||||
}
|
||||
|
||||
func (provider *provider) Check(ctx context.Context, tupleReq *openfgav1.TupleKey) error {
|
||||
checkResponse, err := provider.openfgaServer.Check(
|
||||
ctx,
|
||||
&openfgav1.CheckRequest{
|
||||
StoreId: provider.storeID,
|
||||
AuthorizationModelId: provider.modelID,
|
||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||
User: tupleReq.User,
|
||||
Relation: tupleReq.Relation,
|
||||
Object: tupleReq.Object,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Newf(errors.TypeInternal, authtypes.ErrCodeAuthZUnavailable, "authorization server is unavailable").WithAdditional(err.Error())
|
||||
}
|
||||
func (provider *provider) getStoreIDandModelID() (string, string) {
|
||||
provider.mtx.RLock()
|
||||
defer provider.mtx.RUnlock()
|
||||
|
||||
if !checkResponse.Allowed {
|
||||
return errors.Newf(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "subject %s cannot %s object %s", tupleReq.User, tupleReq.Relation, tupleReq.Object)
|
||||
}
|
||||
storeID := provider.storeID
|
||||
modelID := provider.modelID
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *provider) BatchCheck(ctx context.Context, tupleReq []*openfgav1.TupleKey) error {
|
||||
batchCheckItems := make([]*openfgav1.BatchCheckItem, 0)
|
||||
for _, tuple := range tupleReq {
|
||||
batchCheckItems = append(batchCheckItems, &openfgav1.BatchCheckItem{
|
||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||
User: tuple.User,
|
||||
Relation: tuple.Relation,
|
||||
Object: tuple.Object,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
checkResponse, err := provider.openfgaServer.BatchCheck(
|
||||
ctx,
|
||||
&openfgav1.BatchCheckRequest{
|
||||
StoreId: provider.storeID,
|
||||
AuthorizationModelId: provider.modelID,
|
||||
Checks: batchCheckItems,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Newf(errors.TypeInternal, authtypes.ErrCodeAuthZUnavailable, "authorization server is unavailable").WithAdditional(err.Error())
|
||||
}
|
||||
|
||||
for _, checkResponse := range checkResponse.Result {
|
||||
if checkResponse.GetAllowed() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "")
|
||||
|
||||
}
|
||||
|
||||
func (provider *provider) CheckWithTupleCreation(ctx context.Context, claims authtypes.Claims, orgID valuer.UUID, _ authtypes.Relation, translation authtypes.Relation, _ authtypes.Typeable, _ []authtypes.Selector) error {
|
||||
subject, err := authtypes.NewSubject(authtypes.TypeUser, claims.UserID, authtypes.Relation{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tuples, err := authtypes.TypeableOrganization.Tuples(subject, translation, []authtypes.Selector{authtypes.MustNewSelector(authtypes.TypeOrganization, orgID.StringValue())}, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = provider.BatchCheck(ctx, tuples)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *provider) Write(ctx context.Context, additions []*openfgav1.TupleKey, deletions []*openfgav1.TupleKey) error {
|
||||
deletionTuplesWithoutCondition := make([]*openfgav1.TupleKeyWithoutCondition, len(deletions))
|
||||
for idx, tuple := range deletions {
|
||||
deletionTuplesWithoutCondition[idx] = &openfgav1.TupleKeyWithoutCondition{User: tuple.User, Object: tuple.Object, Relation: tuple.Relation}
|
||||
}
|
||||
|
||||
_, err := provider.openfgaServer.Write(ctx, &openfgav1.WriteRequest{
|
||||
StoreId: provider.storeID,
|
||||
AuthorizationModelId: provider.modelID,
|
||||
Writes: &openfgav1.WriteRequestWrites{
|
||||
TupleKeys: additions,
|
||||
},
|
||||
Deletes: &openfgav1.WriteRequestDeletes{
|
||||
TupleKeys: deletionTuplesWithoutCondition,
|
||||
},
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (provider *provider) ListObjects(ctx context.Context, subject string, relation authtypes.Relation, typeable authtypes.Typeable) ([]*authtypes.Object, error) {
|
||||
response, err := provider.openfgaServer.ListObjects(ctx, &openfgav1.ListObjectsRequest{
|
||||
StoreId: provider.storeID,
|
||||
AuthorizationModelId: provider.modelID,
|
||||
User: subject,
|
||||
Relation: relation.StringValue(),
|
||||
Type: typeable.Type().StringValue(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.TypeInternal, authtypes.ErrCodeAuthZUnavailable, "cannot list objects for subject %s with relation %s for type %s", subject, relation.StringValue(), typeable.Type().StringValue())
|
||||
}
|
||||
|
||||
return authtypes.MustNewObjectsFromStringSlice(response.Objects), nil
|
||||
return storeID, modelID
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ type handler struct {
|
||||
module role.Module
|
||||
}
|
||||
|
||||
func NewHandler(module role.Module) (role.Handler, error) {
|
||||
return &handler{module: module}, nil
|
||||
func NewHandler(module role.Module) role.Handler {
|
||||
return &handler{module: module}
|
||||
}
|
||||
|
||||
func (handler *handler) Create(rw http.ResponseWriter, r *http.Request) {
|
||||
@@ -28,11 +28,6 @@ func (handler *handler) Create(rw http.ResponseWriter, r *http.Request) {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
req := new(roletypes.PostableRole)
|
||||
if err := binding.JSON.BindBody(r.Body, req); err != nil {
|
||||
@@ -40,7 +35,7 @@ func (handler *handler) Create(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
role, err := handler.module.Create(ctx, orgID, req.DisplayName, req.Description)
|
||||
role, err := handler.module.Create(ctx, valuer.MustNewUUID(claims.OrgID), req.DisplayName, req.Description)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
@@ -56,11 +51,6 @@ func (handler *handler) Get(rw http.ResponseWriter, r *http.Request) {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
id, ok := mux.Vars(r)["id"]
|
||||
if !ok {
|
||||
@@ -73,7 +63,7 @@ func (handler *handler) Get(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
role, err := handler.module.Get(ctx, orgID, roleID)
|
||||
role, err := handler.module.Get(ctx, valuer.MustNewUUID(claims.OrgID), roleID)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
@@ -89,11 +79,6 @@ func (handler *handler) GetObjects(rw http.ResponseWriter, r *http.Request) {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
id, ok := mux.Vars(r)["id"]
|
||||
if !ok {
|
||||
@@ -117,7 +102,7 @@ func (handler *handler) GetObjects(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
objects, err := handler.module.GetObjects(ctx, orgID, roleID, relation)
|
||||
objects, err := handler.module.GetObjects(ctx, valuer.MustNewUUID(claims.OrgID), roleID, relation)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
@@ -147,13 +132,8 @@ func (handler *handler) List(rw http.ResponseWriter, r *http.Request) {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
roles, err := handler.module.List(ctx, orgID)
|
||||
roles, err := handler.module.List(ctx, valuer.MustNewUUID(claims.OrgID))
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
@@ -169,11 +149,6 @@ func (handler *handler) Patch(rw http.ResponseWriter, r *http.Request) {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
id, ok := mux.Vars(r)["id"]
|
||||
if !ok {
|
||||
@@ -192,7 +167,7 @@ func (handler *handler) Patch(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err = handler.module.Patch(ctx, orgID, roleID, req.DisplayName, req.Description)
|
||||
err = handler.module.Patch(ctx, valuer.MustNewUUID(claims.OrgID), roleID, req.DisplayName, req.Description)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
@@ -208,11 +183,6 @@ func (handler *handler) PatchObjects(rw http.ResponseWriter, r *http.Request) {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
id, ok := mux.Vars(r)["id"]
|
||||
if !ok {
|
||||
@@ -248,7 +218,7 @@ func (handler *handler) PatchObjects(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err = handler.module.PatchObjects(ctx, orgID, roleID, relation, patchableObjects.Additions, patchableObjects.Deletions)
|
||||
err = handler.module.PatchObjects(ctx, valuer.MustNewUUID(claims.OrgID), roleID, relation, patchableObjects.Additions, patchableObjects.Deletions)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
@@ -264,11 +234,6 @@ func (handler *handler) Delete(rw http.ResponseWriter, r *http.Request) {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
id, ok := mux.Vars(r)["id"]
|
||||
if !ok {
|
||||
@@ -281,7 +246,7 @@ func (handler *handler) Delete(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err = handler.module.Delete(ctx, orgID, roleID)
|
||||
err = handler.module.Delete(ctx, valuer.MustNewUUID(claims.OrgID), roleID)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
|
||||
@@ -17,12 +17,12 @@ type module struct {
|
||||
authz authz.AuthZ
|
||||
}
|
||||
|
||||
func NewModule(ctx context.Context, store roletypes.Store, authz authz.AuthZ, registry []role.RegisterTypeable) (role.Module, error) {
|
||||
func NewModule(store roletypes.Store, authz authz.AuthZ, registry []role.RegisterTypeable) role.Module {
|
||||
return &module{
|
||||
store: store,
|
||||
authz: authz,
|
||||
registry: registry,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (module *module) Create(ctx context.Context, orgID valuer.UUID, displayName, description string) (*roletypes.Role, error) {
|
||||
|
||||
@@ -13,8 +13,8 @@ type store struct {
|
||||
sqlstore sqlstore.SQLStore
|
||||
}
|
||||
|
||||
func NewStore(sqlstore sqlstore.SQLStore) (roletypes.Store, error) {
|
||||
return &store{sqlstore: sqlstore}, nil
|
||||
func NewStore(sqlstore sqlstore.SQLStore) roletypes.Store {
|
||||
return &store{sqlstore: sqlstore}
|
||||
}
|
||||
|
||||
func (store *store) Create(ctx context.Context, role *roletypes.StorableRole) error {
|
||||
@@ -38,7 +38,7 @@ func (store *store) Get(ctx context.Context, orgID valuer.UUID, id valuer.UUID)
|
||||
BunDB().
|
||||
NewSelect().
|
||||
Model(role).
|
||||
Where("orgID = ?", orgID).
|
||||
Where("org_id = ?", orgID).
|
||||
Where("id = ?", id).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
@@ -55,7 +55,7 @@ func (store *store) List(ctx context.Context, orgID valuer.UUID) ([]*roletypes.S
|
||||
BunDB().
|
||||
NewSelect().
|
||||
Model(&roles).
|
||||
Where("orgID = ?", orgID).
|
||||
Where("org_id = ?", orgID).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, store.sqlstore.WrapNotFoundErrf(err, roletypes.ErrCodeRoleNotFound, "no roles found in org_id: %s", orgID)
|
||||
|
||||
@@ -138,6 +138,7 @@ func NewSQLMigrationProviderFactories(
|
||||
sqlmigration.NewUpdateTTLSettingForCustomRetentionFactory(sqlstore, sqlschema),
|
||||
sqlmigration.NewAddRoutePolicyFactory(sqlstore, sqlschema),
|
||||
sqlmigration.NewAddAuthTokenFactory(sqlstore, sqlschema),
|
||||
sqlmigration.NewAddAuthzFactory(sqlstore, sqlschema),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
145
pkg/sqlmigration/051_add_authz.go
Normal file
145
pkg/sqlmigration/051_add_authz.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package sqlmigration
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/sqlschema"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/uptrace/bun"
|
||||
"github.com/uptrace/bun/migrate"
|
||||
)
|
||||
|
||||
type addAuthz struct {
|
||||
sqlstore sqlstore.SQLStore
|
||||
sqlschema sqlschema.SQLSchema
|
||||
}
|
||||
|
||||
func NewAddAuthzFactory(sqlstore sqlstore.SQLStore, sqlschema sqlschema.SQLSchema) factory.ProviderFactory[SQLMigration, Config] {
|
||||
return factory.NewProviderFactory(factory.MustNewName("add_authz"), func(ctx context.Context, ps factory.ProviderSettings, c Config) (SQLMigration, error) {
|
||||
return newAddAuthz(ctx, ps, c, sqlstore, sqlschema)
|
||||
})
|
||||
}
|
||||
|
||||
func newAddAuthz(_ context.Context, _ factory.ProviderSettings, _ Config, sqlstore sqlstore.SQLStore, sqlschema sqlschema.SQLSchema) (SQLMigration, error) {
|
||||
return &addAuthz{
|
||||
sqlstore: sqlstore,
|
||||
sqlschema: sqlschema,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (migration *addAuthz) Register(migrations *migrate.Migrations) error {
|
||||
if err := migrations.Register(migration.Up, migration.Down); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (migration *addAuthz) Up(ctx context.Context, db *bun.DB) error {
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = tx.Rollback()
|
||||
}()
|
||||
|
||||
sqls := [][]byte{}
|
||||
tableSQLs := migration.sqlschema.Operator().CreateTable(&sqlschema.Table{
|
||||
Name: "tuple",
|
||||
Columns: []*sqlschema.Column{
|
||||
{Name: "store", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "object_type", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "object_id", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "relation", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "user_object_type", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "user_object_id", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "user_relation", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "user_type", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "ulid", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "inserted_at", DataType: sqlschema.DataTypeTimestamp, Nullable: false},
|
||||
{Name: "condition_name", DataType: sqlschema.DataTypeText, Nullable: true},
|
||||
{Name: "condition_context", DataType: sqlschema.DataTypeText, Nullable: true},
|
||||
},
|
||||
PrimaryKeyConstraint: &sqlschema.PrimaryKeyConstraint{ColumnNames: []sqlschema.ColumnName{"store", "object_type", "object_id", "relation", "user_object_type", "user_object_id", "user_relation"}},
|
||||
})
|
||||
sqls = append(sqls, tableSQLs...)
|
||||
|
||||
indexSQLs := migration.sqlschema.Operator().CreateIndex(&sqlschema.UniqueIndex{TableName: "tuple", ColumnNames: []sqlschema.ColumnName{"ulid"}})
|
||||
sqls = append(sqls, indexSQLs...)
|
||||
|
||||
tableSQLs = migration.sqlschema.Operator().CreateTable(&sqlschema.Table{
|
||||
Name: "authorization_model",
|
||||
Columns: []*sqlschema.Column{
|
||||
{Name: "store", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "authorization_model_id", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "schema_version", DataType: sqlschema.DataTypeText, Nullable: false, Default: "1.1"},
|
||||
{Name: "serialized_protobuf", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
},
|
||||
PrimaryKeyConstraint: &sqlschema.PrimaryKeyConstraint{ColumnNames: []sqlschema.ColumnName{"store", "authorization_model_id"}},
|
||||
})
|
||||
sqls = append(sqls, tableSQLs...)
|
||||
|
||||
tableSQLs = migration.sqlschema.Operator().CreateTable(&sqlschema.Table{
|
||||
Name: "store",
|
||||
Columns: []*sqlschema.Column{
|
||||
{Name: "id", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "name", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "created_at", DataType: sqlschema.DataTypeTimestamp, Nullable: false},
|
||||
{Name: "updated_at", DataType: sqlschema.DataTypeTimestamp, Nullable: true},
|
||||
{Name: "deleted_at", DataType: sqlschema.DataTypeTimestamp, Nullable: true},
|
||||
},
|
||||
PrimaryKeyConstraint: &sqlschema.PrimaryKeyConstraint{ColumnNames: []sqlschema.ColumnName{"id"}},
|
||||
})
|
||||
sqls = append(sqls, tableSQLs...)
|
||||
|
||||
tableSQLs = migration.sqlschema.Operator().CreateTable(&sqlschema.Table{
|
||||
Name: "assertion",
|
||||
Columns: []*sqlschema.Column{
|
||||
{Name: "store", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "authorization_model_id", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "assertions", DataType: sqlschema.DataTypeText, Nullable: true},
|
||||
},
|
||||
PrimaryKeyConstraint: &sqlschema.PrimaryKeyConstraint{ColumnNames: []sqlschema.ColumnName{"store", "authorization_model_id"}},
|
||||
})
|
||||
sqls = append(sqls, tableSQLs...)
|
||||
|
||||
tableSQLs = migration.sqlschema.Operator().CreateTable(&sqlschema.Table{
|
||||
Name: "changelog",
|
||||
Columns: []*sqlschema.Column{
|
||||
{Name: "store", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "object_type", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "object_id", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "relation", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "user_object_type", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "user_object_id", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "user_relation", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "operation", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "ulid", DataType: sqlschema.DataTypeText, Nullable: false},
|
||||
{Name: "inserted_at", DataType: sqlschema.DataTypeTimestamp, Nullable: false},
|
||||
{Name: "condition_name", DataType: sqlschema.DataTypeText, Nullable: true},
|
||||
{Name: "condition_context", DataType: sqlschema.DataTypeText, Nullable: true},
|
||||
},
|
||||
PrimaryKeyConstraint: &sqlschema.PrimaryKeyConstraint{ColumnNames: []sqlschema.ColumnName{"store", "ulid", "object_type"}},
|
||||
})
|
||||
sqls = append(sqls, tableSQLs...)
|
||||
|
||||
for _, sql := range sqls {
|
||||
if _, err := tx.ExecContext(ctx, string(sql)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (migration *addAuthz) Down(context.Context, *bun.DB) error {
|
||||
return nil
|
||||
}
|
||||
@@ -9,6 +9,9 @@ import (
|
||||
|
||||
var (
|
||||
nameRegex = regexp.MustCompile("^[a-z]{1,35}$")
|
||||
|
||||
_ json.Marshaler = new(Name)
|
||||
_ json.Unmarshaler = new(Name)
|
||||
)
|
||||
|
||||
type Name struct {
|
||||
@@ -36,6 +39,10 @@ func (name Name) String() string {
|
||||
return name.val
|
||||
}
|
||||
|
||||
func (name *Name) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(name.val)
|
||||
}
|
||||
|
||||
func (name *Name) UnmarshalJSON(data []byte) error {
|
||||
nameStr := ""
|
||||
err := json.Unmarshal(data, &nameStr)
|
||||
|
||||
@@ -20,11 +20,11 @@ var (
|
||||
)
|
||||
|
||||
var TypeableRelations = map[Type][]Relation{
|
||||
TypeUser: {RelationRead, RelationUpdate, RelationDelete},
|
||||
TypeRole: {RelationAssignee, RelationRead, RelationUpdate, RelationDelete},
|
||||
TypeOrganization: {RelationCreate, RelationRead, RelationUpdate, RelationDelete, RelationList},
|
||||
TypeResource: {RelationRead, RelationUpdate, RelationDelete, RelationBlock},
|
||||
TypeResources: {RelationCreate, RelationList},
|
||||
TypeUser: {RelationRead, RelationUpdate, RelationDelete},
|
||||
TypeRole: {RelationAssignee, RelationRead, RelationUpdate, RelationDelete},
|
||||
TypeOrganization: {RelationCreate, RelationRead, RelationUpdate, RelationDelete, RelationList},
|
||||
TypeMetaResource: {RelationRead, RelationUpdate, RelationDelete, RelationBlock},
|
||||
TypeMetaResources: {RelationCreate, RelationList},
|
||||
}
|
||||
|
||||
type Relation struct{ valuer.String }
|
||||
|
||||
@@ -9,16 +9,21 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCodeAuthZInvalidSelectorRegex = errors.MustNewCode("authz_invalid_selector_regex")
|
||||
ErrCodeAuthZInvalidSelector = errors.MustNewCode("authz_invalid_selector")
|
||||
)
|
||||
|
||||
var (
|
||||
_ json.Marshaler = new(Selector)
|
||||
_ json.Unmarshaler = new(Selector)
|
||||
)
|
||||
|
||||
var (
|
||||
typeUserSelectorRegex = regexp.MustCompile(`^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$`)
|
||||
typeRoleSelectorRegex = regexp.MustCompile(`^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$`)
|
||||
typeOrganizationSelectorRegex = regexp.MustCompile(`^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$`)
|
||||
typeResourceSelectorRegex = regexp.MustCompile(`^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$`)
|
||||
// resources selectors are used to select either all or none
|
||||
typeResourcesSelectorRegex = regexp.MustCompile(`^\*$`)
|
||||
typeMetaResourceSelectorRegex = regexp.MustCompile(`^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$`)
|
||||
// metaresources selectors are used to select either all or none
|
||||
typeMetaResourcesSelectorRegex = regexp.MustCompile(`^\*$`)
|
||||
)
|
||||
|
||||
type SelectorCallbackFn func(context.Context, Claims) ([]Selector, error)
|
||||
@@ -36,33 +41,6 @@ func NewSelector(typed Type, selector string) (Selector, error) {
|
||||
return Selector{val: selector}, nil
|
||||
}
|
||||
|
||||
func IsValidSelector(typed Type, selector string) error {
|
||||
switch typed {
|
||||
case TypeUser:
|
||||
if !typeUserSelectorRegex.MatchString(selector) {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidSelectorRegex, "selector must conform to regex %s", typeUserSelectorRegex.String())
|
||||
}
|
||||
case TypeRole:
|
||||
if !typeRoleSelectorRegex.MatchString(selector) {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidSelectorRegex, "selector must conform to regex %s", typeRoleSelectorRegex.String())
|
||||
}
|
||||
case TypeOrganization:
|
||||
if !typeOrganizationSelectorRegex.MatchString(selector) {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidSelectorRegex, "selector must conform to regex %s", typeOrganizationSelectorRegex.String())
|
||||
}
|
||||
case TypeResource:
|
||||
if !typeResourceSelectorRegex.MatchString(selector) {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidSelectorRegex, "selector must conform to regex %s", typeResourceSelectorRegex.String())
|
||||
}
|
||||
case TypeResources:
|
||||
if !typeResourcesSelectorRegex.MatchString(selector) {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidSelectorRegex, "selector must conform to regex %s", typeResourcesSelectorRegex.String())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func MustNewSelector(typed Type, input string) Selector {
|
||||
selector, err := NewSelector(typed, input)
|
||||
if err != nil {
|
||||
@@ -72,6 +50,10 @@ func MustNewSelector(typed Type, input string) Selector {
|
||||
return selector
|
||||
}
|
||||
|
||||
func (selector *Selector) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(selector.val)
|
||||
}
|
||||
|
||||
func (selector Selector) String() string {
|
||||
return selector.val
|
||||
}
|
||||
@@ -83,8 +65,40 @@ func (typed *Selector) UnmarshalJSON(data []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
shadow := Selector{val: str}
|
||||
*typed = shadow
|
||||
alias := Selector{val: str}
|
||||
*typed = alias
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsValidSelector(typed Type, selector string) error {
|
||||
switch typed {
|
||||
case TypeUser:
|
||||
if !typeUserSelectorRegex.MatchString(selector) {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidSelector, "selector must conform to regex %s", typeUserSelectorRegex.String())
|
||||
}
|
||||
return nil
|
||||
case TypeRole:
|
||||
if !typeRoleSelectorRegex.MatchString(selector) {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidSelector, "selector must conform to regex %s", typeRoleSelectorRegex.String())
|
||||
}
|
||||
return nil
|
||||
case TypeOrganization:
|
||||
if !typeOrganizationSelectorRegex.MatchString(selector) {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidSelector, "selector must conform to regex %s", typeOrganizationSelectorRegex.String())
|
||||
}
|
||||
return nil
|
||||
case TypeMetaResource:
|
||||
if !typeMetaResourceSelectorRegex.MatchString(selector) {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidSelector, "selector must conform to regex %s", typeMetaResourceSelectorRegex.String())
|
||||
}
|
||||
return nil
|
||||
case TypeMetaResources:
|
||||
if !typeMetaResourcesSelectorRegex.MatchString(selector) {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidSelector, "selector must conform to regex %s", typeMetaResourcesSelectorRegex.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidType, "invalid type: %s", typed)
|
||||
}
|
||||
|
||||
@@ -33,22 +33,24 @@ func NewObject(resource Resource, selector Selector) (*Object, error) {
|
||||
}
|
||||
|
||||
func MustNewObjectFromString(input string) *Object {
|
||||
parts := strings.Split(input, ":")
|
||||
if len(parts) != 3 {
|
||||
panic(errors.Newf(errors.TypeInternal, errors.CodeInternal, "invalid list objects output: %s", input))
|
||||
parts := strings.Split(input, "/")
|
||||
if len(parts) != 4 {
|
||||
panic(errors.Newf(errors.TypeInternal, errors.CodeInternal, "invalid input format: %s", input))
|
||||
}
|
||||
|
||||
typeParts := strings.Split(parts[0], ":")
|
||||
if len(typeParts) != 2 {
|
||||
panic(errors.Newf(errors.TypeInternal, errors.CodeInternal, "invalid type format: %s", parts[0]))
|
||||
}
|
||||
|
||||
resource := Resource{
|
||||
Type: MustNewType(parts[0]),
|
||||
Name: MustNewName(parts[1]),
|
||||
Type: MustNewType(typeParts[0]),
|
||||
Name: MustNewName(parts[2]),
|
||||
}
|
||||
|
||||
object := &Object{
|
||||
Resource: resource,
|
||||
Selector: MustNewSelector(resource.Type, parts[2]),
|
||||
}
|
||||
selector := MustNewSelector(resource.Type, parts[3])
|
||||
|
||||
return object
|
||||
return &Object{Resource: resource, Selector: selector}
|
||||
}
|
||||
|
||||
func MustNewObjectsFromStringSlice(input []string) []*Object {
|
||||
@@ -59,6 +61,14 @@ func MustNewObjectsFromStringSlice(input []string) []*Object {
|
||||
return objects
|
||||
}
|
||||
|
||||
func NewTransaction(relation Relation, object Object) (*Transaction, error) {
|
||||
if !slices.Contains(TypeableRelations[object.Resource.Type], relation) {
|
||||
return nil, errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidRelation, "invalid relation %s for type %s", relation.StringValue(), object.Resource.Type.StringValue())
|
||||
}
|
||||
|
||||
return &Transaction{Relation: relation, Object: object}, nil
|
||||
}
|
||||
|
||||
func (object *Object) UnmarshalJSON(data []byte) error {
|
||||
var shadow = struct {
|
||||
Resource Resource
|
||||
@@ -79,14 +89,6 @@ func (object *Object) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewTransaction(relation Relation, object Object) (*Transaction, error) {
|
||||
if !slices.Contains(TypeableRelations[object.Resource.Type], relation) {
|
||||
return nil, errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidRelation, "invalid relation %s for type %s", relation.StringValue(), object.Resource.Type.StringValue())
|
||||
}
|
||||
|
||||
return &Transaction{Relation: relation, Object: object}, nil
|
||||
}
|
||||
|
||||
func (transaction *Transaction) UnmarshalJSON(data []byte) error {
|
||||
var shadow = struct {
|
||||
Relation Relation
|
||||
|
||||
@@ -11,14 +11,15 @@ import (
|
||||
var (
|
||||
ErrCodeAuthZUnavailable = errors.MustNewCode("authz_unavailable")
|
||||
ErrCodeAuthZForbidden = errors.MustNewCode("authz_forbidden")
|
||||
ErrCodeAuthZInvalidType = errors.MustNewCode("authz_invalid_type")
|
||||
)
|
||||
|
||||
var (
|
||||
TypeUser = Type{valuer.NewString("user")}
|
||||
TypeRole = Type{valuer.NewString("role")}
|
||||
TypeOrganization = Type{valuer.NewString("organization")}
|
||||
TypeResource = Type{valuer.NewString("resource")}
|
||||
TypeResources = Type{valuer.NewString("resources")}
|
||||
TypeUser = Type{valuer.NewString("user")}
|
||||
TypeRole = Type{valuer.NewString("role")}
|
||||
TypeOrganization = Type{valuer.NewString("organization")}
|
||||
TypeMetaResource = Type{valuer.NewString("metaresource")}
|
||||
TypeMetaResources = Type{valuer.NewString("metaresources")}
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -53,12 +54,12 @@ func NewType(input string) (Type, error) {
|
||||
return TypeRole, nil
|
||||
case "organization":
|
||||
return TypeOrganization, nil
|
||||
case "resource":
|
||||
return TypeResource, nil
|
||||
case "resources":
|
||||
return TypeResources, nil
|
||||
case "metaresource":
|
||||
return TypeMetaResource, nil
|
||||
case "metaresources":
|
||||
return TypeMetaResources, nil
|
||||
default:
|
||||
return Type{}, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "invalid type: %s", input)
|
||||
return Type{}, errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidType, "invalid type: %s", input)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,12 +70,12 @@ func (typed *Type) UnmarshalJSON(data []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
shadow, err := NewType(str)
|
||||
alias, err := NewType(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*typed = shadow
|
||||
*typed = alias
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -86,21 +87,21 @@ func NewTypeableFromType(typed Type, name Name) (Typeable, error) {
|
||||
return TypeableUser, nil
|
||||
case TypeOrganization:
|
||||
return TypeableOrganization, nil
|
||||
case TypeResource:
|
||||
resource, err := NewTypeableResource(name)
|
||||
case TypeMetaResource:
|
||||
resource, err := NewTypeableMetaResource(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resource, nil
|
||||
case TypeResources:
|
||||
resources, err := NewTypeableResources(name)
|
||||
case TypeMetaResources:
|
||||
resources, err := NewTypeableMetaResources(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "invalid type")
|
||||
return nil, errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidType, "invalid type %s", typed)
|
||||
}
|
||||
|
||||
func MustNewTypeableFromType(typed Type, name Name) Typeable {
|
||||
|
||||
48
pkg/types/authtypes/typeable_metaresource.go
Normal file
48
pkg/types/authtypes/typeable_metaresource.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package authtypes
|
||||
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||
)
|
||||
|
||||
var _ Typeable = new(typeableMetaResource)
|
||||
|
||||
type typeableMetaResource struct {
|
||||
name Name
|
||||
}
|
||||
|
||||
func NewTypeableMetaResource(name Name) (Typeable, error) {
|
||||
return &typeableMetaResource{name: name}, nil
|
||||
}
|
||||
|
||||
func MustNewTypeableMetaResource(name Name) Typeable {
|
||||
typeableesource, err := NewTypeableMetaResource(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return typeableesource
|
||||
}
|
||||
|
||||
func (typeableMetaResource *typeableMetaResource) Tuples(subject string, relation Relation, selector []Selector, orgID valuer.UUID) ([]*openfgav1.TupleKey, error) {
|
||||
tuples := make([]*openfgav1.TupleKey, 0)
|
||||
for _, selector := range selector {
|
||||
object := typeableMetaResource.Prefix(orgID) + "/" + selector.String()
|
||||
tuples = append(tuples, &openfgav1.TupleKey{User: subject, Relation: relation.StringValue(), Object: object})
|
||||
}
|
||||
|
||||
return tuples, nil
|
||||
}
|
||||
|
||||
func (typeableMetaResource *typeableMetaResource) Type() Type {
|
||||
return TypeMetaResource
|
||||
}
|
||||
|
||||
func (typeableMetaResource *typeableMetaResource) Name() Name {
|
||||
return typeableMetaResource.name
|
||||
}
|
||||
|
||||
// example: metaresource:organization/0199c47d-f61b-7833-bc5f-c0730f12f046/dashboard
|
||||
func (typeableMetaResource *typeableMetaResource) Prefix(orgID valuer.UUID) string {
|
||||
return typeableMetaResource.Type().StringValue() + ":" + "organization" + "/" + orgID.StringValue() + "/" + typeableMetaResource.Name().String()
|
||||
}
|
||||
48
pkg/types/authtypes/typeable_metaresources.go
Normal file
48
pkg/types/authtypes/typeable_metaresources.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package authtypes
|
||||
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||
)
|
||||
|
||||
var _ Typeable = new(typeableMetaResources)
|
||||
|
||||
type typeableMetaResources struct {
|
||||
name Name
|
||||
}
|
||||
|
||||
func NewTypeableMetaResources(name Name) (Typeable, error) {
|
||||
return &typeableMetaResources{name: name}, nil
|
||||
}
|
||||
|
||||
func MustNewTypeableMetaResources(name Name) Typeable {
|
||||
resources, err := NewTypeableMetaResources(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return resources
|
||||
}
|
||||
|
||||
func (typeableResources *typeableMetaResources) Tuples(subject string, relation Relation, selector []Selector, orgID valuer.UUID) ([]*openfgav1.TupleKey, error) {
|
||||
tuples := make([]*openfgav1.TupleKey, 0)
|
||||
for _, selector := range selector {
|
||||
object := typeableResources.Prefix(orgID) + "/" + selector.String()
|
||||
tuples = append(tuples, &openfgav1.TupleKey{User: subject, Relation: relation.StringValue(), Object: object})
|
||||
}
|
||||
|
||||
return tuples, nil
|
||||
}
|
||||
|
||||
func (typeableMetaResources *typeableMetaResources) Type() Type {
|
||||
return TypeMetaResources
|
||||
}
|
||||
|
||||
func (typeableMetaResources *typeableMetaResources) Name() Name {
|
||||
return typeableMetaResources.name
|
||||
}
|
||||
|
||||
// example: metaresources:organization/0199c47d-f61b-7833-bc5f-c0730f12f046/dashboards
|
||||
func (typeableMetaResources *typeableMetaResources) Prefix(orgID valuer.UUID) string {
|
||||
return typeableMetaResources.Type().StringValue() + ":" + "organization" + "/" + orgID.StringValue() + "/" + typeableMetaResources.Name().String()
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package authtypes
|
||||
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||
)
|
||||
|
||||
var _ Typeable = new(typeableResource)
|
||||
|
||||
type typeableResource struct {
|
||||
name Name
|
||||
}
|
||||
|
||||
func NewTypeableResource(name Name) (Typeable, error) {
|
||||
return &typeableResource{name: name}, nil
|
||||
}
|
||||
|
||||
func MustNewTypeableResource(name Name) Typeable {
|
||||
typeableesource, err := NewTypeableResource(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return typeableesource
|
||||
}
|
||||
|
||||
func (typeableResource *typeableResource) Tuples(subject string, relation Relation, selector []Selector, orgID valuer.UUID) ([]*openfgav1.TupleKey, error) {
|
||||
tuples := make([]*openfgav1.TupleKey, 0)
|
||||
for _, selector := range selector {
|
||||
object := typeableResource.Prefix(orgID) + "/" + selector.String()
|
||||
tuples = append(tuples, &openfgav1.TupleKey{User: subject, Relation: relation.StringValue(), Object: object})
|
||||
}
|
||||
|
||||
return tuples, nil
|
||||
}
|
||||
|
||||
func (typeableResource *typeableResource) Type() Type {
|
||||
return TypeResource
|
||||
}
|
||||
|
||||
func (typeableResource *typeableResource) Name() Name {
|
||||
return typeableResource.name
|
||||
}
|
||||
|
||||
func (typeableResource *typeableResource) Prefix(orgID valuer.UUID) string {
|
||||
// example: resource:organization/0199c47d-f61b-7833-bc5f-c0730f12f046/dashboard
|
||||
return typeableResource.Type().StringValue() + ":" + "organization" + "/" + orgID.StringValue() + "/" + typeableResource.Name().String()
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package authtypes
|
||||
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||
)
|
||||
|
||||
var _ Typeable = new(typeableResources)
|
||||
|
||||
type typeableResources struct {
|
||||
name Name
|
||||
}
|
||||
|
||||
func NewTypeableResources(name Name) (Typeable, error) {
|
||||
return &typeableResources{name: name}, nil
|
||||
}
|
||||
|
||||
func MustNewTypeableResources(name Name) Typeable {
|
||||
resources, err := NewTypeableResources(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return resources
|
||||
}
|
||||
|
||||
func (typeableResources *typeableResources) Tuples(subject string, relation Relation, selector []Selector, orgID valuer.UUID) ([]*openfgav1.TupleKey, error) {
|
||||
tuples := make([]*openfgav1.TupleKey, 0)
|
||||
for _, selector := range selector {
|
||||
object := typeableResources.Prefix(orgID) + "/" + selector.String()
|
||||
tuples = append(tuples, &openfgav1.TupleKey{User: subject, Relation: relation.StringValue(), Object: object})
|
||||
}
|
||||
|
||||
return tuples, nil
|
||||
}
|
||||
|
||||
func (typeableResources *typeableResources) Type() Type {
|
||||
return TypeResources
|
||||
}
|
||||
|
||||
func (typeableResources *typeableResources) Name() Name {
|
||||
return typeableResources.name
|
||||
}
|
||||
|
||||
func (typeableResources *typeableResources) Prefix(orgID valuer.UUID) string {
|
||||
// example: resources:organization/0199c47d-f61b-7833-bc5f-c0730f12f046/dashboards
|
||||
return typeableResources.Type().StringValue() + ":" + "organization" + "/" + orgID.StringValue() + "/" + typeableResources.Name().String()
|
||||
}
|
||||
@@ -27,7 +27,7 @@ func (typeableRole *typeableRole) Name() Name {
|
||||
return MustNewName("role")
|
||||
}
|
||||
|
||||
// example: role:organization/0199c47d-f61b-7833-bc5f-c0730f12f046/role
|
||||
func (typeableRole *typeableRole) Prefix(orgID valuer.UUID) string {
|
||||
// example: role:organization/0199c47d-f61b-7833-bc5f-c0730f12f046/role
|
||||
return typeableRole.Type().StringValue() + ":" + "organization" + "/" + orgID.StringValue() + "/" + typeableRole.Name().String()
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ func (typeableUser *typeableUser) Name() Name {
|
||||
return MustNewName("user")
|
||||
}
|
||||
|
||||
// example: user:organization/0199c47d-f61b-7833-bc5f-c0730f12f046/user
|
||||
func (typeableUser *typeableUser) Prefix(orgID valuer.UUID) string {
|
||||
// example: user:organization/0199c47d-f61b-7833-bc5f-c0730f12f046/user
|
||||
return typeableUser.Type().StringValue() + ":" + "organization" + "/" + orgID.StringValue() + "/" + typeableUser.Name().String()
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
TypeableResourceDashboard = authtypes.MustNewTypeableResource(authtypes.MustNewName("dashboard"))
|
||||
TypeableResourcesDashboards = authtypes.MustNewTypeableResources(authtypes.MustNewName("dashboards"))
|
||||
TypeableResourceDashboard = authtypes.MustNewTypeableMetaResource(authtypes.MustNewName("dashboard"))
|
||||
TypeableResourcesDashboards = authtypes.MustNewTypeableMetaResources(authtypes.MustNewName("dashboards"))
|
||||
)
|
||||
|
||||
type StorableDashboard struct {
|
||||
|
||||
@@ -22,7 +22,7 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
TypeableResourcesRoles = authtypes.MustNewTypeableResources(authtypes.MustNewName("roles"))
|
||||
TypeableResourcesRoles = authtypes.MustNewTypeableMetaResources(authtypes.MustNewName("roles"))
|
||||
)
|
||||
|
||||
type StorableRole struct {
|
||||
|
||||
@@ -103,3 +103,7 @@ func (enum *Email) UnmarshalText(text []byte) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (enum Email) MarshalText() (text []byte, err error) {
|
||||
return []byte(enum.StringValue()), nil
|
||||
}
|
||||
|
||||
@@ -73,3 +73,7 @@ func (enum *String) UnmarshalText(text []byte) error {
|
||||
*enum = NewString(string(text))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (enum String) MarshalText() (text []byte, err error) {
|
||||
return []byte(enum.StringValue()), nil
|
||||
}
|
||||
|
||||
@@ -136,3 +136,7 @@ func (enum *UUID) UnmarshalText(text []byte) error {
|
||||
*enum = uuid
|
||||
return nil
|
||||
}
|
||||
|
||||
func (enum UUID) MarshalText() (text []byte, err error) {
|
||||
return []byte(enum.StringValue()), nil
|
||||
}
|
||||
|
||||
@@ -39,4 +39,7 @@ type Valuer interface {
|
||||
|
||||
// Implement encoding.TextUnmarshaler to allow the value to be unmarshalled from a string
|
||||
encoding.TextUnmarshaler
|
||||
|
||||
// Implement encoding.TextUnmarshaler to allow the value to be marshalled unto a string
|
||||
encoding.TextMarshaler
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user