Bootstrap copied BFF and local accounts with public Sendico contracts (#34)

This commit is contained in:
Stephan D committed 2026-09-12 13:37:48 +02:00
1 parent 67dc5b5b1e
commit 7062672a87
556 files changed
+42291 -777

No files matched your search

@@ -0,0 +1,357 @@
package mongoadapter
import (
"context"
"git.sendico.io/tech/sxchnge/bff/pkg/merrors"
"time"
"github.com/casbin/casbin/v3/model"
"github.com/casbin/casbin/v3/persist"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
)
const (
defaultDatabaseName = "casbin"
defaultCollectionName = "casbin_rule"
defaultTimeout = 30 * time.Second
)
var (
_ persist.BatchAdapter = (*Adapter)(nil)
_ persist.FilteredAdapter = (*Adapter)(nil)
_ persist.UpdatableAdapter = (*Adapter)(nil)
)
type Config struct {
DatabaseName string
CollectionName string
Timeout time.Duration
IsFiltered bool
}
type Rule struct {
PType string
V0 string
V1 string
V2 string
V3 string
V4 string
V5 string
}
type Adapter struct {
collection *mongo.Collection
timeout time.Duration
filtered bool
}
func New(client *mongo.Client, config *Config) (*Adapter, error) {
if config == nil {
config = &Config{}
}
databaseName := config.DatabaseName
if databaseName == "" {
databaseName = defaultDatabaseName
}
collectionName := config.CollectionName
if collectionName == "" {
collectionName = defaultCollectionName
}
timeout := config.Timeout
if timeout == 0 {
timeout = defaultTimeout
}
adapter := &Adapter{
collection: client.Database(databaseName).Collection(collectionName),
timeout: timeout,
filtered: config.IsFiltered,
}
if err := adapter.prepareIndexes(); err != nil {
return nil, err
}
return adapter, nil
}
func (a *Adapter) prepareIndexes() error {
keys := bson.D{
{Key: "ptype", Value: 1},
{Key: "v0", Value: 1},
{Key: "v1", Value: 1},
{Key: "v2", Value: 1},
{Key: "v3", Value: 1},
{Key: "v4", Value: 1},
{Key: "v5", Value: 1},
}
_, err := a.collection.Indexes().CreateOne(
context.Background(),
mongo.IndexModel{
Keys: keys,
Options: options.Index().SetUnique(true),
},
)
return err
}
func (a *Adapter) withTimeout() (context.Context, context.CancelFunc) {
return context.WithTimeout(context.Background(), a.timeout)
}
func loadPolicyLine(line Rule, m model.Model) error {
policy := line.policyWithType()
for len(policy) > 0 && policy[len(policy)-1] == "" {
policy = policy[:len(policy)-1]
}
if len(policy) == 0 {
return nil
}
return persist.LoadPolicyArray(policy, m)
}
func (r Rule) policyWithType() []string {
return []string{r.PType, r.V0, r.V1, r.V2, r.V3, r.V4, r.V5}
}
func (r Rule) policyValues() []string {
values := []string{r.V0, r.V1, r.V2, r.V3, r.V4, r.V5}
for len(values) > 0 && values[len(values)-1] == "" {
values = values[:len(values)-1]
}
return values
}
func ruleFromPolicy(ptype string, policy []string) Rule {
rule := Rule{PType: ptype}
if len(policy) > 0 {
rule.V0 = policy[0]
}
if len(policy) > 1 {
rule.V1 = policy[1]
}
if len(policy) > 2 {
rule.V2 = policy[2]
}
if len(policy) > 3 {
rule.V3 = policy[3]
}
if len(policy) > 4 {
rule.V4 = policy[4]
}
if len(policy) > 5 {
rule.V5 = policy[5]
}
return rule
}
func rulesFromPolicies(ptype string, policies [][]string) []Rule {
rules := make([]Rule, 0, len(policies))
for _, policy := range policies {
rules = append(rules, ruleFromPolicy(ptype, policy))
}
return rules
}
func rulesAsDocuments(rules []Rule) []any {
docs := make([]any, 0, len(rules))
for _, rule := range rules {
docs = append(docs, rule)
}
return docs
}
func selectorForPolicy(ptype string, fieldIndex int, fieldValues ...string) bson.M {
selector := bson.M{"ptype": ptype}
fields := []string{"v0", "v1", "v2", "v3", "v4", "v5"}
for i, fieldValue := range fieldValues {
if fieldValue == "" {
continue
}
field := fieldIndex + i
if field < 0 || field >= len(fields) {
continue
}
selector[fields[field]] = fieldValue
}
return selector
}
func (a *Adapter) LoadPolicy(m model.Model) error {
return a.LoadFilteredPolicy(m, nil)
}
func (a *Adapter) LoadFilteredPolicy(m model.Model, filter any) error {
if filter == nil {
a.filtered = false
filter = bson.D{}
} else {
a.filtered = true
}
ctx, cancel := a.withTimeout()
defer cancel()
cursor, err := a.collection.Find(ctx, filter)
if err != nil {
return err
}
defer func() {
_ = cursor.Close(ctx)
}()
for cursor.Next(ctx) {
var line Rule
if err := cursor.Decode(&line); err != nil {
return err
}
if err := loadPolicyLine(line, m); err != nil {
return err
}
}
return cursor.Err()
}
func (a *Adapter) IsFiltered() bool {
return a.filtered
}
func (a *Adapter) SavePolicy(m model.Model) error {
if a.filtered {
return merrors.Define("cannot save a filtered policy")
}
ctx, cancel := a.withTimeout()
defer cancel()
if _, err := a.collection.DeleteMany(ctx, bson.D{}); err != nil {
return err
}
var docs []any
for ptype, ast := range m["p"] {
docs = append(docs, rulesAsDocuments(rulesFromPolicies(ptype, ast.Policy))...)
}
for ptype, ast := range m["g"] {
docs = append(docs, rulesAsDocuments(rulesFromPolicies(ptype, ast.Policy))...)
}
if len(docs) == 0 {
return nil
}
_, err := a.collection.InsertMany(ctx, docs)
return err
}
func (a *Adapter) AddPolicy(_ string, ptype string, policy []string) error {
ctx, cancel := a.withTimeout()
defer cancel()
_, err := a.collection.InsertOne(ctx, ruleFromPolicy(ptype, policy))
return err
}
func (a *Adapter) AddPolicies(_ string, ptype string, policies [][]string) error {
if len(policies) == 0 {
return nil
}
ctx, cancel := a.withTimeout()
defer cancel()
_, err := a.collection.InsertMany(ctx, rulesAsDocuments(rulesFromPolicies(ptype, policies)))
return err
}
func (a *Adapter) RemovePolicy(_ string, ptype string, policy []string) error {
ctx, cancel := a.withTimeout()
defer cancel()
_, err := a.collection.DeleteOne(ctx, ruleFromPolicy(ptype, policy))
return err
}
func (a *Adapter) RemovePolicies(_ string, ptype string, policies [][]string) error {
ctx, cancel := a.withTimeout()
defer cancel()
for _, rule := range rulesFromPolicies(ptype, policies) {
if _, err := a.collection.DeleteOne(ctx, rule); err != nil {
return err
}
}
return nil
}
func (a *Adapter) RemoveFilteredPolicy(_ string, ptype string, fieldIndex int, fieldValues ...string) error {
ctx, cancel := a.withTimeout()
defer cancel()
_, err := a.collection.DeleteMany(ctx, selectorForPolicy(ptype, fieldIndex, fieldValues...))
return err
}
func (a *Adapter) UpdatePolicy(_ string, ptype string, oldPolicy, newPolicy []string) error {
ctx, cancel := a.withTimeout()
defer cancel()
_, err := a.collection.ReplaceOne(ctx, ruleFromPolicy(ptype, oldPolicy), ruleFromPolicy(ptype, newPolicy))
return err
}
func (a *Adapter) UpdatePolicies(_ string, ptype string, oldPolicies, newPolicies [][]string) error {
if len(oldPolicies) != len(newPolicies) {
return merrors.Define("old and new policy counts differ")
}
ctx, cancel := a.withTimeout()
defer cancel()
for i := range oldPolicies {
if _, err := a.collection.ReplaceOne(ctx, ruleFromPolicy(ptype, oldPolicies[i]), ruleFromPolicy(ptype, newPolicies[i])); err != nil {
return err
}
}
return nil
}
func (a *Adapter) UpdateFilteredPolicies(_ string, ptype string, newPolicies [][]string, fieldIndex int, fieldValues ...string) ([][]string, error) {
ctx, cancel := a.withTimeout()
defer cancel()
selector := selectorForPolicy(ptype, fieldIndex, fieldValues...)
cursor, err := a.collection.Find(ctx, selector)
if err != nil {
return nil, err
}
var oldPolicies [][]string
for cursor.Next(ctx) {
var line Rule
if err := cursor.Decode(&line); err != nil {
_ = cursor.Close(ctx)
return nil, err
}
oldPolicies = append(oldPolicies, line.policyValues())
}
if err := cursor.Close(ctx); err != nil {
return nil, err
}
if err := cursor.Err(); err != nil {
return nil, err
}
if _, err := a.collection.DeleteMany(ctx, selector); err != nil {
return nil, err
}
if len(newPolicies) == 0 {
return oldPolicies, nil
}
if _, err := a.collection.InsertMany(ctx, rulesAsDocuments(rulesFromPolicies(ptype, newPolicies))); err != nil {
return nil, err
}
return oldPolicies, nil
}