casbinbunadapter

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 20, 2024 License: MIT Imports: 8 Imported by: 0

README

Casbin Bun Adapter for Postgres

GoDoc Go Report Card GitHub tag

Table of Contents

About

It is just adapter for Casbin using Golang ORM called Bun.

This adapter supports listening to the policies update in database via triggers, so when something is changed in database then your application would be aware of it.

AutoSave feature is implemented.

Supported Attentions/warnings:

  • PostgreSQL is only supported database currently (since of heavy trigger feature usage)! As for other databases (MySQL, SQLite, Microsoft SQL Server) PRs are welcome.

  • This repository is not pretend to be the best Casbin adapter, but it works for my use-cases. Check out others implementations here

  • Do not combine StartUpdatesListening and SavePolicy since it could cause infinite recursion. You should either update Casbin in-memory object with database table updates (via trigger) or update database table due Casbin in-memory updates (via direct method calls) but not both techniques same time.

  • While using StartUpdatesListening UPDATE operation on table calls RemovePolicy/AddPolicy sequentially without rollback mechanism. That means if AddPolicy call fails on *casbin.SyncedEnforcer then there will not be any rollback for previously called RemovePolicy

Installation

go get github.com/LdDl/casbin-bun-adapter

Usage

There are three examples how to use it:

  1. Plain example without AutoSave or PostgreSQL triggers involed - ./examples/custom_names
  2. Example with using AutoSave feature - ./examples/autosave_changes. Just add this line after *casbin.Enforcer is initialized:
    // ...
    enforcer.EnableAutoSave(true)
    // ...
    
  3. Example with using PostgreSQL (version 14.x and above) triggers feature - ./examples/listen_changes. It can be used with *casbin.SyncedEnforcer only:
    // ...
    trigger := casbinbunadapter.TriggerOptions{
        Name:               "casbin_call_trigger",
        FunctionName:       "update_policies_table",
        FunctionSchemaName: "public",
        FunctionReplace:    true,
        TriggerReplace:     true, // Works only for PostgreSQL 14.x and above
        ChannelName:        "CASBIN_UPDATE_MESSAGE",
    }
    adapter := casbinbunadapter.NewBunAdapter(
        dbConn,
        casbinbunadapter.WithMatcherOptions(matcher),
        casbinbunadapter.WithTriggerOptions(trigger),
    )
    // ...
    errCh := make(chan error)
    go func(enf *casbin.SyncedEnforcer, errCh chan error) {
        err = adapter.StartUpdatesListening(enf)
        if err != nil {
            log.Println("Error on database listener", err)
            errCh <- err
        }
    }(enforcer, errCh)
    // ...
    select {
    case e := <-errCh:
    	log.Println("Err", e)
    	return
    }
    

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	EVENT_PAYLOAD_INSERT = TriggerEventPayloadType("EVENT_CASBIN_INSERT")
	EVENT_PAYLOAD_UPDATE = TriggerEventPayloadType("EVENT_CASBIN_UPDATE")
	EVENT_PAYLOAD_DELETE = TriggerEventPayloadType("EVENT_CASBIN_DELETE")
)

Functions

func WithMatcherOptions

func WithMatcherOptions(matcher MatcherOptions) func(*BunAdapter)

WithMatcherOptions overrides default matching options. If some of keys are empty strings than default values will be applied

func WithTriggerOptions

func WithTriggerOptions(trigger TriggerOptions) func(*BunAdapter)

WithTriggerOptions overrides default trigger options. If some of keys are empty strings than default values will be applied

Types

type BunAdapter

type BunAdapter struct {
	*bun.DB
	// contains filtered or unexported fields
}

BunAdapter is just wrapper around *bun.DB

func NewBunAdapter

func NewBunAdapter(bunConnection *bun.DB, opts ...func(*BunAdapter)) *BunAdapter

NewBunAdapter returns new *BunAdapter. Connections to database must be provided. Other arguments are optional

func (*BunAdapter) AddPolicy

func (a *BunAdapter) AddPolicy(sec string, ptype string, rule []string) error

AddPolicy adds a policy rule to the storage. Needed for AutoSave, see the ref. https://casbin.org/docs/adapters/#autosave

func (*BunAdapter) LoadPolicy

func (a *BunAdapter) LoadPolicy(model model.Model) error

LoadPolicy loads all policy rules from the storage

func (*BunAdapter) PrepareTrigger

func (a *BunAdapter) PrepareTrigger() error

BuildTrigger creates function and trigger for sending database data changes payload. It will check if function exists and if not creates it It will check if trigger exists and if not creates it. Finalized function name will match following template: "$SCHEMA_NAME$.$FUNCTION_NAME$" Finalized trigger name will match following template: "$SCHEMA_NAME$_$TABLE_NAME$_$TRIGGER_NAME$"

func (*BunAdapter) RemoveFilteredPolicy

func (a *BunAdapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error

RemoveFilteredPolicy removes policy rules that match the filter from the storage. Needed for AutoSave, see the ref. https://casbin.org/docs/adapters/#autosave

func (*BunAdapter) RemovePolicy

func (a *BunAdapter) RemovePolicy(sec string, ptype string, rule []string) error

RemovePolicy removes a policy rule from the storage. Needed for AutoSave, see the ref. https://casbin.org/docs/adapters/#autosave

func (*BunAdapter) SavePolicy

func (a *BunAdapter) SavePolicy(model model.Model) error

SavePolicy saves all policy rules to the storage

func (*BunAdapter) StartUpdatesListening

func (a *BunAdapter) StartUpdatesListening(enforcer *casbin.SyncedEnforcer) error

type CasbinPolicy

type CasbinPolicy struct {
	bun.BaseModel `bun:"casbin_policies,alias:t"`
	ID            int    `bun:"id,pk,autoincrement" json:"id"` // I'm not sure if 'autoincrement' will work as IDENTITY rather than SERIAL
	PType         string `bun:"ptype,type:varchar(2),notnull,default:'p'" json:"ptype"`
	V0            string `bun:"v0,type:varchar(256),nullzero" json:"v0"`
	V1            string `bun:"v1,type:varchar(256),nullzero" json:"v1"`
	V2            string `bun:"v2,type:varchar(256),nullzero" json:"v2"`
	V3            string `bun:"v3,type:varchar(256),nullzero" json:"v3"`
	V4            string `bun:"v4,type:varchar(256),nullzero" json:"v4"`
	V5            string `bun:"v5,type:varchar(256),nullzero" json:"v5"`
}

CasbinPolicy is database storage format following the below https://casbin.org/docs/policy-storage#database-storage-format Template on which Golang struct has been prepared: CREATE TABLE casbin_policies (

  id int4 GENERATED BY DEFAULT AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START 1 CACHE 1 NO CYCLE) NOT NULL,
	ptype varchar(2) DEFAULT 'p'::character varying NOT NULL,
	v0 varchar(256) null,
	v1 varchar(256) null,
	v2 varchar(256) null,
	v3 varchar(256) null,
	v4 varchar(256) null,
	v5 varchar(256) null,
	CONSTRAINT casbin_policies_pk PRIMARY KEY (id),
	CONSTRAINT casbin_policies_unique UNIQUE NULLS NOT DISTINCT (ptype, v0, v1, v2, v3, v4, v5) -- Aware! This will work only for Postgres 15+. For the ref. see: https://stackoverflow.com/a/8289253/6026885

);

func NewCasbinPolicyFrom

func NewCasbinPolicyFrom(ptype string, rule []string) CasbinPolicy

NewCasbinPolicyFrom creates CasbinPolicy object from well-defined policy type and rules

type MatcherOptions

type MatcherOptions struct {
	SchemaName string
	TableName  string
	ID         string
	PType      string
	V0         string
	V1         string
	V2         string
	V3         string
	V4         string
	V5         string
}

MatcherOptions is for matching user defined columns to canonical Casbin columns

type TriggerDataPayload

type TriggerDataPayload struct {
	EventType TriggerEventPayloadType `json:"event_type"`
	Old       CasbinPolicy            `json:"old"`
	New       CasbinPolicy            `json:"new"`
}

type TriggerEventPayloadType

type TriggerEventPayloadType string

type TriggerOptions

type TriggerOptions struct {
	// Trigger name
	Name string
	// Function which must be executed
	FunctionName string
	// Schema name where function is located
	FunctionSchemaName string
	// If function needed to be replaced in case it exists
	FunctionReplace bool
	// If trigger needed to be replaced in case it exists. Be carefull. This is PostgreSQL 14.x and above feature!
	TriggerReplace bool
	// Name for PostgreSQL channel for listening updates
	ChannelName string
}

TriggerOptions is for defining trigger whicl will be executed after data update in database table

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL