From 93329c484e19b4bc29a46f74d67377d78bd7a11b Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Mon, 25 Mar 2024 23:35:59 +0900 Subject: [PATCH 01/41] install packages (#5) --- go.mod | 17 +++++++++++++++++ go.sum | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 go.sum diff --git a/go.mod b/go.mod index 0de9de5..79f7da7 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,20 @@ module github.com/JunNishimura/casbin-bun-adapter go 1.22.0 + +require ( + github.com/casbin/casbin/v2 v2.85.0 + github.com/google/go-cmp v0.6.0 + github.com/uptrace/bun v1.1.17 + github.com/uptrace/bun/dialect/mysqldialect v1.1.17 +) + +require ( + github.com/casbin/govaluate v1.1.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/sys v0.16.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..a54946c --- /dev/null +++ b/go.sum @@ -0,0 +1,38 @@ +github.com/casbin/casbin/v2 v2.85.0 h1:VajW9GR/T0fp3SND183gneZGIAdYtl9C7bDYBrqQiGg= +github.com/casbin/casbin/v2 v2.85.0/go.mod h1:jX8uoN4veP85O/n2674r2qtfSXI6myvxW85f6TH50fw= +github.com/casbin/govaluate v1.1.0 h1:6xdCWIpE9CwHdZhlVQW+froUrCsjb6/ZYNcXODfLT+E= +github.com/casbin/govaluate v1.1.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= +github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= +github.com/uptrace/bun v1.1.17 h1:qxBaEIo0hC/8O3O6GrMDKxqyT+mw5/s0Pn/n6xjyGIk= +github.com/uptrace/bun v1.1.17/go.mod h1:hATAzivtTIRsSJR4B8AXR+uABqnQxr3myKDKEf5iQ9U= +github.com/uptrace/bun/dialect/mysqldialect v1.1.17 h1:CsaZu+C3hW6jH5XnbQWPeZbHOoeURRpX9wd9wNy9fYU= +github.com/uptrace/bun/dialect/mysqldialect v1.1.17/go.mod h1:PDT12yHB0yLidZWFoPjhXfEKvsu7tLyjY67+OSMQsVw= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From cd0717d86509b9105ef14dab9031524289f8b189 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Mon, 25 Mar 2024 23:46:50 +0900 Subject: [PATCH 02/41] add CasbinPolicy struct (#5) --- policy.go | 113 +++++++++++++++ policy_test.go | 384 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 497 insertions(+) create mode 100644 policy.go create mode 100644 policy_test.go diff --git a/policy.go b/policy.go new file mode 100644 index 0000000..af785cf --- /dev/null +++ b/policy.go @@ -0,0 +1,113 @@ +package casbinbunadapter + +// Database storage format following the below +// https://casbin.org/docs/policy-storage#database-storage-format +type CasbinPolicy struct { + ID int64 `bun:"id,pk,autoincrement"` + PType string `bun:"ptype,type:varchar(255),notnull"` + V0 string `bun:"v0,type:varchar(255)"` + V1 string `bun:"v1,type:varchar(255)"` + V2 string `bun:"v2,type:varchar(255)"` + V3 string `bun:"v3,type:varchar(255)"` + V4 string `bun:"v4,type:varchar(255)"` + V5 string `bun:"v5,type:varchar(255)"` +} + +func (c CasbinPolicy) toSlice() []string { + policies := make([]string, 0) + if c.PType != "" { + policies = append(policies, c.PType) + } + if c.V0 != "" { + policies = append(policies, c.V0) + } + if c.V1 != "" { + policies = append(policies, c.V1) + } + if c.V2 != "" { + policies = append(policies, c.V2) + } + if c.V3 != "" { + policies = append(policies, c.V3) + } + if c.V4 != "" { + policies = append(policies, c.V4) + } + if c.V5 != "" { + policies = append(policies, c.V5) + } + return policies +} + +func (c CasbinPolicy) filterValues() []string { + values := make([]string, 0) + if c.V0 != "" { + values = append(values, c.V0) + } + if c.V1 != "" { + values = append(values, c.V1) + } + if c.V2 != "" { + values = append(values, c.V2) + } + if c.V3 != "" { + values = append(values, c.V3) + } + if c.V4 != "" { + values = append(values, c.V4) + } + if c.V5 != "" { + values = append(values, c.V5) + } + + return values +} + +func (c CasbinPolicy) filterValuesWithKey() map[string]string { + values := make(map[string]string) + if c.V0 != "" { + values["v0"] = c.V0 + } + if c.V1 != "" { + values["v1"] = c.V1 + } + if c.V2 != "" { + values["v2"] = c.V2 + } + if c.V3 != "" { + values["v3"] = c.V3 + } + if c.V4 != "" { + values["v4"] = c.V4 + } + if c.V5 != "" { + values["v5"] = c.V5 + } + + return values +} + +func newCasbinPolicy(ptype string, rule []string) CasbinPolicy { + c := CasbinPolicy{ + PType: ptype, + } + + for i, v := range rule { + switch i { + case 0: + c.V0 = v + case 1: + c.V1 = v + case 2: + c.V2 = v + case 3: + c.V3 = v + case 4: + c.V4 = v + case 5: + c.V5 = v + } + } + + return c +} diff --git a/policy_test.go b/policy_test.go new file mode 100644 index 0000000..1d1bdda --- /dev/null +++ b/policy_test.go @@ -0,0 +1,384 @@ +package casbinbunadapter + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func Test_newCasbinPolicy(t *testing.T) { + type args struct { + ptype string + rule []string + } + tests := []struct { + name string + args args + want CasbinPolicy + }{ + { + name: "success when ptype is p and one rules is provided", + args: args{ + ptype: "p", + rule: []string{"alice"}, + }, + want: CasbinPolicy{ + PType: "p", + V0: "alice", + }, + }, + { + name: "success when ptype is p and two rules are provided", + args: args{ + ptype: "p", + rule: []string{"alice", "data1"}, + }, + want: CasbinPolicy{ + PType: "p", + V0: "alice", + V1: "data1", + }, + }, + { + name: "success when ptype is p and three rules are provided", + args: args{ + ptype: "p", + rule: []string{"alice", "data1", "read"}, + }, + want: CasbinPolicy{ + PType: "p", + V0: "alice", + V1: "data1", + V2: "read", + }, + }, + { + name: "success when ptype is p and four rules are provided", + args: args{ + ptype: "p", + rule: []string{"alice", "data1", "read", "allow"}, + }, + want: CasbinPolicy{ + PType: "p", + V0: "alice", + V1: "data1", + V2: "read", + V3: "allow", + }, + }, + { + name: "success when ptype is p and five rules are provided", + args: args{ + ptype: "p", + rule: []string{"alice", "data1", "read", "allow", "1"}, + }, + want: CasbinPolicy{ + PType: "p", + V0: "alice", + V1: "data1", + V2: "read", + V3: "allow", + V4: "1", + }, + }, + { + name: "success when ptype is p and six rules are provided", + args: args{ + ptype: "p", + rule: []string{"alice", "data1", "read", "allow", "1", "2"}, + }, + want: CasbinPolicy{ + PType: "p", + V0: "alice", + V1: "data1", + V2: "read", + V3: "allow", + V4: "1", + V5: "2", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := newCasbinPolicy(tt.args.ptype, tt.args.rule) + if diff := cmp.Diff(tt.want, got); diff != "" { + t.Errorf("newCasbinPolicy() mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func TestCasbinPolicy_toSlice(t *testing.T) { + type fields struct { + ptype string + v0 string + v1 string + v2 string + v3 string + v4 string + v5 string + } + tests := []struct { + name string + fields fields + want []string + }{ + { + name: "success when ptype is p and one rules is provided", + fields: fields{ + ptype: "p", + v0: "alice", + }, + want: []string{"p", "alice"}, + }, + { + name: "success when ptype is p and two rules are provided", + fields: fields{ + ptype: "p", + v0: "alice", + v1: "data1", + }, + want: []string{"p", "alice", "data1"}, + }, + { + name: "success when ptype is p and three rules are provided", + fields: fields{ + ptype: "p", + v0: "alice", + v1: "data1", + v2: "read", + }, + want: []string{"p", "alice", "data1", "read"}, + }, + { + name: "success when ptype is p and four rules are provided", + fields: fields{ + ptype: "p", + v0: "alice", + v1: "data1", + v2: "read", + v3: "allow", + }, + want: []string{"p", "alice", "data1", "read", "allow"}, + }, + { + name: "success when ptype is p and five rules are provided", + fields: fields{ + ptype: "p", + v0: "alice", + v1: "data1", + v2: "read", + v3: "allow", + v4: "1", + }, + want: []string{"p", "alice", "data1", "read", "allow", "1"}, + }, + { + name: "success when ptype is p and six rules are provided", + fields: fields{ + ptype: "p", + v0: "alice", + v1: "data1", + v2: "read", + v3: "allow", + v4: "1", + v5: "2", + }, + want: []string{"p", "alice", "data1", "read", "allow", "1", "2"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + policy := CasbinPolicy{ + PType: tt.fields.ptype, + V0: tt.fields.v0, + V1: tt.fields.v1, + V2: tt.fields.v2, + V3: tt.fields.v3, + V4: tt.fields.v4, + V5: tt.fields.v5, + } + if diff := cmp.Diff(tt.want, policy.toSlice()); diff != "" { + t.Errorf("toSlice() mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func TestCasbinPolicy_filterValues(t *testing.T) { + type fields struct { + v0 string + v1 string + v2 string + v3 string + v4 string + v5 string + } + tests := []struct { + name string + fields fields + want []string + }{ + { + name: "success when one rules is provided", + fields: fields{ + v0: "alice", + }, + want: []string{"alice"}, + }, + { + name: "success when two rules are provided", + fields: fields{ + v0: "alice", + v1: "data1", + }, + want: []string{"alice", "data1"}, + }, + { + name: "success when three rules are provided", + fields: fields{ + v0: "alice", + v1: "data1", + v2: "read", + }, + want: []string{"alice", "data1", "read"}, + }, + { + name: "success when four rules are provided", + fields: fields{ + v0: "alice", + v1: "data1", + v2: "read", + v3: "allow", + }, + want: []string{"alice", "data1", "read", "allow"}, + }, + { + name: "success when five rules are provided", + fields: fields{ + v0: "alice", + v1: "data1", + v2: "read", + v3: "allow", + v4: "1", + }, + want: []string{"alice", "data1", "read", "allow", "1"}, + }, + { + name: "success when six rules are provided", + fields: fields{ + v0: "alice", + v1: "data1", + v2: "read", + v3: "allow", + v4: "1", + v5: "2", + }, + want: []string{"alice", "data1", "read", "allow", "1", "2"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + policy := CasbinPolicy{ + V0: tt.fields.v0, + V1: tt.fields.v1, + V2: tt.fields.v2, + V3: tt.fields.v3, + V4: tt.fields.v4, + V5: tt.fields.v5, + } + if diff := cmp.Diff(tt.want, policy.filterValues()); diff != "" { + t.Errorf("filterValues() mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func TestCasbinPolicy_filterValuesWithKey(t *testing.T) { + type fields struct { + v0 string + v1 string + v2 string + v3 string + v4 string + v5 string + } + tests := []struct { + name string + fields fields + want map[string]string + }{ + { + name: "success when one rules is provided", + fields: fields{ + v0: "alice", + }, + want: map[string]string{"v0": "alice"}, + }, + { + name: "success when two rules are provided", + fields: fields{ + v0: "alice", + v1: "data1", + }, + want: map[string]string{"v0": "alice", "v1": "data1"}, + }, + { + name: "success when three rules are provided", + fields: fields{ + v0: "alice", + v1: "data1", + v2: "read", + }, + want: map[string]string{"v0": "alice", "v1": "data1", "v2": "read"}, + }, + { + name: "success when four rules are provided", + fields: fields{ + v0: "alice", + v1: "data1", + v2: "read", + v3: "allow", + }, + want: map[string]string{"v0": "alice", "v1": "data1", "v2": "read", "v3": "allow"}, + }, + { + name: "success when five rules are provided", + fields: fields{ + v0: "alice", + v1: "data1", + v2: "read", + v3: "allow", + v4: "1", + }, + want: map[string]string{"v0": "alice", "v1": "data1", "v2": "read", "v3": "allow", "v4": "1"}, + }, + { + name: "success when six rules are provided", + fields: fields{ + v0: "alice", + v1: "data1", + v2: "read", + v3: "allow", + v4: "1", + v5: "2", + }, + want: map[string]string{"v0": "alice", "v1": "data1", "v2": "read", "v3": "allow", "v4": "1", "v5": "2"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + policy := CasbinPolicy{ + V0: tt.fields.v0, + V1: tt.fields.v1, + V2: tt.fields.v2, + V3: tt.fields.v3, + V4: tt.fields.v4, + V5: tt.fields.v5, + } + if diff := cmp.Diff(tt.want, policy.filterValuesWithKey()); diff != "" { + t.Errorf("filterValuesWithKey() mismatch (-want +got):\n%s", diff) + } + }) + } +} From 0857be0fa19722006879533d25278136635dbb0d Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Tue, 26 Mar 2024 00:16:25 +0900 Subject: [PATCH 03/41] add services to test workflow of GitHub Actions (#5) --- .github/workflows/test.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5f6971b..dd1fafc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,6 +14,32 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} + services: + mysql: + image: mysql + env: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: test + ports: + - 3306:3306 + postgres: + image: postgres + env: + POSTGRES_PASSWORD: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + mssql: + image: mcr.microsoft.com/mssql/server:2022-latest + env: + MSSQL_SA_PASSWORD: "Password123" + ACCEPT_EULA: "Y" + ports: + - 1433:1433 steps: - name: checkout uses: actions/checkout@v2 From fc8185a2d0588612bee300504d26bf5350e2f534 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Tue, 26 Mar 2024 00:19:28 +0900 Subject: [PATCH 04/41] change to run CI test on Ubuntu (#5) --- .github/workflows/test.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dd1fafc..6e38fa2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,12 +8,7 @@ on: jobs: test: name: test - strategy: - fail-fast: false - max-parallel: 3 - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest services: mysql: image: mysql From eac2a823f85957e4e2e5a28b022d5978c87617e9 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Tue, 26 Mar 2024 23:56:55 +0900 Subject: [PATCH 05/41] add testdata (#5) --- testdata/rbac_model.conf | 14 ++++++++++++++ testdata/rbac_policy.csv | 5 +++++ 2 files changed, 19 insertions(+) create mode 100644 testdata/rbac_model.conf create mode 100644 testdata/rbac_policy.csv diff --git a/testdata/rbac_model.conf b/testdata/rbac_model.conf new file mode 100644 index 0000000..71159e3 --- /dev/null +++ b/testdata/rbac_model.conf @@ -0,0 +1,14 @@ +[request_definition] +r = sub, 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.obj == p.obj && r.act == p.act \ No newline at end of file diff --git a/testdata/rbac_policy.csv b/testdata/rbac_policy.csv new file mode 100644 index 0000000..f93d6df --- /dev/null +++ b/testdata/rbac_policy.csv @@ -0,0 +1,5 @@ +p, alice, data1, read +p, bob, data2, write +p, data2_admin, data2, read +p, data2_admin, data2, write +g, alice, data2_admin \ No newline at end of file From bf3d64a11976b73c6ad191a3f56ac3a51c253144 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Wed, 27 Mar 2024 01:06:45 +0900 Subject: [PATCH 06/41] add db related packages (#5) --- go.mod | 29 ++++++++++++++++++ go.sum | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/go.mod b/go.mod index 79f7da7..80a36b3 100644 --- a/go.mod +++ b/go.mod @@ -4,17 +4,46 @@ go 1.22.0 require ( github.com/casbin/casbin/v2 v2.85.0 + github.com/denisenkom/go-mssqldb v0.12.3 + github.com/go-sql-driver/mysql v1.8.1 github.com/google/go-cmp v0.6.0 github.com/uptrace/bun v1.1.17 + github.com/uptrace/bun/dialect/mssqldialect v1.1.17 github.com/uptrace/bun/dialect/mysqldialect v1.1.17 + github.com/uptrace/bun/dialect/pgdialect v1.1.17 + github.com/uptrace/bun/dialect/sqlitedialect v1.1.17 + github.com/uptrace/bun/driver/pgdriver v1.1.17 + github.com/uptrace/bun/driver/sqliteshim v1.1.17 ) require ( + filippo.io/edwards25519 v1.1.0 // indirect github.com/casbin/govaluate v1.1.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect + github.com/golang-sql/sqlexp v0.1.0 // indirect + github.com/google/uuid v1.5.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-sqlite3 v1.14.19 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + golang.org/x/crypto v0.18.0 // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/sys v0.16.0 // indirect + golang.org/x/tools v0.16.1 // indirect + lukechampine.com/uint128 v1.3.0 // indirect + mellium.im/sasl v0.3.1 // indirect + modernc.org/cc/v3 v3.41.0 // indirect + modernc.org/ccgo/v3 v3.16.15 // indirect + modernc.org/libc v1.40.1 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/opt v0.1.3 // indirect + modernc.org/sqlite v1.28.0 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect ) diff --git a/go.sum b/go.sum index a54946c..55c667d 100644 --- a/go.sum +++ b/go.sum @@ -1,38 +1,135 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= github.com/casbin/casbin/v2 v2.85.0 h1:VajW9GR/T0fp3SND183gneZGIAdYtl9C7bDYBrqQiGg= github.com/casbin/casbin/v2 v2.85.0/go.mod h1:jX8uoN4veP85O/n2674r2qtfSXI6myvxW85f6TH50fw= github.com/casbin/govaluate v1.1.0 h1:6xdCWIpE9CwHdZhlVQW+froUrCsjb6/ZYNcXODfLT+E= github.com/casbin/govaluate v1.1.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw= +github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= +github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= github.com/uptrace/bun v1.1.17 h1:qxBaEIo0hC/8O3O6GrMDKxqyT+mw5/s0Pn/n6xjyGIk= github.com/uptrace/bun v1.1.17/go.mod h1:hATAzivtTIRsSJR4B8AXR+uABqnQxr3myKDKEf5iQ9U= +github.com/uptrace/bun/dialect/mssqldialect v1.1.17 h1:Dp9SDA/IPiTdqaVvyXZLEuurnkZnuMuecXNtT2pv768= +github.com/uptrace/bun/dialect/mssqldialect v1.1.17/go.mod h1:PNpWl2ymuby1ZyeMxryfvqDfX7gMWAxsSuF27VGAIzY= github.com/uptrace/bun/dialect/mysqldialect v1.1.17 h1:CsaZu+C3hW6jH5XnbQWPeZbHOoeURRpX9wd9wNy9fYU= github.com/uptrace/bun/dialect/mysqldialect v1.1.17/go.mod h1:PDT12yHB0yLidZWFoPjhXfEKvsu7tLyjY67+OSMQsVw= +github.com/uptrace/bun/dialect/pgdialect v1.1.17 h1:NsvFVHAx1Az6ytlAD/B6ty3cVE6j9Yp82bjqd9R9hOs= +github.com/uptrace/bun/dialect/pgdialect v1.1.17/go.mod h1:fLBDclNc7nKsZLzNjFL6BqSdgJzbj2HdnyOnLoDvAME= +github.com/uptrace/bun/dialect/sqlitedialect v1.1.17 h1:i8NFU9r8YuavNFaYlNqi4ppn+MgoHtqLgpWQDrVTjm0= +github.com/uptrace/bun/dialect/sqlitedialect v1.1.17/go.mod h1:YF0FO4VVnY9GHNH6rM4r3STlVEBxkOc6L88Bm5X5mzA= +github.com/uptrace/bun/driver/pgdriver v1.1.17 h1:hLj6WlvSZk5x45frTQnJrYtyhvgI6CA4r7gYdJ0gpn8= +github.com/uptrace/bun/driver/pgdriver v1.1.17/go.mod h1:c9fa6FiiQjOe9mCaJC9NmFUE6vCGKTEsqrtLjPNz+kk= +github.com/uptrace/bun/driver/sqliteshim v1.1.17 h1:Iye/NdURWx7JfzbMk+k5bhzWUkvTNLsdANb4aVCgQoU= +github.com/uptrace/bun/driver/sqliteshim v1.1.17/go.mod h1:ksjltqVfcPYYKYFbvgI+unY2H/IweDDLi6NCywq/ff0= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= +golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= +lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo= +mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= +modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= +modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= +modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0= +modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI= +modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v1.40.1 h1:ZhRylEBcj3GyQbPVC8JxIg7SdrT4JOxIDJoUon0NfF8= +modernc.org/libc v1.40.1/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= +modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= +modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= +modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= From 0fdb981fd3ebcf9024d2fe59732c6ff40652c0fa Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Thu, 28 Mar 2024 23:14:08 +0900 Subject: [PATCH 07/41] implement adapter (#5) --- adapter.go | 509 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 509 insertions(+) diff --git a/adapter.go b/adapter.go index 90a2298..c8f080d 100644 --- a/adapter.go +++ b/adapter.go @@ -1 +1,510 @@ package casbinbunadapter + +import ( + "context" + "database/sql" + "fmt" + + "github.com/casbin/casbin/v2/model" + "github.com/casbin/casbin/v2/persist" + _ "github.com/denisenkom/go-mssqldb" + _ "github.com/go-sql-driver/mysql" + "github.com/uptrace/bun" + "github.com/uptrace/bun/dialect/mssqldialect" + "github.com/uptrace/bun/dialect/mysqldialect" + "github.com/uptrace/bun/dialect/pgdialect" + "github.com/uptrace/bun/dialect/sqlitedialect" + "github.com/uptrace/bun/driver/pgdriver" + "github.com/uptrace/bun/driver/sqliteshim" +) + +const ( + defaultTableName = "casbin_policy" +) + +var ( + // check if the bunAdapter implements the Adapter interface + _ persist.Adapter = (*bunAdapter)(nil) + // check if the bunAdapter implements the BatchAdapter interface + _ persist.BatchAdapter = (*bunAdapter)(nil) + // check if the bunAdapter implements the UpdatableAdapter interface + _ persist.UpdatableAdapter = (*bunAdapter)(nil) +) + +type bunAdapter struct { + db *bun.DB + tableName string +} + +type adapterOption func(*bunAdapter) + +func WithTableName(tableName string) adapterOption { + return func(a *bunAdapter) { + a.tableName = tableName + } +} + +func NewAdapter(driverName, dataSourceName string, opts ...adapterOption) (*bunAdapter, error) { + b, err := newAdapter(driverName, dataSourceName) + if err != nil { + return nil, err + } + + for _, opt := range opts { + opt(b) + } + + return b, nil +} + +func newAdapter(driverName, dataSourceName string) (*bunAdapter, error) { + db, err := connectDB(driverName, dataSourceName) + if err != nil { + return nil, err + } + + return &bunAdapter{ + db: db, + tableName: defaultTableName, + }, nil +} + +func connectDB(driverName, dataSourceName string) (*bun.DB, error) { + switch driverName { + case "mysql": + sqlDB, err := sql.Open(driverName, dataSourceName) + if err != nil { + return nil, err + } + return bun.NewDB(sqlDB, mysqldialect.New()), nil + case "postgres": + sqlDB := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dataSourceName))) + return bun.NewDB(sqlDB, pgdialect.New()), nil + case "mssql": + sqlDB, err := sql.Open(driverName, dataSourceName) + if err != nil { + return nil, err + } + return bun.NewDB(sqlDB, mssqldialect.New()), nil + case "sqlite3": + sqlDB, err := sql.Open(sqliteshim.ShimName, dataSourceName) + if err != nil { + return nil, err + } + return bun.NewDB(sqlDB, sqlitedialect.New()), nil + default: + return nil, fmt.Errorf("unsupported driver: %s", driverName) + } +} + +// LoadPolicy loads all policy rules from the storage. +func (a *bunAdapter) LoadPolicy(model model.Model) error { + var policies []CasbinPolicy + err := a.db.NewSelect(). + Model(&policies). + Table(a.tableName). + Scan(context.Background()) + if err != nil { + return err + } + + for _, policy := range policies { + if err := loadPolicyRecord(policy, model); err != nil { + return err + } + } + + return nil +} + +func loadPolicyRecord(policy CasbinPolicy, model model.Model) error { + pType := policy.PType + sec := pType[:1] + ok, err := model.HasPolicyEx(sec, pType, policy.filterValues()) + if err != nil { + return err + } + if ok { + return nil + } + model.AddPolicy(sec, pType, policy.filterValues()) + return nil +} + +// SavePolicy saves all policy rules to the storage. +func (a *bunAdapter) SavePolicy(model model.Model) error { + policies := make([]CasbinPolicy, 0) + + // go through policy definitions + for ptype, ast := range model["p"] { + for _, rule := range ast.Policy { + policies = append(policies, newCasbinPolicy(ptype, rule)) + } + } + + // go through role definitions + for ptype, ast := range model["g"] { + for _, rule := range ast.Policy { + policies = append(policies, newCasbinPolicy(ptype, rule)) + } + } + + return a.savePolicyRecords(policies) +} + +func (a *bunAdapter) savePolicyRecords(policies []CasbinPolicy) error { + // delete existing policies + if err := a.refreshTable(); err != nil { + return err + } + + // bulk insert new policies + if _, err := a.db.NewInsert().Model(&policies).Exec(context.Background()); err != nil { + return err + } + + return nil +} + +// delete all policy rules from the storage. +func (a *bunAdapter) refreshTable() error { + if _, err := a.db.NewTruncateTable(). + Model((*CasbinPolicy)(nil)). + Table(a.tableName). + Exec(context.Background()); err != nil { + return err + } + return nil +} + +// AddPolicy adds a policy rule to the storage. +// This is part of the Auto-Save feature. +func (a *bunAdapter) AddPolicy(sec string, ptype string, rule []string) error { + newPolicy := newCasbinPolicy(ptype, rule) + if _, err := a.db.NewInsert(). + Model(&newPolicy). + Table(a.tableName). + Exec(context.Background()); err != nil { + return err + } + return nil +} + +// AddPolicies adds policy rules to the storage. +// This is part of the Auto-Save feature. +func (a *bunAdapter) AddPolicies(sec string, ptype string, rules [][]string) error { + policies := make([]CasbinPolicy, 0) + for _, rule := range rules { + policies = append(policies, newCasbinPolicy(ptype, rule)) + } + if _, err := a.db.NewInsert(). + Model(&policies). + Table(a.tableName). + Exec(context.Background()); err != nil { + return err + } + return nil +} + +// RemovePolicy removes a policy rule from the storage. +// This is part of the Auto-Save feature. +func (a *bunAdapter) RemovePolicy(sec string, ptype string, rule []string) error { + exisingPolicy := newCasbinPolicy(ptype, rule) + if err := a.deleteRecord(exisingPolicy); err != nil { + return err + } + return nil +} + +// RemovePolicies removes policy rules from the storage. +// This is part of the Auto-Save feature. +func (a *bunAdapter) RemovePolicies(sec string, ptype string, rules [][]string) error { + return a.db.RunInTx(context.Background(), &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error { + for _, rule := range rules { + exisingPolicy := newCasbinPolicy(ptype, rule) + if err := a.deleteRecordInTx(tx, exisingPolicy); err != nil { + return err + } + } + return nil + }) +} + +func (a *bunAdapter) deleteRecord(existingPolicy CasbinPolicy) error { + query := a.db.NewDelete(). + Table(a.tableName). + Where("ptype = ?", existingPolicy.PType) + + values := existingPolicy.filterValuesWithKey() + + return a.delete(query, values) +} + +func (a *bunAdapter) deleteRecordInTx(tx bun.Tx, existingPolicy CasbinPolicy) error { + query := tx.NewDelete(). + Table(a.tableName). + Where("ptype = ?", existingPolicy.PType) + + values := existingPolicy.filterValuesWithKey() + + return a.delete(query, values) +} + +func (a *bunAdapter) delete(query *bun.DeleteQuery, values map[string]string) error { + for key, value := range values { + query = query.Where(fmt.Sprintf("%s = ?", key), value) + } + + if _, err := query.Exec(context.Background()); err != nil { + return err + } + + return nil +} + +// RemoveFilteredPolicy removes policy rules that match the filter from the storage. +// This is part of the Auto-Save feature. +// This API is explained in the link below: +// https://casbin.org/docs/management-api/#removefilteredpolicy +func (a *bunAdapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error { + if err := a.deleteFilteredPolicy(ptype, fieldIndex, fieldValues...); err != nil { + return err + } + return nil +} + +func (a *bunAdapter) deleteFilteredPolicy(ptype string, fieldIndex int, fieldValues ...string) error { + query := a.db.NewDelete(). + Table(a.tableName). + Where("ptype = ?", ptype) + + // Note that empty string in fieldValues could be any word. + if fieldIndex <= 0 && 0 < fieldIndex+len(fieldValues) { + value := fieldValues[0-fieldIndex] + if value == "" { + query = query.Where("v0 LIKE '%'") + } else { + query = query.Where("v0 = ?", value) + } + } + if fieldIndex <= 1 && 1 < fieldIndex+len(fieldValues) { + value := fieldValues[1-fieldIndex] + if value == "" { + query = query.Where("v1 LIKE '%'") + } else { + query = query.Where("v1 = ?", value) + } + } + if fieldIndex <= 2 && 2 < fieldIndex+len(fieldValues) { + value := fieldValues[2-fieldIndex] + if value == "" { + query = query.Where("v2 LIKE '%'") + } else { + query = query.Where("v2 = ?", value) + } + } + if fieldIndex <= 3 && 3 < fieldIndex+len(fieldValues) { + value := fieldValues[3-fieldIndex] + if value == "" { + query = query.Where("v3 LIKE '%'") + } else { + query = query.Where("v3 = ?", value) + } + } + if fieldIndex <= 4 && 4 < fieldIndex+len(fieldValues) { + value := fieldValues[4-fieldIndex] + if value == "" { + query = query.Where("v4 LIKE '%'") + } else { + query = query.Where("v4 = ?", value) + } + } + if fieldIndex <= 5 && 5 < fieldIndex+len(fieldValues) { + value := fieldValues[5-fieldIndex] + if value == "" { + query = query.Where("v5 LIKE '%'") + } else { + query = query.Where("v5 = ?", value) + } + } + + if _, err := query.Exec(context.Background()); err != nil { + return err + } + + return nil +} + +// UpdatePolicy updates a policy rule from storage. +// This is part of the Auto-Save feature. +func (a *bunAdapter) UpdatePolicy(sec string, ptype string, oldRule, newRule []string) error { + oldPolicy := newCasbinPolicy(ptype, oldRule) + newPolicy := newCasbinPolicy(ptype, newRule) + return a.updateRecord(oldPolicy, newPolicy) +} + +func (a *bunAdapter) updateRecord(oldPolicy, newPolicy CasbinPolicy) error { + query := a.db.NewUpdate(). + Model(&newPolicy). + Table(a.tableName). + Where("ptype = ?", oldPolicy.PType) + + values := oldPolicy.filterValuesWithKey() + + return a.update(query, values) +} + +func (a *bunAdapter) updateRecordInTx(tx bun.Tx, oldPolicy, newPolicy CasbinPolicy) error { + query := tx.NewUpdate(). + Model(&newPolicy). + Table(a.tableName). + Where("ptype = ?", oldPolicy.PType) + + values := oldPolicy.filterValuesWithKey() + + return a.update(query, values) +} + +func (a *bunAdapter) update(query *bun.UpdateQuery, values map[string]string) error { + for key, value := range values { + query = query.Where(fmt.Sprintf("%s = ?", key), value) + } + + if _, err := query.Exec(context.Background()); err != nil { + return err + } + + return nil +} + +// UpdatePolicies updates some policy rules to storage, like db, redis. +func (a *bunAdapter) UpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) error { + oldPolicies := make([]CasbinPolicy, 0, len(oldRules)) + newPolicies := make([]CasbinPolicy, 0, len(newRules)) + for _, rule := range oldRules { + oldPolicies = append(oldPolicies, newCasbinPolicy(ptype, rule)) + } + for _, rule := range newRules { + newPolicies = append(newPolicies, newCasbinPolicy(ptype, rule)) + } + + return a.db.RunInTx(context.Background(), &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error { + for i := range oldPolicies { + if err := a.updateRecordInTx(tx, oldPolicies[i], newPolicies[i]); err != nil { + return err + } + } + return nil + }) +} + +// UpdateFilteredPolicies deletes old rules and adds new rules. +func (a *bunAdapter) UpdateFilteredPolicies(sec string, ptype string, newRules [][]string, fieldIndex int, fieldValues ...string) ([][]string, error) { + newPolicies := make([]CasbinPolicy, 0, len(newRules)) + for _, rule := range newRules { + newPolicies = append(newPolicies, newCasbinPolicy(ptype, rule)) + } + + tx, err := a.db.BeginTx(context.Background(), &sql.TxOptions{}) + if err != nil { + return nil, err + } + + selectQuery := tx.NewSelect(). + Table(a.tableName). + Where("ptype = ?", ptype) + deleteQuery := tx.NewDelete(). + Table(a.tableName). + Where("ptype = ?", ptype) + + // Note that empty string in fieldValues could be any word. + if fieldIndex <= 0 && 0 < fieldIndex+len(fieldValues) { + value := fieldValues[0-fieldIndex] + if value == "" { + selectQuery = selectQuery.Where("v0 LIKE '%'") + deleteQuery = deleteQuery.Where("v0 LIKE '%'") + } else { + selectQuery = selectQuery.Where("v0 = ?", value) + deleteQuery = deleteQuery.Where("v0 = ?", value) + } + } + if fieldIndex <= 1 && 1 < fieldIndex+len(fieldValues) { + value := fieldValues[1-fieldIndex] + if value == "" { + selectQuery = selectQuery.Where("v1 LIKE '%'") + deleteQuery = deleteQuery.Where("v1 LIKE '%'") + } else { + selectQuery = selectQuery.Where("v1 = ?", value) + deleteQuery = deleteQuery.Where("v1 = ?", value) + } + } + if fieldIndex <= 2 && 2 < fieldIndex+len(fieldValues) { + value := fieldValues[2-fieldIndex] + if value == "" { + selectQuery = selectQuery.Where("v2 LIKE '%'") + deleteQuery = deleteQuery.Where("v2 LIKE '%'") + } else { + selectQuery = selectQuery.Where("v2 = ?", value) + deleteQuery = deleteQuery.Where("v2 = ?", value) + } + } + if fieldIndex <= 3 && 3 < fieldIndex+len(fieldValues) { + value := fieldValues[3-fieldIndex] + if value == "" { + selectQuery = selectQuery.Where("v3 LIKE '%'") + deleteQuery = deleteQuery.Where("v3 LIKE '%'") + } else { + selectQuery = selectQuery.Where("v3 = ?", value) + deleteQuery = deleteQuery.Where("v3 = ?", value) + } + } + if fieldIndex <= 4 && 4 < fieldIndex+len(fieldValues) { + value := fieldValues[4-fieldIndex] + if value == "" { + selectQuery = selectQuery.Where("v4 LIKE '%'") + deleteQuery = deleteQuery.Where("v4 LIKE '%'") + } else { + selectQuery = selectQuery.Where("v4 = ?", value) + deleteQuery = deleteQuery.Where("v4 = ?", value) + } + } + if fieldIndex <= 5 && 5 < fieldIndex+len(fieldValues) { + value := fieldValues[5-fieldIndex] + if value == "" { + selectQuery = selectQuery.Where("v5 LIKE '%'") + deleteQuery = deleteQuery.Where("v5 LIKE '%'") + } else { + selectQuery = selectQuery.Where("v5 = ?", value) + deleteQuery = deleteQuery.Where("v5 = ?", value) + } + } + + // store old policies + oldPolicies := make([]CasbinPolicy, 0) + if err := selectQuery.Scan(context.Background(), &oldPolicies); err != nil { + tx.Rollback() + return nil, err + } + + // delete old policies + if _, err := deleteQuery.Exec(context.Background()); err != nil { + tx.Rollback() + return nil, err + } + + // create new policies + if _, err := tx.NewInsert(). + Model(&newPolicies). + Table(a.tableName). + Exec(context.Background()); err != nil { + tx.Rollback() + return nil, err + } + + out := make([][]string, 0, len(oldPolicies)) + for _, policy := range oldPolicies { + out = append(out, policy.toSlice()) + } + + return out, tx.Commit() +} From bdf9011452fe92385e496e0cea138bece0356d43 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Thu, 28 Mar 2024 23:14:29 +0900 Subject: [PATCH 08/41] add unit test for AddPolicy method (#5) --- adapter_test.go | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 adapter_test.go diff --git a/adapter_test.go b/adapter_test.go new file mode 100644 index 0000000..16314c9 --- /dev/null +++ b/adapter_test.go @@ -0,0 +1,69 @@ +package casbinbunadapter + +import ( + "testing" + + "github.com/casbin/casbin/v2" + "github.com/casbin/casbin/v2/util" +) + +func testGetPolicy(t *testing.T, e *casbin.Enforcer, want [][]string) { + got := e.GetPolicy() + + if !util.Array2DEquals(want, got) { + t.Errorf("got %v, want %v", got, want) + } +} + +func initPolicy(t *testing.T, adapter *bunAdapter) { + e, err := casbin.NewEnforcer("testdata/rbac_model.conf", "testdata/rbac_policy.csv") + if err != nil { + panic(err) + } + + if err := adapter.SavePolicy(e.GetModel()); err != nil { + panic(err) + } + + e.ClearPolicy() + testGetPolicy(t, e, [][]string{}) + + if err := adapter.LoadPolicy(e.GetModel()); err != nil { + panic(err) + } + testGetPolicy( + t, + e, + [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}, + ) +} + +func initAdapter(t *testing.T, driverName, dataSourceName string, opts ...adapterOption) *bunAdapter { + a, err := NewAdapter(driverName, dataSourceName, opts...) + if err != nil { + panic(err) + } + + initPolicy(t, a) + + return a +} + +func TestBunAdapter_AddPolicy(t *testing.T) { + a := initAdapter(t, "mysql", "root:root@tcp(127.0.0.1:3306)/test") + e, err := casbin.NewEnforcer("testdata/rbac_model.conf", a) + if err != nil { + t.Fatalf("failed to create enforcer: %v", err) + } + if _, err := e.AddPolicy("jack", "data1", "read"); err != nil { + t.Fatalf("failed to add policy: %v", err) + } + if err := e.LoadPolicy(); err != nil { + t.Fatalf("failed to load policy: %v", err) + } + testGetPolicy( + t, + e, + [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}, {"jack", "data1", "read"}}, + ) +} From 3c2801a75be3560d22faee5cc390551bdae1ed03 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Thu, 28 Mar 2024 23:20:41 +0900 Subject: [PATCH 09/41] error handling for Rollback (#5) --- adapter.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/adapter.go b/adapter.go index c8f080d..019b655 100644 --- a/adapter.go +++ b/adapter.go @@ -482,13 +482,17 @@ func (a *bunAdapter) UpdateFilteredPolicies(sec string, ptype string, newRules [ // store old policies oldPolicies := make([]CasbinPolicy, 0) if err := selectQuery.Scan(context.Background(), &oldPolicies); err != nil { - tx.Rollback() + if err := tx.Rollback(); err != nil { + return nil, err + } return nil, err } // delete old policies if _, err := deleteQuery.Exec(context.Background()); err != nil { - tx.Rollback() + if err := tx.Rollback(); err != nil { + return nil, err + } return nil, err } @@ -497,7 +501,9 @@ func (a *bunAdapter) UpdateFilteredPolicies(sec string, ptype string, newRules [ Model(&newPolicies). Table(a.tableName). Exec(context.Background()); err != nil { - tx.Rollback() + if err := tx.Rollback(); err != nil { + return nil, err + } return nil, err } From b0dac1999a95dc61740c84b4eb241e6715826379 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Thu, 28 Mar 2024 23:52:52 +0900 Subject: [PATCH 10/41] create table when to init adapter (#5) --- adapter.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/adapter.go b/adapter.go index 019b655..1be066e 100644 --- a/adapter.go +++ b/adapter.go @@ -54,6 +54,10 @@ func NewAdapter(driverName, dataSourceName string, opts ...adapterOption) (*bunA opt(b) } + if err := b.createTalbe(); err != nil { + return nil, err + } + return b, nil } @@ -97,6 +101,29 @@ func connectDB(driverName, dataSourceName string) (*bun.DB, error) { } } +func (a *bunAdapter) createTalbe() error { + if _, err := a.db.NewCreateTable(). + Model((*CasbinPolicy)(nil)). + Table(a.tableName). + IfNotExists(). + Exec(context.Background()); err != nil { + return err + } + return nil +} + +var _ bun.AfterCreateTableHook = (*CasbinPolicy)(nil) + +func (*CasbinPolicy) AfterCreateTable(ctx context.Context, query *bun.CreateTableQuery) error { + _, err := query.DB().NewCreateIndex(). + Model((*CasbinPolicy)(nil)). + Unique(). + Index("idx_ptype_v0_v1_v2_v3_v4_v5"). + Column("ptype", "v0", "v1", "v2", "v3", "v4", "v5"). + Exec(ctx) + return err +} + // LoadPolicy loads all policy rules from the storage. func (a *bunAdapter) LoadPolicy(model model.Model) error { var policies []CasbinPolicy From 692758af10bd000212c7dd502b1c496ba434c52e Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Fri, 29 Mar 2024 00:03:41 +0900 Subject: [PATCH 11/41] change the size of varchar in CasbinPolicy table (#5) --- policy.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/policy.go b/policy.go index af785cf..d61587d 100644 --- a/policy.go +++ b/policy.go @@ -4,13 +4,13 @@ package casbinbunadapter // https://casbin.org/docs/policy-storage#database-storage-format type CasbinPolicy struct { ID int64 `bun:"id,pk,autoincrement"` - PType string `bun:"ptype,type:varchar(255),notnull"` - V0 string `bun:"v0,type:varchar(255)"` - V1 string `bun:"v1,type:varchar(255)"` - V2 string `bun:"v2,type:varchar(255)"` - V3 string `bun:"v3,type:varchar(255)"` - V4 string `bun:"v4,type:varchar(255)"` - V5 string `bun:"v5,type:varchar(255)"` + PType string `bun:"ptype,type:varchar(100),notnull"` + V0 string `bun:"v0,type:varchar(100)"` + V1 string `bun:"v1,type:varchar(100)"` + V2 string `bun:"v2,type:varchar(100)"` + V3 string `bun:"v3,type:varchar(100)"` + V4 string `bun:"v4,type:varchar(100)"` + V5 string `bun:"v5,type:varchar(100)"` } func (c CasbinPolicy) toSlice() []string { From b1cdd3c236adb12b76d9b0f9614b4e52ed9fa16d Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Fri, 29 Mar 2024 00:15:46 +0900 Subject: [PATCH 12/41] specifiy table name when to insert (#5) --- adapter.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/adapter.go b/adapter.go index 1be066e..72f8f24 100644 --- a/adapter.go +++ b/adapter.go @@ -186,7 +186,10 @@ func (a *bunAdapter) savePolicyRecords(policies []CasbinPolicy) error { } // bulk insert new policies - if _, err := a.db.NewInsert().Model(&policies).Exec(context.Background()); err != nil { + if _, err := a.db.NewInsert(). + Model(&policies). + Table(a.tableName). + Exec(context.Background()); err != nil { return err } From ce6d7c7dfade2c059cd6db2acab2ec5651205d39 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Fri, 29 Mar 2024 01:56:12 +0900 Subject: [PATCH 13/41] add debugging mode (#5) --- adapter.go | 12 ++++++++++++ adapter_test.go | 2 +- go.mod | 3 +++ go.sum | 8 ++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/adapter.go b/adapter.go index 72f8f24..cd62179 100644 --- a/adapter.go +++ b/adapter.go @@ -16,6 +16,7 @@ import ( "github.com/uptrace/bun/dialect/sqlitedialect" "github.com/uptrace/bun/driver/pgdriver" "github.com/uptrace/bun/driver/sqliteshim" + "github.com/uptrace/bun/extra/bundebug" ) const ( @@ -34,6 +35,7 @@ var ( type bunAdapter struct { db *bun.DB tableName string + debugMode bool } type adapterOption func(*bunAdapter) @@ -44,6 +46,12 @@ func WithTableName(tableName string) adapterOption { } } +func WithDebugMode() adapterOption { + return func(a *bunAdapter) { + a.debugMode = true + } +} + func NewAdapter(driverName, dataSourceName string, opts ...adapterOption) (*bunAdapter, error) { b, err := newAdapter(driverName, dataSourceName) if err != nil { @@ -54,6 +62,10 @@ func NewAdapter(driverName, dataSourceName string, opts ...adapterOption) (*bunA opt(b) } + if b.debugMode { + b.db.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true))) + } + if err := b.createTalbe(); err != nil { return nil, err } diff --git a/adapter_test.go b/adapter_test.go index 16314c9..a1081d3 100644 --- a/adapter_test.go +++ b/adapter_test.go @@ -50,7 +50,7 @@ func initAdapter(t *testing.T, driverName, dataSourceName string, opts ...adapte } func TestBunAdapter_AddPolicy(t *testing.T) { - a := initAdapter(t, "mysql", "root:root@tcp(127.0.0.1:3306)/test") + a := initAdapter(t, "mysql", "root:root@tcp(127.0.0.1:3306)/test", WithDebugMode()) e, err := casbin.NewEnforcer("testdata/rbac_model.conf", a) if err != nil { t.Fatalf("failed to create enforcer: %v", err) diff --git a/go.mod b/go.mod index 80a36b3..aeb94b5 100644 --- a/go.mod +++ b/go.mod @@ -20,15 +20,18 @@ require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/casbin/govaluate v1.1.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/fatih/color v1.16.0 // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/google/uuid v1.5.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.19 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect + github.com/uptrace/bun/extra/bundebug v1.1.17 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect golang.org/x/crypto v0.18.0 // indirect diff --git a/go.sum b/go.sum index 55c667d..eb34959 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDror github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= @@ -33,6 +35,9 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= @@ -63,6 +68,8 @@ github.com/uptrace/bun/driver/pgdriver v1.1.17 h1:hLj6WlvSZk5x45frTQnJrYtyhvgI6C github.com/uptrace/bun/driver/pgdriver v1.1.17/go.mod h1:c9fa6FiiQjOe9mCaJC9NmFUE6vCGKTEsqrtLjPNz+kk= github.com/uptrace/bun/driver/sqliteshim v1.1.17 h1:Iye/NdURWx7JfzbMk+k5bhzWUkvTNLsdANb4aVCgQoU= github.com/uptrace/bun/driver/sqliteshim v1.1.17/go.mod h1:ksjltqVfcPYYKYFbvgI+unY2H/IweDDLi6NCywq/ff0= +github.com/uptrace/bun/extra/bundebug v1.1.17 h1:LcZ8DzyyGdXAmbUqmnCpBq7TPFegMp59FGy+uzEE21c= +github.com/uptrace/bun/extra/bundebug v1.1.17/go.mod h1:FOwNaBEGGChv3qBVh3pz3TPlUuikZ93qKjd/LJdl91o= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= @@ -86,6 +93,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= From bc9f8b5003f08d37a78e16bcead0ac056ec75ea3 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 10:26:50 +0900 Subject: [PATCH 14/41] override table name (#5) --- adapter.go | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/adapter.go b/adapter.go index cd62179..3f2513e 100644 --- a/adapter.go +++ b/adapter.go @@ -116,7 +116,7 @@ func connectDB(driverName, dataSourceName string) (*bun.DB, error) { func (a *bunAdapter) createTalbe() error { if _, err := a.db.NewCreateTable(). Model((*CasbinPolicy)(nil)). - Table(a.tableName). + ModelTableExpr(a.tableName). IfNotExists(). Exec(context.Background()); err != nil { return err @@ -141,7 +141,7 @@ func (a *bunAdapter) LoadPolicy(model model.Model) error { var policies []CasbinPolicy err := a.db.NewSelect(). Model(&policies). - Table(a.tableName). + ModelTableExpr(a.tableName). Scan(context.Background()) if err != nil { return err @@ -200,7 +200,7 @@ func (a *bunAdapter) savePolicyRecords(policies []CasbinPolicy) error { // bulk insert new policies if _, err := a.db.NewInsert(). Model(&policies). - Table(a.tableName). + ModelTableExpr(a.tableName). Exec(context.Background()); err != nil { return err } @@ -208,15 +208,18 @@ func (a *bunAdapter) savePolicyRecords(policies []CasbinPolicy) error { return nil } -// delete all policy rules from the storage. +// drop and recreate the table func (a *bunAdapter) refreshTable() error { - if _, err := a.db.NewTruncateTable(). - Model((*CasbinPolicy)(nil)). - Table(a.tableName). + // just truncate the table could be a better choice + // but NewTruncateTable() does not support ModelTableExpr + // so we drop and recreate the table instead + if _, err := a.db.NewDropTable(). + ModelTableExpr(a.tableName). + IfExists(). Exec(context.Background()); err != nil { return err } - return nil + return a.createTalbe() } // AddPolicy adds a policy rule to the storage. @@ -225,7 +228,7 @@ func (a *bunAdapter) AddPolicy(sec string, ptype string, rule []string) error { newPolicy := newCasbinPolicy(ptype, rule) if _, err := a.db.NewInsert(). Model(&newPolicy). - Table(a.tableName). + ModelTableExpr(a.tableName). Exec(context.Background()); err != nil { return err } @@ -241,7 +244,7 @@ func (a *bunAdapter) AddPolicies(sec string, ptype string, rules [][]string) err } if _, err := a.db.NewInsert(). Model(&policies). - Table(a.tableName). + ModelTableExpr(a.tableName). Exec(context.Background()); err != nil { return err } @@ -274,7 +277,7 @@ func (a *bunAdapter) RemovePolicies(sec string, ptype string, rules [][]string) func (a *bunAdapter) deleteRecord(existingPolicy CasbinPolicy) error { query := a.db.NewDelete(). - Table(a.tableName). + ModelTableExpr(a.tableName). Where("ptype = ?", existingPolicy.PType) values := existingPolicy.filterValuesWithKey() @@ -284,7 +287,7 @@ func (a *bunAdapter) deleteRecord(existingPolicy CasbinPolicy) error { func (a *bunAdapter) deleteRecordInTx(tx bun.Tx, existingPolicy CasbinPolicy) error { query := tx.NewDelete(). - Table(a.tableName). + ModelTableExpr(a.tableName). Where("ptype = ?", existingPolicy.PType) values := existingPolicy.filterValuesWithKey() @@ -317,7 +320,7 @@ func (a *bunAdapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex i func (a *bunAdapter) deleteFilteredPolicy(ptype string, fieldIndex int, fieldValues ...string) error { query := a.db.NewDelete(). - Table(a.tableName). + ModelTableExpr(a.tableName). Where("ptype = ?", ptype) // Note that empty string in fieldValues could be any word. @@ -388,7 +391,7 @@ func (a *bunAdapter) UpdatePolicy(sec string, ptype string, oldRule, newRule []s func (a *bunAdapter) updateRecord(oldPolicy, newPolicy CasbinPolicy) error { query := a.db.NewUpdate(). Model(&newPolicy). - Table(a.tableName). + ModelTableExpr(a.tableName). Where("ptype = ?", oldPolicy.PType) values := oldPolicy.filterValuesWithKey() @@ -399,7 +402,7 @@ func (a *bunAdapter) updateRecord(oldPolicy, newPolicy CasbinPolicy) error { func (a *bunAdapter) updateRecordInTx(tx bun.Tx, oldPolicy, newPolicy CasbinPolicy) error { query := tx.NewUpdate(). Model(&newPolicy). - Table(a.tableName). + ModelTableExpr(a.tableName). Where("ptype = ?", oldPolicy.PType) values := oldPolicy.filterValuesWithKey() @@ -453,10 +456,10 @@ func (a *bunAdapter) UpdateFilteredPolicies(sec string, ptype string, newRules [ } selectQuery := tx.NewSelect(). - Table(a.tableName). + ModelTableExpr(a.tableName). Where("ptype = ?", ptype) deleteQuery := tx.NewDelete(). - Table(a.tableName). + ModelTableExpr(a.tableName). Where("ptype = ?", ptype) // Note that empty string in fieldValues could be any word. @@ -541,7 +544,7 @@ func (a *bunAdapter) UpdateFilteredPolicies(sec string, ptype string, newRules [ // create new policies if _, err := tx.NewInsert(). Model(&newPolicies). - Table(a.tableName). + ModelTableExpr(a.tableName). Exec(context.Background()); err != nil { if err := tx.Rollback(); err != nil { return nil, err From 714c35cabc57eecae8a8e820669abc9b4b095742 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 10:38:45 +0900 Subject: [PATCH 15/41] create table and index in the transaction (#5) --- adapter.go | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/adapter.go b/adapter.go index 3f2513e..b2b721c 100644 --- a/adapter.go +++ b/adapter.go @@ -114,28 +114,33 @@ func connectDB(driverName, dataSourceName string) (*bun.DB, error) { } func (a *bunAdapter) createTalbe() error { - if _, err := a.db.NewCreateTable(). - Model((*CasbinPolicy)(nil)). - ModelTableExpr(a.tableName). - IfNotExists(). - Exec(context.Background()); err != nil { + if err := a.db.RunInTx(context.Background(), &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error { + if _, err := tx.NewCreateTable(). + Model((*CasbinPolicy)(nil)). + ModelTableExpr(a.tableName). + IfNotExists(). + Exec(ctx); err != nil { + return err + } + // it might be better to create a unique index using hooks + // but the table name(a.tableName) cannot be accessed from the hook + // so we create the index here + if _, err := tx.NewCreateIndex(). + Model((*CasbinPolicy)(nil)). + ModelTableExpr(a.tableName). + Unique(). + Index("idx_ptype_v0_v1_v2_v3_v4_v5"). + Column("ptype", "v0", "v1", "v2", "v3", "v4", "v5"). + Exec(ctx); err != nil { + return err + } + return nil + }); err != nil { return err } return nil } -var _ bun.AfterCreateTableHook = (*CasbinPolicy)(nil) - -func (*CasbinPolicy) AfterCreateTable(ctx context.Context, query *bun.CreateTableQuery) error { - _, err := query.DB().NewCreateIndex(). - Model((*CasbinPolicy)(nil)). - Unique(). - Index("idx_ptype_v0_v1_v2_v3_v4_v5"). - Column("ptype", "v0", "v1", "v2", "v3", "v4", "v5"). - Exec(ctx) - return err -} - // LoadPolicy loads all policy rules from the storage. func (a *bunAdapter) LoadPolicy(model model.Model) error { var policies []CasbinPolicy From 2e72f9f151b4a780dddd2ce3d8c71bcd667fe5b6 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 10:58:03 +0900 Subject: [PATCH 16/41] add test for AddPolicies method (#5) --- adapter_test.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/adapter_test.go b/adapter_test.go index a1081d3..1ea8478 100644 --- a/adapter_test.go +++ b/adapter_test.go @@ -67,3 +67,29 @@ func TestBunAdapter_AddPolicy(t *testing.T) { [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}, {"jack", "data1", "read"}}, ) } + +func TestBunAdapter_AddPolicies(t *testing.T) { + a := initAdapter(t, "mysql", "root:root@tcp(127.0.0.1:3306)/test", WithDebugMode()) + e, err := casbin.NewEnforcer("testdata/rbac_model.conf", a) + if err != nil { + t.Fatalf("failed to create enforcer: %v", err) + } + if _, err := e.AddPolicies([][]string{{"jack", "data1", "read"}, {"jill", "data2", "write"}}); err != nil { + t.Fatalf("failed to add policies: %v", err) + } + if err := e.LoadPolicy(); err != nil { + t.Fatalf("failed to load policy: %v", err) + } + testGetPolicy( + t, + e, + [][]string{ + {"alice", "data1", "read"}, + {"bob", "data2", "write"}, + {"data2_admin", "data2", "read"}, + {"data2_admin", "data2", "write"}, + {"jack", "data1", "read"}, + {"jill", "data2", "write"}, + }, + ) +} From 9dd55ae4937fa407053883b2a2e9da0512a9cf6b Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 11:36:28 +0900 Subject: [PATCH 17/41] create unique index only if not exists (#5) --- adapter.go | 1 + 1 file changed, 1 insertion(+) diff --git a/adapter.go b/adapter.go index b2b721c..1beee30 100644 --- a/adapter.go +++ b/adapter.go @@ -128,6 +128,7 @@ func (a *bunAdapter) createTalbe() error { if _, err := tx.NewCreateIndex(). Model((*CasbinPolicy)(nil)). ModelTableExpr(a.tableName). + IfNotExists(). Unique(). Index("idx_ptype_v0_v1_v2_v3_v4_v5"). Column("ptype", "v0", "v1", "v2", "v3", "v4", "v5"). From f72285c4c2d66ece810f262fbb8ed0c978ea267e Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 11:37:00 +0900 Subject: [PATCH 18/41] add unit tests for removing policies (#5) --- adapter_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/adapter_test.go b/adapter_test.go index 1ea8478..63c58d0 100644 --- a/adapter_test.go +++ b/adapter_test.go @@ -93,3 +93,44 @@ func TestBunAdapter_AddPolicies(t *testing.T) { }, ) } + +func TestBunAdapter_RemovePolicy(t *testing.T) { + a := initAdapter(t, "mysql", "root:root@tcp(127.0.0.1:3306)/test", WithDebugMode()) + e, err := casbin.NewEnforcer("testdata/rbac_model.conf", a) + if err != nil { + t.Fatalf("failed to create enforcer: %v", err) + } + if _, err := e.RemovePolicy("alice", "data1", "read"); err != nil { + t.Fatalf("failed to remove policy: %v", err) + } + if err := e.LoadPolicy(); err != nil { + t.Fatalf("failed to load policy: %v", err) + } + testGetPolicy( + t, + e, + [][]string{{"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}, + ) +} + +func TestBunAdapter_RemovePolicies(t *testing.T) { + a := initAdapter(t, "mysql", "root:root@tcp(127.0.0.1:3306)/test", WithDebugMode()) + e, err := casbin.NewEnforcer("testdata/rbac_model.conf", a) + if err != nil { + t.Fatalf("failed to create enforcer: %v", err) + } + if _, err := e.RemovePolicy("alice", "data1", "read"); err != nil { + t.Fatalf("failed to remove policy: %v", err) + } + if _, err := e.RemovePolicies([][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}}); err != nil { + t.Fatalf("failed to remove policies: %v", err) + } + if err := e.LoadPolicy(); err != nil { + t.Fatalf("failed to load policy: %v", err) + } + testGetPolicy( + t, + e, + [][]string{{"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}, + ) +} From 685d414e4579a42ecb2b103de63bd65f6aed099b Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 15:51:05 +0900 Subject: [PATCH 19/41] create index in hooks (#5) --- adapter.go | 51 ++++++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/adapter.go b/adapter.go index 1beee30..7a18699 100644 --- a/adapter.go +++ b/adapter.go @@ -66,7 +66,7 @@ func NewAdapter(driverName, dataSourceName string, opts ...adapterOption) (*bunA b.db.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true))) } - if err := b.createTalbe(); err != nil { + if err := b.createTable(); err != nil { return nil, err } @@ -113,33 +113,26 @@ func connectDB(driverName, dataSourceName string) (*bun.DB, error) { } } -func (a *bunAdapter) createTalbe() error { - if err := a.db.RunInTx(context.Background(), &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error { - if _, err := tx.NewCreateTable(). - Model((*CasbinPolicy)(nil)). - ModelTableExpr(a.tableName). - IfNotExists(). - Exec(ctx); err != nil { - return err - } - // it might be better to create a unique index using hooks - // but the table name(a.tableName) cannot be accessed from the hook - // so we create the index here - if _, err := tx.NewCreateIndex(). - Model((*CasbinPolicy)(nil)). - ModelTableExpr(a.tableName). - IfNotExists(). - Unique(). - Index("idx_ptype_v0_v1_v2_v3_v4_v5"). - Column("ptype", "v0", "v1", "v2", "v3", "v4", "v5"). - Exec(ctx); err != nil { - return err - } - return nil - }); err != nil { - return err - } - return nil +func (a *bunAdapter) createTable() error { + _, err := a.db.NewCreateTable(). + Model((*CasbinPolicy)(nil)). + ModelTableExpr(a.tableName). + IfNotExists(). + Exec(context.Background()) + return err +} + +var _ bun.AfterCreateTableHook = (*CasbinPolicy)(nil) + +func (*CasbinPolicy) AfterCreateTable(ctx context.Context, query *bun.CreateTableQuery) error { + _, err := query.DB().NewCreateIndex(). + Model((*CasbinPolicy)(nil)). + ModelTableExpr(query.GetTableName()). + Unique(). + Index("idx_ptype_v0_v1_v2_v3_v4_v5"). + Column("ptype", "v0", "v1", "v2", "v3", "v4", "v5"). + Exec(ctx) + return err } // LoadPolicy loads all policy rules from the storage. @@ -225,7 +218,7 @@ func (a *bunAdapter) refreshTable() error { Exec(context.Background()); err != nil { return err } - return a.createTalbe() + return a.createTable() } // AddPolicy adds a policy rule to the storage. From 598d2218775dabb525c52769fca3bf78be256d02 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 15:58:17 +0900 Subject: [PATCH 20/41] define tableName as global variable reluctantly (#5) --- adapter.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/adapter.go b/adapter.go index 7a18699..582dc14 100644 --- a/adapter.go +++ b/adapter.go @@ -119,15 +119,23 @@ func (a *bunAdapter) createTable() error { ModelTableExpr(a.tableName). IfNotExists(). Exec(context.Background()) + tableNameForHook = a.tableName // pass the tableName field to the hook function return err } -var _ bun.AfterCreateTableHook = (*CasbinPolicy)(nil) +var ( + _ bun.AfterCreateTableHook = (*CasbinPolicy)(nil) + // TODO: find a better way to pass the tableName field to the hook function + // Originally, we want to use the value of the tableName field of the bunAdapter + // but hook function cannot access the field of the struct `bunAdapter` + // so we use a global variable to store the value of the tableName field + tableNameForHook = defaultTableName +) func (*CasbinPolicy) AfterCreateTable(ctx context.Context, query *bun.CreateTableQuery) error { _, err := query.DB().NewCreateIndex(). Model((*CasbinPolicy)(nil)). - ModelTableExpr(query.GetTableName()). + ModelTableExpr(tableNameForHook). Unique(). Index("idx_ptype_v0_v1_v2_v3_v4_v5"). Column("ptype", "v0", "v1", "v2", "v3", "v4", "v5"). From 2e3deede559873615192b0cd75ff61303d47a914 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 16:51:24 +0900 Subject: [PATCH 21/41] specify table name (#5) --- policy.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/policy.go b/policy.go index d61587d..be83e52 100644 --- a/policy.go +++ b/policy.go @@ -1,16 +1,19 @@ package casbinbunadapter +import "github.com/uptrace/bun" + // Database storage format following the below // https://casbin.org/docs/policy-storage#database-storage-format type CasbinPolicy struct { - ID int64 `bun:"id,pk,autoincrement"` - PType string `bun:"ptype,type:varchar(100),notnull"` - V0 string `bun:"v0,type:varchar(100)"` - V1 string `bun:"v1,type:varchar(100)"` - V2 string `bun:"v2,type:varchar(100)"` - V3 string `bun:"v3,type:varchar(100)"` - V4 string `bun:"v4,type:varchar(100)"` - V5 string `bun:"v5,type:varchar(100)"` + bun.BaseModel `bun:"casbin_policies,alias:cp"` + ID int64 `bun:"id,pk,autoincrement"` + PType string `bun:"ptype,type:varchar(100),notnull"` + V0 string `bun:"v0,type:varchar(100)"` + V1 string `bun:"v1,type:varchar(100)"` + V2 string `bun:"v2,type:varchar(100)"` + V3 string `bun:"v3,type:varchar(100)"` + V4 string `bun:"v4,type:varchar(100)"` + V5 string `bun:"v5,type:varchar(100)"` } func (c CasbinPolicy) toSlice() []string { From fef1a945076b304fe95af3195972f164b8e20b26 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 16:51:53 +0900 Subject: [PATCH 22/41] specify table name and not to set unique index (#5) --- adapter.go | 68 ++++++++---------------------------------------------- 1 file changed, 10 insertions(+), 58 deletions(-) diff --git a/adapter.go b/adapter.go index 582dc14..1486c5a 100644 --- a/adapter.go +++ b/adapter.go @@ -19,10 +19,6 @@ import ( "github.com/uptrace/bun/extra/bundebug" ) -const ( - defaultTableName = "casbin_policy" -) - var ( // check if the bunAdapter implements the Adapter interface _ persist.Adapter = (*bunAdapter)(nil) @@ -34,18 +30,11 @@ var ( type bunAdapter struct { db *bun.DB - tableName string debugMode bool } type adapterOption func(*bunAdapter) -func WithTableName(tableName string) adapterOption { - return func(a *bunAdapter) { - a.tableName = tableName - } -} - func WithDebugMode() adapterOption { return func(a *bunAdapter) { a.debugMode = true @@ -80,8 +69,7 @@ func newAdapter(driverName, dataSourceName string) (*bunAdapter, error) { } return &bunAdapter{ - db: db, - tableName: defaultTableName, + db: db, }, nil } @@ -114,33 +102,13 @@ func connectDB(driverName, dataSourceName string) (*bun.DB, error) { } func (a *bunAdapter) createTable() error { - _, err := a.db.NewCreateTable(). + if _, err := a.db.NewCreateTable(). Model((*CasbinPolicy)(nil)). - ModelTableExpr(a.tableName). IfNotExists(). - Exec(context.Background()) - tableNameForHook = a.tableName // pass the tableName field to the hook function - return err -} - -var ( - _ bun.AfterCreateTableHook = (*CasbinPolicy)(nil) - // TODO: find a better way to pass the tableName field to the hook function - // Originally, we want to use the value of the tableName field of the bunAdapter - // but hook function cannot access the field of the struct `bunAdapter` - // so we use a global variable to store the value of the tableName field - tableNameForHook = defaultTableName -) - -func (*CasbinPolicy) AfterCreateTable(ctx context.Context, query *bun.CreateTableQuery) error { - _, err := query.DB().NewCreateIndex(). - Model((*CasbinPolicy)(nil)). - ModelTableExpr(tableNameForHook). - Unique(). - Index("idx_ptype_v0_v1_v2_v3_v4_v5"). - Column("ptype", "v0", "v1", "v2", "v3", "v4", "v5"). - Exec(ctx) - return err + Exec(context.Background()); err != nil { + return err + } + return nil } // LoadPolicy loads all policy rules from the storage. @@ -148,7 +116,6 @@ func (a *bunAdapter) LoadPolicy(model model.Model) error { var policies []CasbinPolicy err := a.db.NewSelect(). Model(&policies). - ModelTableExpr(a.tableName). Scan(context.Background()) if err != nil { return err @@ -207,7 +174,6 @@ func (a *bunAdapter) savePolicyRecords(policies []CasbinPolicy) error { // bulk insert new policies if _, err := a.db.NewInsert(). Model(&policies). - ModelTableExpr(a.tableName). Exec(context.Background()); err != nil { return err } @@ -215,18 +181,14 @@ func (a *bunAdapter) savePolicyRecords(policies []CasbinPolicy) error { return nil } -// drop and recreate the table +// truncate tables func (a *bunAdapter) refreshTable() error { - // just truncate the table could be a better choice - // but NewTruncateTable() does not support ModelTableExpr - // so we drop and recreate the table instead - if _, err := a.db.NewDropTable(). - ModelTableExpr(a.tableName). - IfExists(). + if _, err := a.db.NewTruncateTable(). + Model((*CasbinPolicy)(nil)). Exec(context.Background()); err != nil { return err } - return a.createTable() + return nil } // AddPolicy adds a policy rule to the storage. @@ -235,7 +197,6 @@ func (a *bunAdapter) AddPolicy(sec string, ptype string, rule []string) error { newPolicy := newCasbinPolicy(ptype, rule) if _, err := a.db.NewInsert(). Model(&newPolicy). - ModelTableExpr(a.tableName). Exec(context.Background()); err != nil { return err } @@ -251,7 +212,6 @@ func (a *bunAdapter) AddPolicies(sec string, ptype string, rules [][]string) err } if _, err := a.db.NewInsert(). Model(&policies). - ModelTableExpr(a.tableName). Exec(context.Background()); err != nil { return err } @@ -284,7 +244,6 @@ func (a *bunAdapter) RemovePolicies(sec string, ptype string, rules [][]string) func (a *bunAdapter) deleteRecord(existingPolicy CasbinPolicy) error { query := a.db.NewDelete(). - ModelTableExpr(a.tableName). Where("ptype = ?", existingPolicy.PType) values := existingPolicy.filterValuesWithKey() @@ -294,7 +253,6 @@ func (a *bunAdapter) deleteRecord(existingPolicy CasbinPolicy) error { func (a *bunAdapter) deleteRecordInTx(tx bun.Tx, existingPolicy CasbinPolicy) error { query := tx.NewDelete(). - ModelTableExpr(a.tableName). Where("ptype = ?", existingPolicy.PType) values := existingPolicy.filterValuesWithKey() @@ -327,7 +285,6 @@ func (a *bunAdapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex i func (a *bunAdapter) deleteFilteredPolicy(ptype string, fieldIndex int, fieldValues ...string) error { query := a.db.NewDelete(). - ModelTableExpr(a.tableName). Where("ptype = ?", ptype) // Note that empty string in fieldValues could be any word. @@ -398,7 +355,6 @@ func (a *bunAdapter) UpdatePolicy(sec string, ptype string, oldRule, newRule []s func (a *bunAdapter) updateRecord(oldPolicy, newPolicy CasbinPolicy) error { query := a.db.NewUpdate(). Model(&newPolicy). - ModelTableExpr(a.tableName). Where("ptype = ?", oldPolicy.PType) values := oldPolicy.filterValuesWithKey() @@ -409,7 +365,6 @@ func (a *bunAdapter) updateRecord(oldPolicy, newPolicy CasbinPolicy) error { func (a *bunAdapter) updateRecordInTx(tx bun.Tx, oldPolicy, newPolicy CasbinPolicy) error { query := tx.NewUpdate(). Model(&newPolicy). - ModelTableExpr(a.tableName). Where("ptype = ?", oldPolicy.PType) values := oldPolicy.filterValuesWithKey() @@ -463,10 +418,8 @@ func (a *bunAdapter) UpdateFilteredPolicies(sec string, ptype string, newRules [ } selectQuery := tx.NewSelect(). - ModelTableExpr(a.tableName). Where("ptype = ?", ptype) deleteQuery := tx.NewDelete(). - ModelTableExpr(a.tableName). Where("ptype = ?", ptype) // Note that empty string in fieldValues could be any word. @@ -551,7 +504,6 @@ func (a *bunAdapter) UpdateFilteredPolicies(sec string, ptype string, newRules [ // create new policies if _, err := tx.NewInsert(). Model(&newPolicies). - ModelTableExpr(a.tableName). Exec(context.Background()); err != nil { if err := tx.Rollback(); err != nil { return nil, err From 5d7a09809bb35d9627117d110fae66c1676f78e6 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 17:05:06 +0900 Subject: [PATCH 23/41] specify table when to delete records (#5) --- adapter.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/adapter.go b/adapter.go index 1486c5a..b98cfef 100644 --- a/adapter.go +++ b/adapter.go @@ -244,6 +244,7 @@ func (a *bunAdapter) RemovePolicies(sec string, ptype string, rules [][]string) func (a *bunAdapter) deleteRecord(existingPolicy CasbinPolicy) error { query := a.db.NewDelete(). + Model((*CasbinPolicy)(nil)). Where("ptype = ?", existingPolicy.PType) values := existingPolicy.filterValuesWithKey() @@ -253,6 +254,7 @@ func (a *bunAdapter) deleteRecord(existingPolicy CasbinPolicy) error { func (a *bunAdapter) deleteRecordInTx(tx bun.Tx, existingPolicy CasbinPolicy) error { query := tx.NewDelete(). + Model((*CasbinPolicy)(nil)). Where("ptype = ?", existingPolicy.PType) values := existingPolicy.filterValuesWithKey() From 7337f1f7a7e281ca46ff37b0d31f72b3c5916150 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 17:25:56 +0900 Subject: [PATCH 24/41] add unit test for RemoveFilteredPolicy (#5) --- adapter_test.go | 93 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 3 deletions(-) diff --git a/adapter_test.go b/adapter_test.go index 63c58d0..315db74 100644 --- a/adapter_test.go +++ b/adapter_test.go @@ -119,9 +119,6 @@ func TestBunAdapter_RemovePolicies(t *testing.T) { if err != nil { t.Fatalf("failed to create enforcer: %v", err) } - if _, err := e.RemovePolicy("alice", "data1", "read"); err != nil { - t.Fatalf("failed to remove policy: %v", err) - } if _, err := e.RemovePolicies([][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}}); err != nil { t.Fatalf("failed to remove policies: %v", err) } @@ -134,3 +131,93 @@ func TestBunAdapter_RemovePolicies(t *testing.T) { [][]string{{"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}, ) } + +func TestBunAdapter_RemoveFilteredPolicy(t *testing.T) { + a := initAdapter(t, "mysql", "root:root@tcp(127.0.0.1:3306)/test", WithDebugMode()) + e, err := casbin.NewEnforcer("testdata/rbac_model.conf", a) + if err != nil { + t.Fatalf("failed to create enforcer: %v", err) + } + // 1. check if the policy with alice is all removed + if _, err := e.AddPolicy("alice", "data1", "write"); err != nil { + t.Fatalf("failed to add policy: %v", err) + } + _ = e.LoadPolicy() + testGetPolicy( + t, + e, + [][]string{ + {"alice", "data1", "read"}, + {"bob", "data2", "write"}, + {"data2_admin", "data2", "read"}, + {"data2_admin", "data2", "write"}, + {"alice", "data1", "write"}, + }, + ) + if _, err := e.RemoveFilteredPolicy(0, "alice"); err != nil { + t.Fatalf("failed to remove filtered policy: %v", err) + } + _ = e.LoadPolicy() + testGetPolicy( + t, + e, + [][]string{ + {"bob", "data2", "write"}, + {"data2_admin", "data2", "read"}, + {"data2_admin", "data2", "write"}, + }, + ) + // 2. check if the policy with data1 is all removed + if _, err := e.AddPolicies([][]string{{"alice", "data1", "read"}, {"alice", "data1", "write"}, {"alice", "data2", "read"}, {"alice", "data2", "write"}}); err != nil { + t.Fatalf("failed to add policies: %v", err) + } + _ = e.LoadPolicy() + testGetPolicy( + t, + e, + [][]string{ + {"bob", "data2", "write"}, + {"data2_admin", "data2", "read"}, + {"data2_admin", "data2", "write"}, + {"alice", "data1", "read"}, + {"alice", "data1", "write"}, + {"alice", "data2", "read"}, + {"alice", "data2", "write"}, + }, + ) + if _, err := e.RemoveFilteredPolicy(1, "data1"); err != nil { + t.Fatalf("failed to remove filtered policy: %v", err) + } + _ = e.LoadPolicy() + testGetPolicy( + t, + e, + [][]string{ + {"bob", "data2", "write"}, + {"data2_admin", "data2", "read"}, + {"data2_admin", "data2", "write"}, + {"alice", "data2", "read"}, + {"alice", "data2", "write"}, + }, + ) + // 3. check if the policy with alice and data2 is all removed + if _, err := e.RemoveFilteredPolicy(0, "alice", "data2"); err != nil { + t.Fatalf("failed to remove filtered policy: %v", err) + } + _ = e.LoadPolicy() + testGetPolicy( + t, + e, + [][]string{ + {"bob", "data2", "write"}, + {"data2_admin", "data2", "read"}, + {"data2_admin", "data2", "write"}, + }, + ) + // 4. check if the all policies are removed when fieldValues is empty + if _, err := e.RemoveFilteredPolicy(0, ""); err != nil { + t.Fatalf("failed to remove filtered policy: %v", err) + } + _ = e.LoadPolicy() + testGetPolicy(t, e, [][]string{}) +} From d71b2ace919ed7c8123d16ab3157130d9bf1184e Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 17:29:48 +0900 Subject: [PATCH 25/41] specify table for deletion (#5) --- adapter.go | 1 + 1 file changed, 1 insertion(+) diff --git a/adapter.go b/adapter.go index b98cfef..bb4425d 100644 --- a/adapter.go +++ b/adapter.go @@ -287,6 +287,7 @@ func (a *bunAdapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex i func (a *bunAdapter) deleteFilteredPolicy(ptype string, fieldIndex int, fieldValues ...string) error { query := a.db.NewDelete(). + Model((*CasbinPolicy)(nil)). Where("ptype = ?", ptype) // Note that empty string in fieldValues could be any word. From 09ae8229205d2c994ad4e120762608c6846362bc Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 17:45:50 +0900 Subject: [PATCH 26/41] add unit test for update polices (#5) --- adapter_test.go | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/adapter_test.go b/adapter_test.go index 315db74..31d55a5 100644 --- a/adapter_test.go +++ b/adapter_test.go @@ -221,3 +221,120 @@ func TestBunAdapter_RemoveFilteredPolicy(t *testing.T) { _ = e.LoadPolicy() testGetPolicy(t, e, [][]string{}) } + +func TestBunAdapter_UpdatePolicy(t *testing.T) { + a := initAdapter(t, "mysql", "root:root@tcp(127.0.0.1:3306)/test", WithDebugMode()) + e, err := casbin.NewEnforcer("testdata/rbac_model.conf", a) + if err != nil { + t.Fatalf("failed to create enforcer: %v", err) + } + if _, err := e.UpdatePolicy( + []string{"alice", "data1", "read"}, + []string{"alice", "data1", "write"}, + ); err != nil { + t.Fatalf("failed to update policy: %v", err) + } + if err := e.LoadPolicy(); err != nil { + t.Fatalf("failed to load policy: %v", err) + } + testGetPolicy( + t, + e, + [][]string{ + {"alice", "data1", "write"}, + {"bob", "data2", "write"}, + {"data2_admin", "data2", "read"}, + {"data2_admin", "data2", "write"}, + }, + ) +} + +func TestBunAdapter_UpdatePolicies(t *testing.T) { + a := initAdapter(t, "mysql", "root:root@tcp(127.0.0.1:3306)/test", WithDebugMode()) + e, err := casbin.NewEnforcer("testdata/rbac_model.conf", a) + if err != nil { + t.Fatalf("failed to create enforcer: %v", err) + } + if _, err := e.UpdatePolicies( + [][]string{{"alice", "data1", "write"}, {"bob", "data2", "write"}}, + [][]string{{"alice", "data1", "read"}, {"bob", "data2", "read"}}, + ); err != nil { + t.Fatalf("failed to update policies: %v", err) + } + if err := e.LoadPolicy(); err != nil { + t.Fatalf("failed to load policy: %v", err) + } + testGetPolicy( + t, + e, + [][]string{ + {"alice", "data1", "read"}, + {"bob", "data2", "read"}, + {"data2_admin", "data2", "read"}, + {"data2_admin", "data2", "write"}, + }, + ) +} + +func TestBunAdapter_UpdateFilteredPolicies(t *testing.T) { + a := initAdapter(t, "mysql", "root:root@tcp(127.0.0.1:3306)/test", WithDebugMode()) + e, err := casbin.NewEnforcer("testdata/rbac_model.conf", a) + if err != nil { + t.Fatalf("failed to create enforcer: %v", err) + } + // 1. check if the policy with data1 is all updated + if _, err := e.AddPolicy("bob", "data1", "read"); err != nil { + t.Fatalf("failed to add policy: %v", err) + } + _ = e.LoadPolicy() + testGetPolicy( + t, + e, + [][]string{ + {"alice", "data1", "read"}, + {"bob", "data2", "write"}, + {"data2_admin", "data2", "read"}, + {"data2_admin", "data2", "write"}, + {"bob", "data1", "read"}, + }, + ) + if _, err := e.UpdateFilteredPolicies( + [][]string{{"data1", "write"}}, + 1, + "data1", + ); err != nil { + t.Fatalf("failed to update filtered policies: %v", err) + } + _ = e.LoadPolicy() + testGetPolicy( + t, + e, + [][]string{ + {"alice", "data1", "write"}, + {"bob", "data2", "write"}, + {"data2_admin", "data2", "read"}, + {"data2_admin", "data2", "write"}, + {"bob", "data1", "write"}, + }, + ) + // 2. check if the policy with data1 is all updated + if _, err := e.UpdateFilteredPolicies( + [][]string{{"delete"}}, + 1, + "data1", + ); err != nil { + t.Fatalf("failed to update filtered policies: %v", err) + } + _ = e.LoadPolicy() + testGetPolicy( + t, + e, + [][]string{ + {"alice", "data1", "delete"}, + {"bob", "data2", "write"}, + {"data2_admin", "data2", "read"}, + {"data2_admin", "data2", "write"}, + {"bob", "data1", "delete"}, + }, + ) +} From 211c81a941000fe0d32f107849ef0944a7691a80 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 17:51:34 +0900 Subject: [PATCH 27/41] specify tables (#5) --- adapter.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/adapter.go b/adapter.go index bb4425d..7d5c6d0 100644 --- a/adapter.go +++ b/adapter.go @@ -420,9 +420,12 @@ func (a *bunAdapter) UpdateFilteredPolicies(sec string, ptype string, newRules [ return nil, err } + oldPolicies := make([]CasbinPolicy, 0) selectQuery := tx.NewSelect(). + Model(&oldPolicies). Where("ptype = ?", ptype) deleteQuery := tx.NewDelete(). + Model((*CasbinPolicy)(nil)). Where("ptype = ?", ptype) // Note that empty string in fieldValues could be any word. @@ -488,8 +491,7 @@ func (a *bunAdapter) UpdateFilteredPolicies(sec string, ptype string, newRules [ } // store old policies - oldPolicies := make([]CasbinPolicy, 0) - if err := selectQuery.Scan(context.Background(), &oldPolicies); err != nil { + if err := selectQuery.Scan(context.Background()); err != nil { if err := tx.Rollback(); err != nil { return nil, err } From 3e6b2e0f1fde03240f145fe91782c968d87d5040 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 18:14:34 +0900 Subject: [PATCH 28/41] fix UpdateFilteredPolicies (#5) --- adapter_test.go | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/adapter_test.go b/adapter_test.go index 31d55a5..ad6aaea 100644 --- a/adapter_test.go +++ b/adapter_test.go @@ -282,8 +282,8 @@ func TestBunAdapter_UpdateFilteredPolicies(t *testing.T) { if err != nil { t.Fatalf("failed to create enforcer: %v", err) } - // 1. check if the policy with data1 is all updated - if _, err := e.AddPolicy("bob", "data1", "read"); err != nil { + // 1. check if the policy with alice is all updated + if _, err := e.AddPolicy("alice", "data1", "write"); err != nil { t.Fatalf("failed to add policy: %v", err) } _ = e.LoadPolicy() @@ -295,13 +295,13 @@ func TestBunAdapter_UpdateFilteredPolicies(t *testing.T) { {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}, - {"bob", "data1", "read"}, + {"alice", "data1", "write"}, }, ) if _, err := e.UpdateFilteredPolicies( - [][]string{{"data1", "write"}}, - 1, - "data1", + [][]string{{"alice", "data3", "read"}, {"alice", "data3", "write"}}, + 0, + "alice", ); err != nil { t.Fatalf("failed to update filtered policies: %v", err) } @@ -310,18 +310,19 @@ func TestBunAdapter_UpdateFilteredPolicies(t *testing.T) { t, e, [][]string{ - {"alice", "data1", "write"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}, - {"bob", "data1", "write"}, + {"alice", "data3", "read"}, + {"alice", "data3", "write"}, }, ) - // 2. check if the policy with data1 is all updated + // 2. check if the policy with data2 and write is all updated if _, err := e.UpdateFilteredPolicies( - [][]string{{"delete"}}, + [][]string{{"bob", "data2", "delete"}, {"data2_admin", "data2", "delete"}}, 1, - "data1", + "data2", + "write", ); err != nil { t.Fatalf("failed to update filtered policies: %v", err) } @@ -330,11 +331,11 @@ func TestBunAdapter_UpdateFilteredPolicies(t *testing.T) { t, e, [][]string{ - {"alice", "data1", "delete"}, - {"bob", "data2", "write"}, + {"bob", "data2", "delete"}, {"data2_admin", "data2", "read"}, - {"data2_admin", "data2", "write"}, - {"bob", "data1", "delete"}, + {"data2_admin", "data2", "delete"}, + {"alice", "data3", "read"}, + {"alice", "data3", "write"}, }, ) } From 39999e620256bfcb50bfbd69cc77540149dfd455 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sat, 30 Mar 2024 18:18:49 +0900 Subject: [PATCH 29/41] fix unit test (#5) --- adapter_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adapter_test.go b/adapter_test.go index ad6aaea..3af9c62 100644 --- a/adapter_test.go +++ b/adapter_test.go @@ -331,11 +331,11 @@ func TestBunAdapter_UpdateFilteredPolicies(t *testing.T) { t, e, [][]string{ - {"bob", "data2", "delete"}, {"data2_admin", "data2", "read"}, - {"data2_admin", "data2", "delete"}, {"alice", "data3", "read"}, {"alice", "data3", "write"}, + {"bob", "data2", "delete"}, + {"data2_admin", "data2", "delete"}, }, ) } From fbd20cb2abeb7583123003171ed2a4af1a0b22bf Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sun, 31 Mar 2024 11:59:35 +0900 Subject: [PATCH 30/41] add unit test for each database (#5) --- .github/workflows/test.yml | 2 + adapter_test.go | 90 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6e38fa2..75a5234 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,6 +21,7 @@ jobs: image: postgres env: POSTGRES_PASSWORD: postgres + POSTGRES_DB: test options: >- --health-cmd pg_isready --health-interval 10s @@ -33,6 +34,7 @@ jobs: env: MSSQL_SA_PASSWORD: "Password123" ACCEPT_EULA: "Y" + DBNAME: "test" ports: - 1433:1433 steps: diff --git a/adapter_test.go b/adapter_test.go index 3af9c62..152a5cd 100644 --- a/adapter_test.go +++ b/adapter_test.go @@ -49,6 +49,96 @@ func initAdapter(t *testing.T, driverName, dataSourceName string, opts ...adapte return a } +func testSaveLoad(t *testing.T, a *bunAdapter) { + initPolicy(t, a) + + e, err := casbin.NewEnforcer("testdata/rbac_model.conf", a) + if err != nil { + t.Fatalf("failed to create enforcer: %v", err) + } + testGetPolicy( + t, + e, + [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}, + ) +} + +func testAutoSave(t *testing.T, a *bunAdapter) { + e, err := casbin.NewEnforcer("testdata/rbac_model.conf", a) + if err != nil { + t.Fatalf("failed to create enforcer: %v", err) + } + e.EnableAutoSave(false) + + if _, err := e.AddPolicy("alice", "data1", "read"); err != nil { + t.Fatalf("failed to add policy: %v", err) + } + if err := e.LoadPolicy(); err != nil { + t.Fatalf("failed to load policy: %v", err) + } + testGetPolicy( + t, + e, + [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}, + ) + + e.EnableAutoSave(true) + + if _, err := e.AddPolicy("alice", "data1", "write"); err != nil { + t.Fatalf("failed to add policy: %v", err) + } + if err := e.LoadPolicy(); err != nil { + t.Fatalf("failed to load policy: %v", err) + } + testGetPolicy( + t, + e, + [][]string{{"alice", "data1", "write"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}, {"alice", "data1", "write"}}, + ) + + if _, err := e.RemovePolicy("alice", "data1", "write"); err != nil { + t.Fatalf("failed to remove policy: %v", err) + } + if err := e.LoadPolicy(); err != nil { + t.Fatalf("failed to load policy: %v", err) + } + testGetPolicy( + t, + e, + [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}, + ) + + if _, err := e.RemoveFilteredPolicy(0, "data2_admin"); err != nil { + t.Fatalf("failed to remove filtered policy: %v", err) + } + if err := e.LoadPolicy(); err != nil { + t.Fatalf("failed to load policy: %v", err) + } + testGetPolicy( + t, + e, + [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}}, + ) +} + +func TestBunAdapters(t *testing.T) { + a := initAdapter(t, "mysql", "root:root@tcp(127.0.0.1:3306)/test", WithDebugMode()) + testSaveLoad(t, a) + testAutoSave(t, a) + + a = initAdapter(t, "postgres", "postgres://postgres:@localhost:5432/test?sslmode=disable") + testSaveLoad(t, a) + testAutoSave(t, a) + + a = initAdapter(t, "sqlite3", "file::memory:?cache=shared") + testSaveLoad(t, a) + testAutoSave(t, a) + + a = initAdapter(t, "mssql", "sqlserver://sa:Password123@localhost:1433?database=test") + testSaveLoad(t, a) + testAutoSave(t, a) +} + func TestBunAdapter_AddPolicy(t *testing.T) { a := initAdapter(t, "mysql", "root:root@tcp(127.0.0.1:3306)/test", WithDebugMode()) e, err := casbin.NewEnforcer("testdata/rbac_model.conf", a) From 50ed18c393c73bc3414b19885e81b4429b9c4bc7 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sun, 31 Mar 2024 12:09:50 +0900 Subject: [PATCH 31/41] update dsn for postgres (#5) --- adapter_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter_test.go b/adapter_test.go index 152a5cd..9ef27e4 100644 --- a/adapter_test.go +++ b/adapter_test.go @@ -126,7 +126,7 @@ func TestBunAdapters(t *testing.T) { testSaveLoad(t, a) testAutoSave(t, a) - a = initAdapter(t, "postgres", "postgres://postgres:@localhost:5432/test?sslmode=disable") + a = initAdapter(t, "postgres", "postgres://postgres:postgres@localhost:5432/test?sslmode=disable") testSaveLoad(t, a) testAutoSave(t, a) From 7778ebb59d2aa83b9319b1e48c75623bd224fcb2 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sun, 31 Mar 2024 12:15:29 +0900 Subject: [PATCH 32/41] fix dsn for mssql (#5) --- .github/workflows/test.yml | 1 - adapter_test.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 75a5234..081eb1c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,7 +34,6 @@ jobs: env: MSSQL_SA_PASSWORD: "Password123" ACCEPT_EULA: "Y" - DBNAME: "test" ports: - 1433:1433 steps: diff --git a/adapter_test.go b/adapter_test.go index 9ef27e4..02939b0 100644 --- a/adapter_test.go +++ b/adapter_test.go @@ -134,7 +134,7 @@ func TestBunAdapters(t *testing.T) { testSaveLoad(t, a) testAutoSave(t, a) - a = initAdapter(t, "mssql", "sqlserver://sa:Password123@localhost:1433?database=test") + a = initAdapter(t, "mssql", "sqlserver://sa:Password123@localhost:1433?database=master") testSaveLoad(t, a) testAutoSave(t, a) } From 44dc82f66abbc96eea43ba148b0e6d0edf572b92 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sun, 31 Mar 2024 12:20:31 +0900 Subject: [PATCH 33/41] fix unit test (#5) --- adapter_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter_test.go b/adapter_test.go index 02939b0..56d14c4 100644 --- a/adapter_test.go +++ b/adapter_test.go @@ -93,7 +93,7 @@ func testAutoSave(t *testing.T, a *bunAdapter) { testGetPolicy( t, e, - [][]string{{"alice", "data1", "write"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}, {"alice", "data1", "write"}}, + [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}, {"alice", "data1", "write"}}, ) if _, err := e.RemovePolicy("alice", "data1", "write"); err != nil { From e65352fc3fcd843a0815e5d43156825aa3542068 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sun, 31 Mar 2024 12:58:24 +0900 Subject: [PATCH 34/41] add packages for unit tests (#5) --- go.mod | 5 +++++ go.sum | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/go.mod b/go.mod index aeb94b5..9ef8e26 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,9 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect + github.com/agiledragon/gomonkey/v2 v2.11.0 // indirect github.com/casbin/govaluate v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.16.0 // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect @@ -29,7 +31,9 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.19 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/stretchr/testify v1.9.0 // indirect github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect github.com/uptrace/bun/extra/bundebug v1.1.17 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect @@ -38,6 +42,7 @@ require ( golang.org/x/mod v0.14.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/tools v0.16.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/uint128 v1.3.0 // indirect mellium.im/sasl v0.3.1 // indirect modernc.org/cc/v3 v3.41.0 // indirect diff --git a/go.sum b/go.sum index eb34959..0d6b088 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4 github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= +github.com/agiledragon/gomonkey/v2 v2.11.0 h1:5oxSgA+tC1xuGsrIorR+sYiziYltmJyEZ9qA25b6l5U= +github.com/agiledragon/gomonkey/v2 v2.11.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= github.com/casbin/casbin/v2 v2.85.0 h1:VajW9GR/T0fp3SND183gneZGIAdYtl9C7bDYBrqQiGg= github.com/casbin/casbin/v2 v2.85.0/go.mod h1:jX8uoN4veP85O/n2674r2qtfSXI6myvxW85f6TH50fw= github.com/casbin/govaluate v1.1.0 h1:6xdCWIpE9CwHdZhlVQW+froUrCsjb6/ZYNcXODfLT+E= @@ -31,8 +33,10 @@ github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbu github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -48,10 +52,14 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= github.com/uptrace/bun v1.1.17 h1:qxBaEIo0hC/8O3O6GrMDKxqyT+mw5/s0Pn/n6xjyGIk= @@ -101,6 +109,7 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= From 90ca3e2be6773b4ca93353548d781cfd28402bb1 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sun, 31 Mar 2024 12:58:38 +0900 Subject: [PATCH 35/41] add context adapters (#5) --- context_adapter.go | 81 +++++++++++++++++++++++++++++ context_adapter_test.go | 109 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 context_adapter.go create mode 100644 context_adapter_test.go diff --git a/context_adapter.go b/context_adapter.go new file mode 100644 index 0000000..b4c2de3 --- /dev/null +++ b/context_adapter.go @@ -0,0 +1,81 @@ +// this implementation refers to the following link: +// https://github.com/casbin/gorm-adapter/blob/master/context_adapter.go + +package casbinbunadapter + +import ( + "context" + + "github.com/casbin/casbin/v2/model" + "github.com/casbin/casbin/v2/persist" +) + +var ( + // check if the ctxBunAdapter implements the ContextAdapter interface + _ persist.ContextAdapter = (*ctxBunAdapter)(nil) // Ensure ctxBunAdapter +) + +type ctxBunAdapter struct { + *bunAdapter +} + +func NewCtxAdapter(driverName string, dataSourceName string, opts ...adapterOption) (*ctxBunAdapter, error) { + adapter, err := NewAdapter(driverName, dataSourceName, opts...) + if err != nil { + return nil, err + } + return &ctxBunAdapter{adapter}, nil +} + +// executeWithContext is a helper function to execute a function with context and return the result or error. +func executeWithContext(ctx context.Context, fn func() error) error { + done := make(chan error) + go func() { + done <- fn() + }() + + select { + case <-ctx.Done(): + return ctx.Err() + case err := <-done: + return err + } +} + +// LoadPolicyCtx loads all policy rules from the storage with context. +func (a *ctxBunAdapter) LoadPolicyCtx(ctx context.Context, model model.Model) error { + return executeWithContext(ctx, func() error { + return a.LoadPolicy(model) + }) +} + +// SavePolicyCtx saves all policy rules to the storage with context. +func (a *ctxBunAdapter) SavePolicyCtx(ctx context.Context, model model.Model) error { + return executeWithContext(ctx, func() error { + return a.SavePolicy(model) + }) +} + +// AddPolicyCtx adds a policy rule to the storage with context. +// This is part of the Auto-Save feature. +func (a *ctxBunAdapter) AddPolicyCtx(ctx context.Context, sec string, ptype string, rule []string) error { + return executeWithContext(ctx, func() error { + return a.AddPolicy(sec, ptype, rule) + }) +} + +// RemovePolicyCtx removes a policy rule from the storage with context. +// This is part of the Auto-Save feature. +func (a *ctxBunAdapter) RemovePolicyCtx(ctx context.Context, sec string, ptype string, rule []string) error { + return executeWithContext(ctx, func() error { + return a.RemovePolicy(sec, ptype, rule) + }) +} + +// RemoveFilteredPolicyCtx removes policy rules that match the filter from the storage with context. +// This is part of the Auto-Save feature. +func (a *ctxBunAdapter) RemoveFilteredPolicyCtx(ctx context.Context, sec string, ptype string, fieldIndex int, fieldValues ...string) error { + return executeWithContext(ctx, func() error { + return a.RemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues...) + }) +} diff --git a/context_adapter_test.go b/context_adapter_test.go new file mode 100644 index 0000000..9c33037 --- /dev/null +++ b/context_adapter_test.go @@ -0,0 +1,109 @@ +package casbinbunadapter + +import ( + "context" + "testing" + "time" + + "github.com/agiledragon/gomonkey/v2" + "github.com/casbin/casbin/v2" + "github.com/stretchr/testify/assert" +) + +func mockExecuteWithContextTimeOut(ctx context.Context, fn func() error) error { + done := make(chan error) + go func() { + time.Sleep(500 * time.Microsecond) + done <- fn() + }() + + select { + case <-ctx.Done(): + return ctx.Err() + case err := <-done: + return err + } +} + +func clearDBPolicy() (*casbin.Enforcer, *ctxBunAdapter) { + ca, err := NewCtxAdapter("mysql", "root:root@tcp(127.0.0.1:3306)/test", WithDebugMode()) + if err != nil { + panic(err) + } + e, err := casbin.NewEnforcer("examples/rbac_model.conf", ca) + if err != nil { + panic(err) + } + e.ClearPolicy() + if err := e.SavePolicy(); err != nil { + panic(err) + } + return e, ca +} + +func TestCtxBunAdapter_AddPolicyCtx(t *testing.T) { + e, ca := clearDBPolicy() + + if err := ca.AddPolicyCtx(context.Background(), "p", "p", []string{"alice", "data1", "read"}); err != nil { + t.Fatalf("failed to add policy: %v", err) + } + _ = e.LoadPolicy() + testGetPolicy( + t, + e, + [][]string{ + {"alice", "data1", "read"}, + }, + ) + + var p = gomonkey.ApplyFunc(executeWithContext, mockExecuteWithContextTimeOut) + defer p.Reset() + ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond) + defer cancel() + assert.EqualError(t, ca.AddPolicyCtx(ctx, "p", "p", []string{"alice", "data2", "read"}), "context deadline exceeded") +} + +func TestCtxBunAdapter_RemovePolicyCtx(t *testing.T) { + e, ca := clearDBPolicy() + + _ = ca.AddPolicyCtx(context.Background(), "p", "p", []string{"alice", "data1", "read"}) + _ = ca.AddPolicyCtx(context.Background(), "p", "p", []string{"alice", "data2", "read"}) + _ = ca.RemovePolicyCtx(context.Background(), "p", "p", []string{"alice", "data1", "read"}) + _ = e.LoadPolicy() + testGetPolicy( + t, + e, + [][]string{ + {"alice", "data2", "read"}, + }, + ) + + var p = gomonkey.ApplyFunc(executeWithContext, mockExecuteWithContextTimeOut) + defer p.Reset() + ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond) + defer cancel() + assert.EqualError(t, ca.RemovePolicyCtx(ctx, "p", "p", []string{"alice", "data2", "read"}), "context deadline exceeded") +} + +func TestCtxBunAdapter_RemoveFilteredPolicyCtx(t *testing.T) { + e, ca := clearDBPolicy() + + _ = ca.AddPolicyCtx(context.Background(), "p", "p", []string{"alice", "data1", "read"}) + _ = ca.AddPolicyCtx(context.Background(), "p", "p", []string{"alice", "data2", "read"}) + _ = ca.AddPolicyCtx(context.Background(), "p", "p", []string{"bob", "data1", "read"}) + _ = ca.RemoveFilteredPolicyCtx(context.Background(), "p", "p", 0, "alice") + _ = e.LoadPolicy() + testGetPolicy( + t, + e, + [][]string{ + {"bob", "data1", "read"}, + }, + ) + + var p = gomonkey.ApplyFunc(executeWithContext, mockExecuteWithContextTimeOut) + defer p.Reset() + ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond) + defer cancel() + assert.EqualError(t, ca.RemoveFilteredPolicyCtx(ctx, "p", "p", 0, "alice"), "context deadline exceeded") +} From caabba8539fe5d4e757a6f5f55ec08ae44cc9651 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sun, 31 Mar 2024 13:02:09 +0900 Subject: [PATCH 36/41] fix unit test (#5) --- context_adapter_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context_adapter_test.go b/context_adapter_test.go index 9c33037..a4e563f 100644 --- a/context_adapter_test.go +++ b/context_adapter_test.go @@ -30,7 +30,7 @@ func clearDBPolicy() (*casbin.Enforcer, *ctxBunAdapter) { if err != nil { panic(err) } - e, err := casbin.NewEnforcer("examples/rbac_model.conf", ca) + e, err := casbin.NewEnforcer("testdata/rbac_model.conf", ca) if err != nil { panic(err) } From 661364edd6f19f592023c01185537fee6895ed53 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sun, 31 Mar 2024 13:07:42 +0900 Subject: [PATCH 37/41] add unit test for save and load (#5) --- context_adapter_test.go | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/context_adapter_test.go b/context_adapter_test.go index a4e563f..08f7914 100644 --- a/context_adapter_test.go +++ b/context_adapter_test.go @@ -41,6 +41,54 @@ func clearDBPolicy() (*casbin.Enforcer, *ctxBunAdapter) { return e, ca } +func TestCtxBunAdapter_LoadPolicyCtx(t *testing.T) { + e, _ := casbin.NewEnforcer("testdata/rbac_model.conf", "testdata/rbac_policy.csv") + ca, err := NewCtxAdapter("mysql", "root:root@tcp(127.0.0.1:3306)/test", WithDebugMode()) + if err != nil { + panic(err) + } + + assert.NoError(t, ca.LoadPolicyCtx(context.Background(), e.GetModel())) + e, _ = casbin.NewEnforcer("testdata/rbac_model.conf", ca) + testGetPolicy( + t, + e, + [][]string{ + {"alice", "data1", "read"}, + {"bob", "data2", "write"}, + {"data2_admin", "data2", "read"}, + {"data2_admin", "data2", "write"}, + }, + ) + + var p = gomonkey.ApplyFunc(executeWithContext, mockExecuteWithContextTimeOut) + defer p.Reset() + ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond) + defer cancel() + assert.EqualError(t, ca.LoadPolicyCtx(ctx, e.GetModel()), "context deadline exceeded") +} + +func TestCtxBunAdapter_SavePolicyCtx(t *testing.T) { + e, ca := clearDBPolicy() + + _, _ = e.AddPolicy("alice", "data1", "read") + assert.NoError(t, ca.SavePolicyCtx(context.Background(), e.GetModel())) + _ = e.LoadPolicy() + testGetPolicy( + t, + e, + [][]string{ + {"alice", "data1", "read"}, + }, + ) + + var p = gomonkey.ApplyFunc(executeWithContext, mockExecuteWithContextTimeOut) + defer p.Reset() + ctx, cancel := context.WithTimeout(context.Background(), 300*time.Microsecond) + defer cancel() + assert.EqualError(t, ca.SavePolicyCtx(ctx, e.GetModel()), "context deadline exceeded") +} + func TestCtxBunAdapter_AddPolicyCtx(t *testing.T) { e, ca := clearDBPolicy() From 5487bb8b2a5d847a93671a61b1adf65380c91f4d Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sun, 31 Mar 2024 13:12:54 +0900 Subject: [PATCH 38/41] fix unit test (#5) --- context_adapter_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/context_adapter_test.go b/context_adapter_test.go index 08f7914..4f8a9d5 100644 --- a/context_adapter_test.go +++ b/context_adapter_test.go @@ -71,6 +71,7 @@ func TestCtxBunAdapter_LoadPolicyCtx(t *testing.T) { func TestCtxBunAdapter_SavePolicyCtx(t *testing.T) { e, ca := clearDBPolicy() + e.EnableAutoSave(false) _, _ = e.AddPolicy("alice", "data1", "read") assert.NoError(t, ca.SavePolicyCtx(context.Background(), e.GetModel())) _ = e.LoadPolicy() From 4a3edd88c11d16340677ea80b3aa81935e85a279 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sun, 31 Mar 2024 13:20:23 +0900 Subject: [PATCH 39/41] fix unit test (#5) --- context_adapter_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/context_adapter_test.go b/context_adapter_test.go index 4f8a9d5..964eea4 100644 --- a/context_adapter_test.go +++ b/context_adapter_test.go @@ -35,9 +35,8 @@ func clearDBPolicy() (*casbin.Enforcer, *ctxBunAdapter) { panic(err) } e.ClearPolicy() - if err := e.SavePolicy(); err != nil { - panic(err) - } + _ = e.SavePolicy() + return e, ca } From c1a93554a34d65a50b02a948f6a6bfd6e35fa17f Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sun, 31 Mar 2024 13:28:54 +0900 Subject: [PATCH 40/41] set finalizer for adapter (#5) --- adapter.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/adapter.go b/adapter.go index 7d5c6d0..05c0283 100644 --- a/adapter.go +++ b/adapter.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "runtime" "github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/persist" @@ -59,6 +60,12 @@ func NewAdapter(driverName, dataSourceName string, opts ...adapterOption) (*bunA return nil, err } + runtime.SetFinalizer(b, func(a *bunAdapter) { + if err := a.db.Close(); err != nil { + panic(err) + } + }) + return b, nil } From 0db9c1ed9505a61963a8e22d1c8305967dd35c39 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sun, 31 Mar 2024 13:35:09 +0900 Subject: [PATCH 41/41] fix unit test (#5) --- context_adapter_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context_adapter_test.go b/context_adapter_test.go index 964eea4..70b97f8 100644 --- a/context_adapter_test.go +++ b/context_adapter_test.go @@ -46,7 +46,7 @@ func TestCtxBunAdapter_LoadPolicyCtx(t *testing.T) { if err != nil { panic(err) } - + _ = ca.SavePolicyCtx(context.Background(), e.GetModel()) assert.NoError(t, ca.LoadPolicyCtx(context.Background(), e.GetModel())) e, _ = casbin.NewEnforcer("testdata/rbac_model.conf", ca) testGetPolicy(