diff --git a/.travis.yml b/.travis.yml index 3271265..bb9fc8c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,17 @@ sudo: required -services: - - docker - env: - - DOCKER_BIND_LOCALHOST=true + - GO111MODULE=on language: go go_import_path: github.com/ory/ladon go: - - 1.9 + - 1.11 install: - go get github.com/mattn/goveralls golang.org/x/tools/cmd/cover github.com/pierrre/gotestcover - - curl -L -s https://github.com/golang/dep/releases/download/v0.3.2/dep-linux-amd64 -o $GOPATH/bin/dep && chmod +x $GOPATH/bin/dep - - dep ensure -vendor-only script: - gotestcover -coverprofile="cover.out" -race -covermode="atomic" $(go list ./... | grep -v /vendor/) diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index 7f29134..0000000 --- a/Gopkg.lock +++ /dev/null @@ -1,276 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - name = "github.com/Azure/go-ansiterm" - packages = [ - ".", - "winterm" - ] - revision = "d6e3b3328b783f23731bc4d058875b0371ff8109" - -[[projects]] - name = "github.com/Microsoft/go-winio" - packages = ["."] - revision = "67921128fb397dd80339870d2193d6b1e6856fd4" - version = "v0.4.8" - -[[projects]] - branch = "master" - name = "github.com/Nvveen/Gotty" - packages = ["."] - revision = "cd527374f1e5bff4938207604a14f2e38a9cf512" - -[[projects]] - name = "github.com/cenkalti/backoff" - packages = ["."] - revision = "2ea60e5f094469f9e65adb9cd103795b73ae743e" - version = "v2.0.0" - -[[projects]] - branch = "master" - name = "github.com/containerd/continuity" - packages = ["pathdriver"] - revision = "0cf103d319cc2d7efe085224094f466d1f8b9640" - -[[projects]] - name = "github.com/davecgh/go-spew" - packages = ["spew"] - revision = "346938d642f2ec3594ed81d874461961cd0faa76" - version = "v1.1.0" - -[[projects]] - name = "github.com/docker/docker" - packages = [ - "api/types", - "api/types/blkiodev", - "api/types/container", - "api/types/filters", - "api/types/mount", - "api/types/network", - "api/types/registry", - "api/types/strslice", - "api/types/swarm", - "api/types/swarm/runtime", - "api/types/versions", - "opts", - "pkg/archive", - "pkg/fileutils", - "pkg/homedir", - "pkg/idtools", - "pkg/ioutils", - "pkg/jsonmessage", - "pkg/longpath", - "pkg/mount", - "pkg/pools", - "pkg/stdcopy", - "pkg/system", - "pkg/term", - "pkg/term/windows" - ] - revision = "fe8aac6f5ae413a967adb0adad0b54abdfb825c4" - -[[projects]] - name = "github.com/docker/go-connections" - packages = ["nat"] - revision = "3ede32e2033de7505e6500d6c868c2b9ed9f169d" - version = "v0.3.0" - -[[projects]] - name = "github.com/docker/go-units" - packages = ["."] - revision = "47565b4f722fb6ceae66b95f853feed578a4a51c" - version = "v0.3.3" - -[[projects]] - name = "github.com/go-sql-driver/mysql" - packages = ["."] - revision = "a0583e0143b1624142adab07e0e97fe106d99561" - version = "v1.3" - -[[projects]] - name = "github.com/gogo/protobuf" - packages = ["proto"] - revision = "342cbe0a04158f6dcb03ca0079991a51a4248c02" - version = "v0.5" - -[[projects]] - name = "github.com/golang/mock" - packages = ["gomock"] - revision = "13f360950a79f5864a972c786a10a50e44b69541" - version = "v1.0.0" - -[[projects]] - branch = "master" - name = "github.com/hashicorp/golang-lru" - packages = [ - ".", - "simplelru" - ] - revision = "0a025b7e63adc15a622f29b0b2c4c3848243bbf6" - -[[projects]] - branch = "master" - name = "github.com/jmoiron/sqlx" - packages = [ - ".", - "reflectx" - ] - revision = "3379e5993990b1f927fc8db926485e6f6becf2d2" - -[[projects]] - branch = "master" - name = "github.com/lib/pq" - packages = [ - ".", - "oid" - ] - revision = "83612a56d3dd153a94a629cd64925371c9adad78" - -[[projects]] - name = "github.com/opencontainers/go-digest" - packages = ["."] - revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf" - version = "v1.0.0-rc1" - -[[projects]] - name = "github.com/opencontainers/image-spec" - packages = [ - "specs-go", - "specs-go/v1" - ] - revision = "d60099175f88c47cd379c4738d158884749ed235" - version = "v1.0.1" - -[[projects]] - name = "github.com/opencontainers/runc" - packages = [ - "libcontainer/system", - "libcontainer/user" - ] - revision = "baf6536d6259209c3edfa2b22237af82942d3dfa" - version = "v0.1.1" - -[[projects]] - name = "github.com/ory/dockertest" - packages = [ - ".", - "docker", - "docker/opts", - "docker/pkg/archive", - "docker/pkg/fileutils", - "docker/pkg/homedir", - "docker/pkg/idtools", - "docker/pkg/ioutils", - "docker/pkg/jsonmessage", - "docker/pkg/longpath", - "docker/pkg/mount", - "docker/pkg/pools", - "docker/pkg/stdcopy", - "docker/pkg/system", - "docker/pkg/term", - "docker/pkg/term/windows", - "docker/types", - "docker/types/blkiodev", - "docker/types/container", - "docker/types/filters", - "docker/types/mount", - "docker/types/network", - "docker/types/registry", - "docker/types/strslice", - "docker/types/versions" - ] - revision = "9bca068bf5e4af2484b9c2e8cfeb3d098d5327d7" - version = "v3.3.1" - -[[projects]] - name = "github.com/ory/pagination" - packages = ["."] - revision = "abd7ec33a01fdec119267449c8f3bad187f881f6" - version = "v0.0.1" - -[[projects]] - name = "github.com/ory/sqlcon" - packages = ["dockertest"] - revision = "7cb5a0f3099d9596e71128202e4bd54405927dfa" - version = "v0.0.3" - -[[projects]] - name = "github.com/pborman/uuid" - packages = ["."] - revision = "e790cca94e6cc75c7064b1332e63811d4aae1a53" - version = "v1.1" - -[[projects]] - name = "github.com/pkg/errors" - packages = ["."] - revision = "645ef00459ed84a119197bfb8d8205042c6df63d" - version = "v0.8.0" - -[[projects]] - name = "github.com/pmezard/go-difflib" - packages = ["difflib"] - revision = "792786c7400a136282c1664665ae0a8db921c6c2" - version = "v1.0.0" - -[[projects]] - branch = "master" - name = "github.com/rubenv/sql-migrate" - packages = [ - ".", - "sqlparse" - ] - revision = "6edbfbd6a369ee82463312ec67b2c92f97a1d4dc" - -[[projects]] - name = "github.com/sirupsen/logrus" - packages = ["."] - revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc" - version = "v1.0.5" - -[[projects]] - name = "github.com/stretchr/testify" - packages = [ - "assert", - "require" - ] - revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0" - version = "v1.1.4" - -[[projects]] - branch = "master" - name = "golang.org/x/crypto" - packages = ["ssh/terminal"] - revision = "b080dc9a8c480b08e698fb1219160d598526310f" - -[[projects]] - branch = "master" - name = "golang.org/x/net" - packages = [ - "context", - "context/ctxhttp" - ] - revision = "c7086645de248775cbf2373cf5ca4d2fa664b8c1" - -[[projects]] - branch = "master" - name = "golang.org/x/sys" - packages = [ - "unix", - "windows" - ] - revision = "4ff8c001ce4cc464e644b922325097228fce14d8" - -[[projects]] - name = "gopkg.in/gorp.v1" - packages = ["."] - revision = "c87af80f3cc5036b55b83d77171e156791085e2e" - version = "v1.7.1" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - inputs-digest = "d412c12bcd4f082e633f5c0e547fd745ad6d93c33099d7cc9f14a55b44847cab" - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index feda468..0000000 --- a/Gopkg.toml +++ /dev/null @@ -1,62 +0,0 @@ - -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" - - -[[constraint]] - name = "github.com/go-sql-driver/mysql" - version = "1.3.0" - -[[constraint]] - name = "github.com/golang/mock" - version = "1.0.0" - -[[constraint]] - branch = "master" - name = "github.com/hashicorp/golang-lru" - -[[constraint]] - branch = "master" - name = "github.com/jmoiron/sqlx" - -[[constraint]] - branch = "master" - name = "github.com/lib/pq" - -[[constraint]] - name = "github.com/ory/dockertest" - version = "3.1.0" - -[[constraint]] - name = "github.com/pborman/uuid" - version = "1.1.0" - -[[constraint]] - name = "github.com/pkg/errors" - version = "0.8.0" - -[[constraint]] - branch = "master" - name = "github.com/rubenv/sql-migrate" - -[[constraint]] - name = "github.com/stretchr/testify" - version = "1.1.4" diff --git a/HISTORY.md b/HISTORY.md index 36a48d1..8ecfaff 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -14,6 +14,13 @@ +## 1.0.0 + +The SQL storage implementation has been removed. The reason being that it had serious scalability issues which could +cause trouble in high-throughput environments. + +Everything else stays the same, ORY Ladon is now feature-complete. + ## 0.8.0 Managers now must implement `Update(policy Policy) error`. diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 9209e88..0000000 --- a/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,11 +0,0 @@ -Before creating your pull request, make sure that you have -[signed the DOC](https://github.com/ory/ladon/blob/master/CONTRIBUTING.md#developers-certificate-of-origin). You can ammend your -signature to the current commit using `git commit --amend -s`. - -Please create PRs only for bugs, or features that have been discussed with the maintainers, either at the -[ORY Community](https://community.ory.am/) or join the [ORY Chat](https://gitter.im/ory-am/hydra). - -If you think you found a security vulnerability, please refrain from posting it publicly on the forums, the chat, or GitHub -and send us an email to [hi@ory.am](mailto:hi@ory.am) instead. - -Please remove this template if you completed the steps from above. diff --git a/README.md b/README.md index 888714e..1fd87bd 100644 --- a/README.md +++ b/README.md @@ -10,27 +10,16 @@ [Ladon](https://en.wikipedia.org/wiki/Ladon_%28mythology%29) is the serpent dragon protecting your resources. ---- - -This repository has been archived. This has several reasons: - -- The SDK is feature-complete. -- We encourage you to use [ORY Keto](https://github.com/ory/keto) which exposes the same capabilities as ORY Ladon via a REST API. -- We are no longer using ORY Ladon internally. - -You can still use this library, just be aware that we are not supporting it any more. If you do use this library, we discourage you from using the SQL datastore implementation as [it has issues when many writes occur](https://github.com/ory/keto/issues/30) and may become slow when many policies are stored. - -If you are just getting started, we highly recommend checking out [ORY Keto](https://github.com/ory/keto). It works out of the box, implements several access control patterns (RBAC, ACL, ...), has better reporting and performance. - ---- - Ladon is a library written in [Go](https://golang.org) for access control policies, similar to [Role Based Access Control](https://en.wikipedia.org/wiki/Role-based_access_control) or [Access Control Lists](https://en.wikipedia.org/wiki/Access_control_list). In contrast to [ACL](https://en.wikipedia.org/wiki/Access_control_list) and [RBAC](https://en.wikipedia.org/wiki/Role-based_access_control) you get fine-grained access control with the ability to answer questions in complex environments such as multi-tenant or distributed applications and large organizations. Ladon is inspired by [AWS IAM Policies](http://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html). -Ladon officially ships with storage adapters for SQL (officially supported: MySQL 5.5+, PostgreSQL 9.2+) and in-memory. Community adapters are available for [CockroachDB](https://github.com/wehco/ladon-crdb). +Ladon officially ships with an exemplary in-memory storage implementations. +Community-supported adapters are available for [CockroachDB](https://github.com/wehco/ladon-crdb). + +Ladon is now considered stable. --- @@ -76,12 +65,14 @@ Please refer to [ory-am/dockertest](https://github.com/ory-am/dockertest) for mo ## Installation +This library works with Go 1.11+. + ``` +export GO111MODULE=on go get github.com/ory/ladon ``` -We recommend to use [Dep](https://github.com/golang/dep) for dependency management. Ladon uses [semantic -versioning](http://semver.org/) and versions beginning with zero (`0.1.2`) might introduce backwards compatibility +Ladon uses [semantic versioning](http://semver.org/) and versions beginning with zero (`0.1.2`) might introduce backwards compatibility breaks with [each minor version](http://semver.org/#how-should-i-deal-with-revisions-in-the-0yz-initial-development-phase). ## Concepts @@ -604,8 +595,8 @@ func main() { #### Persistence Obviously, creating such a policy is not enough. You want to persist it too. Ladon ships an interface `ladon.Manager` for -this purpose with default implementations for In-Memory and SQL (PostgreSQL, MySQL) via [sqlx](github.com/jmoiron/sqlx). -There are also adapters available written by the community [for Redis and RethinkDB](https://github.com/ory/ladon-community) +this purpose. You have to implement that interface for persistence. An exemplary in-memory adapter can be found in +[./manager/memory/manager_memory.go](./manager/memory/manager_memory.go): Let's take a look how to instantiate those: @@ -628,49 +619,6 @@ func main() { } ``` -**SQL** (officially supported) - -```go -import "github.com/ory/ladon" -import manager "github.com/ory/ladon/manager/sql" -import "github.com/jmoiron/sqlx" -import _ "github.com/go-sql-driver/mysql" - -func main() { - // The database manager expects a sqlx.DB object - // - // For MySQL, be sure to include parseTime=true in the connection string - // You can find all of the supported MySQL connection string options for the - // driver at: https://github.com/go-sql-driver/mysql - // - db, err = sqlx.Open("mysql", "user:pass@tcp(127.0.0.1:3306)/?parseTime=true") - // Or, if using postgres: - // import _ "github.com/lib/pq" - // - // db, err = sqlx.Open("postgres", "postgres://foo:bar@localhost/ladon") - if err != nil { - log.Fatalf("Could not connect to database: %s", err) - } - - warden := &ladon.Ladon{ - Manager: manager.NewSQLManager(db, nil), - } - - // You must call SQLManager.CreateSchemas(schema, table) before use - // to apply the necessary SQL migrations - // - // You can provide your own schema and table name or pass - // empty strings to use the default - n, err := warden.Manager.CreateSchemas("", "") - if err != nil { - log.Fatalf("Failed to create schemas: %s", err) - } - log.Printf("applied %d migrations", n) - - // ... -} -``` - ### Access Control (Warden) Now that we have defined our policies, we can use the warden to check if a request is valid. diff --git a/audit_logger_test.go b/audit_logger_test.go index e9c3897..7ebdfef 100644 --- a/audit_logger_test.go +++ b/audit_logger_test.go @@ -25,9 +25,10 @@ import ( "log" "testing" + "github.com/stretchr/testify/assert" + . "github.com/ory/ladon" . "github.com/ory/ladon/manager/memory" - "github.com/stretchr/testify/assert" ) func TestAuditLogger(t *testing.T) { diff --git a/benchmark_warden_test.go b/benchmark_warden_test.go index a7fca9b..572a917 100644 --- a/benchmark_warden_test.go +++ b/benchmark_warden_test.go @@ -25,10 +25,11 @@ import ( "strconv" "testing" - "github.com/ory/ladon" - "github.com/ory/ladon/manager/memory" "github.com/pborman/uuid" "github.com/pkg/errors" + + "github.com/ory/ladon" + "github.com/ory/ladon/manager/memory" ) func benchmarkLadon(i int, b *testing.B, warden *ladon.Ladon) { diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b776e73 --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module github.com/ory/ladon + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/golang/mock v1.1.1 + github.com/hashicorp/golang-lru v0.5.0 + github.com/ory/pagination v0.0.1 + github.com/pborman/uuid v1.2.0 + github.com/pkg/errors v0.8.0 + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.2.2 + golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..a1cf9bf --- /dev/null +++ b/go.sum @@ -0,0 +1,20 @@ +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.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/ory/pagination v0.0.1 h1:Zp+0n/UXSGYlJAMN0BuRjZhULsQRebGHfqByKtZXNYI= +github.com/ory/pagination v0.0.1/go.mod h1:d1ToRROAUleriPhmb2dYbhANhhLwZ8s395m2yJCDFh8= +github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 h1:x6rhz8Y9CjbgQkccRGmELH6K+LJj7tOoh3XWeC1yaQM= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/integration/integration.go b/integration/integration.go deleted file mode 100644 index 8549947..0000000 --- a/integration/integration.go +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright © 2016-2018 Aeneas Rekkas - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @author Aeneas Rekkas - * @copyright 2015-2018 Aeneas Rekkas - * @license Apache-2.0 - */ - -package integration - -import ( - "fmt" - "log" - "time" - - _ "github.com/go-sql-driver/mysql" - "github.com/jmoiron/sqlx" - _ "github.com/lib/pq" - "github.com/ory/dockertest" -) - -var resources []*dockertest.Resource -var pool *dockertest.Pool - -func KillAll() { - for _, resource := range resources { - pool.Purge(resource) - } - resources = []*dockertest.Resource{} -} - -func ConnectToMySQL() *sqlx.DB { - var db *sqlx.DB - var err error - pool, err = dockertest.NewPool("") - pool.MaxWait = time.Minute * 2 - if err != nil { - log.Fatalf("Could not connect to docker: %s", err) - } - - resource, err := pool.Run("mysql", "5.6", []string{"MYSQL_ROOT_PASSWORD=secret"}) - if err != nil { - log.Fatalf("Could not start resource: %s", err) - } - - if err = pool.Retry(func() error { - var err error - db, err = sqlx.Open("mysql", fmt.Sprintf("root:secret@(localhost:%s)/mysql?parseTime=true", resource.GetPort("3306/tcp"))) - if err != nil { - return err - } - return db.Ping() - }); err != nil { - log.Fatalf("Could not connect to docker: %s", err) - } - - resources = append(resources, resource) - return db -} - -func ConnectToPostgres(database string) *sqlx.DB { - var db *sqlx.DB - var err error - pool, err = dockertest.NewPool("") - if err != nil { - log.Fatalf("Could not connect to docker: %s", err) - } - - resource, err := pool.Run("postgres", "9.2", []string{"POSTGRES_PASSWORD=secret", "POSTGRES_DB=" + database}) - if err != nil { - log.Fatalf("Could not start resource: %s", err) - } - - if err = pool.Retry(func() error { - var err error - db, err = sqlx.Open("postgres", fmt.Sprintf("postgres://postgres:secret@localhost:%s/%s?sslmode=disable", resource.GetPort("5432/tcp"), database)) - if err != nil { - return err - } - return db.Ping() - }); err != nil { - log.Fatalf("Could not connect to docker: %s", err) - } - - resources = append(resources, resource) - return db -} diff --git a/ladon_test.go b/ladon_test.go index 5c212fd..bfe2e37 100644 --- a/ladon_test.go +++ b/ladon_test.go @@ -24,10 +24,11 @@ import ( "fmt" "testing" - . "github.com/ory/ladon" - . "github.com/ory/ladon/manager/memory" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + . "github.com/ory/ladon" + . "github.com/ory/ladon/manager/memory" ) // A bunch of exemplary policies diff --git a/manager/memory/manager_memory.go b/manager/memory/manager_memory.go index 4bbadb0..1cb6c89 100644 --- a/manager/memory/manager_memory.go +++ b/manager/memory/manager_memory.go @@ -23,9 +23,10 @@ package memory import ( "sync" + "github.com/pkg/errors" + . "github.com/ory/ladon" "github.com/ory/pagination" - "github.com/pkg/errors" ) // MemoryManager is an in-memory (non-persistent) implementation of Manager. diff --git a/manager/sql/databases.go b/manager/sql/databases.go deleted file mode 100644 index b2943e8..0000000 --- a/manager/sql/databases.go +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright © 2016-2018 Aeneas Rekkas - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @author Aeneas Rekkas - * @copyright 2015-2018 Aeneas Rekkas - * @license Apache-2.0 - */ - -package sql - -import ( - "fmt" - - migrate "github.com/rubenv/sql-migrate" -) - -type Statements struct { - Migrations *migrate.MemoryMigrationSource - QueryInsertPolicy string - QueryInsertPolicyActions string - QueryInsertPolicyActionsRel string - QueryInsertPolicyResources string - QueryInsertPolicyResourcesRel string - QueryInsertPolicySubjects string - QueryInsertPolicySubjectsRel string - QueryPoliciesForSubject string - QueryPoliciesForResource string -} - -func createQueryPolicies(objectToQueryFor, db string) string { - if objectToQueryFor != "subject" && objectToQueryFor != "resource" { - panic(fmt.Sprintf("Cannot call createQueryPolicies for %s", objectToQueryFor)) - } - var whereClause string - if db == "postgres" { - whereClause = fmt.Sprintf(` - WHERE - (%[1]s.has_regex IS NOT TRUE AND %[1]s.template = $1) - OR - (%[1]s.has_regex IS TRUE AND $2 ~ %[1]s.compiled)`, - objectToQueryFor) - } else if db == "mysql" { - whereClause = fmt.Sprintf(` - WHERE - (%[1]s.has_regex = 0 AND %[1]s.template = ?) - OR - (%[1]s.has_regex = 1 AND ? REGEXP BINARY %[1]s.compiled)`, - objectToQueryFor) - } else { - panic(fmt.Sprintf("Cannot call createQueryPolicies for db:%s", db)) - } - return ` - SELECT - p.id, - p.effect, - p.conditions, - p.description, - p.meta, - subject.template AS subject, - resource.template AS resource, - action.template AS action - FROM - ladon_policy AS p - - INNER JOIN ladon_policy_subject_rel AS rs ON rs.policy = p.id - LEFT JOIN ladon_policy_action_rel AS ra ON ra.policy = p.id - LEFT JOIN ladon_policy_resource_rel AS rr ON rr.policy = p.id - - INNER JOIN ladon_subject AS subject ON rs.subject = subject.id - LEFT JOIN ladon_action AS action ON ra.action = action.id - LEFT JOIN ladon_resource AS resource ON rr.resource = resource.id` + whereClause -} - -var sharedMigrations = []*migrate.Migration{ - { - Id: "1", - Up: []string{ - `CREATE TABLE IF NOT EXISTS ladon_policy ( - id varchar(255) NOT NULL PRIMARY KEY, - description text NOT NULL, - effect text NOT NULL CHECK (effect='allow' OR effect='deny'), - conditions text NOT NULL - )`, - `CREATE TABLE IF NOT EXISTS ladon_policy_subject ( - compiled text NOT NULL, - template varchar(1023) NOT NULL, - policy varchar(255) NOT NULL, - FOREIGN KEY (policy) REFERENCES ladon_policy(id) ON DELETE CASCADE - )`, - `CREATE TABLE IF NOT EXISTS ladon_policy_permission ( - compiled text NOT NULL, - template varchar(1023) NOT NULL, - policy varchar(255) NOT NULL, - FOREIGN KEY (policy) REFERENCES ladon_policy(id) ON DELETE CASCADE - )`, - `CREATE TABLE IF NOT EXISTS ladon_policy_resource ( - compiled text NOT NULL, - template varchar(1023) NOT NULL, - policy varchar(255) NOT NULL, - FOREIGN KEY (policy) REFERENCES ladon_policy(id) ON DELETE CASCADE - )`, - }, - Down: []string{ - "DROP TABLE ladon_policy", - "DROP TABLE ladon_policy_subject", - "DROP TABLE ladon_policy_permission", - "DROP TABLE ladon_policy_resource", - }, - }, - { - Id: "2", - Up: []string{ - `CREATE TABLE IF NOT EXISTS ladon_subject ( - id varchar(64) NOT NULL PRIMARY KEY, - has_regex bool NOT NULL, - compiled varchar(511) NOT NULL UNIQUE, - template varchar(511) NOT NULL UNIQUE - )`, - `CREATE TABLE IF NOT EXISTS ladon_action ( - id varchar(64) NOT NULL PRIMARY KEY, - has_regex bool NOT NULL, - compiled varchar(511) NOT NULL UNIQUE, - template varchar(511) NOT NULL UNIQUE - )`, - `CREATE TABLE IF NOT EXISTS ladon_resource ( - id varchar(64) NOT NULL PRIMARY KEY, - has_regex bool NOT NULL, - compiled varchar(511) NOT NULL UNIQUE, - template varchar(511) NOT NULL UNIQUE - )`, - `CREATE TABLE IF NOT EXISTS ladon_policy_subject_rel ( - policy varchar(255) NOT NULL, - subject varchar(64) NOT NULL, - PRIMARY KEY (policy, subject), - FOREIGN KEY (policy) REFERENCES ladon_policy(id) ON DELETE CASCADE, - FOREIGN KEY (subject) REFERENCES ladon_subject(id) ON DELETE CASCADE - )`, - `CREATE TABLE IF NOT EXISTS ladon_policy_action_rel ( - policy varchar(255) NOT NULL, - action varchar(64) NOT NULL, - PRIMARY KEY (policy, action), - FOREIGN KEY (policy) REFERENCES ladon_policy(id) ON DELETE CASCADE, - FOREIGN KEY (action) REFERENCES ladon_action(id) ON DELETE CASCADE - )`, - `CREATE TABLE IF NOT EXISTS ladon_policy_resource_rel ( - policy varchar(255) NOT NULL, - resource varchar(64) NOT NULL, - PRIMARY KEY (policy, resource), - FOREIGN KEY (policy) REFERENCES ladon_policy(id) ON DELETE CASCADE, - FOREIGN KEY (resource) REFERENCES ladon_resource(id) ON DELETE CASCADE - )`, - }, - Down: []string{}, - }, -} - -var Migrations = map[string]Statements{ - "postgres": { - Migrations: &migrate.MemoryMigrationSource{ - Migrations: []*migrate.Migration{ - sharedMigrations[0], - sharedMigrations[1], - { - Id: "3", - Up: []string{ - "CREATE INDEX ladon_subject_compiled_idx ON ladon_subject (compiled text_pattern_ops)", - "CREATE INDEX ladon_permission_compiled_idx ON ladon_action (compiled text_pattern_ops)", - "CREATE INDEX ladon_resource_compiled_idx ON ladon_resource (compiled text_pattern_ops)", - }, - Down: []string{ - "DROP INDEX ladon_subject_compiled_idx", - "DROP INDEX ladon_permission_compiled_idx", - "DROP INDEX ladon_resource_compiled_idx", - }, - }, - { - Id: "4", - Up: []string{ - "ALTER TABLE ladon_policy ADD COLUMN meta json", - }, - Down: []string{ - "ALTER TABLE ladon_policy DROP COLUMN IF EXISTS meta", - }, - }, - }, - }, - QueryInsertPolicy: `INSERT INTO ladon_policy(id, description, effect, conditions, meta) SELECT $1::varchar, $2, $3, $4, $5 WHERE NOT EXISTS (SELECT 1 FROM ladon_policy WHERE id = $1)`, - QueryInsertPolicyActions: `INSERT INTO ladon_action (id, template, compiled, has_regex) SELECT $1::varchar, $2, $3, $4 WHERE NOT EXISTS (SELECT 1 FROM ladon_action WHERE id = $1)`, - QueryInsertPolicyActionsRel: `INSERT INTO ladon_policy_action_rel (policy, action) SELECT $1::varchar, $2::varchar WHERE NOT EXISTS (SELECT 1 FROM ladon_policy_action_rel WHERE policy = $1 AND action = $2)`, - QueryInsertPolicyResources: `INSERT INTO ladon_resource (id, template, compiled, has_regex) SELECT $1::varchar, $2, $3, $4 WHERE NOT EXISTS (SELECT 1 FROM ladon_resource WHERE id = $1)`, - QueryInsertPolicyResourcesRel: `INSERT INTO ladon_policy_resource_rel (policy, resource) SELECT $1::varchar, $2::varchar WHERE NOT EXISTS (SELECT 1 FROM ladon_policy_resource_rel WHERE policy = $1 AND resource = $2)`, - QueryInsertPolicySubjects: `INSERT INTO ladon_subject (id, template, compiled, has_regex) SELECT $1::varchar, $2, $3, $4 WHERE NOT EXISTS (SELECT 1 FROM ladon_subject WHERE id = $1)`, - QueryInsertPolicySubjectsRel: `INSERT INTO ladon_policy_subject_rel (policy, subject) SELECT $1::varchar, $2::varchar WHERE NOT EXISTS (SELECT 1 FROM ladon_policy_subject_rel WHERE policy = $1 AND subject = $2)`, - QueryPoliciesForSubject: createQueryPolicies("subject", "postgres"), - QueryPoliciesForResource: createQueryPolicies("resource", "postgres"), - }, - "mysql": { - Migrations: &migrate.MemoryMigrationSource{ - Migrations: []*migrate.Migration{ - sharedMigrations[0], - sharedMigrations[1], - { - Id: "3", - Up: []string{ - "CREATE FULLTEXT INDEX ladon_subject_compiled_idx ON ladon_subject (compiled)", - "CREATE FULLTEXT INDEX ladon_action_compiled_idx ON ladon_action (compiled)", - "CREATE FULLTEXT INDEX ladon_resource_compiled_idx ON ladon_resource (compiled)", - }, - Down: []string{ - "DROP INDEX ladon_subject_compiled_idx", - "DROP INDEX ladon_permission_compiled_idx", - "DROP INDEX ladon_resource_compiled_idx", - }, - }, - { - Id: "4", - Up: []string{ - "ALTER TABLE ladon_policy ADD COLUMN meta text", - }, - Down: []string{ - "ALTER TABLE ladon_policy DROP COLUMN meta", - }, - }, - }, - }, - QueryInsertPolicy: `INSERT IGNORE INTO ladon_policy (id, description, effect, conditions, meta) VALUES(?,?,?,?,?)`, - QueryInsertPolicyActions: `INSERT IGNORE INTO ladon_action (id, template, compiled, has_regex) VALUES(?,?,?,?)`, - QueryInsertPolicyActionsRel: `INSERT IGNORE INTO ladon_policy_action_rel (policy, action) VALUES(?,?)`, - QueryInsertPolicyResources: `INSERT IGNORE INTO ladon_resource (id, template, compiled, has_regex) VALUES(?,?,?,?)`, - QueryInsertPolicyResourcesRel: `INSERT IGNORE INTO ladon_policy_resource_rel (policy, resource) VALUES(?,?)`, - QueryInsertPolicySubjects: `INSERT IGNORE INTO ladon_subject (id, template, compiled, has_regex) VALUES(?,?,?,?)`, - QueryInsertPolicySubjectsRel: `INSERT IGNORE INTO ladon_policy_subject_rel (policy, subject) VALUES(?,?)`, - QueryPoliciesForSubject: createQueryPolicies("subject", "mysql"), - QueryPoliciesForResource: createQueryPolicies("resource", "mysql"), - }, -} diff --git a/manager/sql/manager_sql.go b/manager/sql/manager_sql.go deleted file mode 100644 index 2ebc077..0000000 --- a/manager/sql/manager_sql.go +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright © 2016-2018 Aeneas Rekkas - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @author Aeneas Rekkas - * @copyright 2015-2018 Aeneas Rekkas - * @license Apache-2.0 - */ - -package sql - -import ( - "crypto/sha256" - "database/sql" - "encoding/json" - "fmt" - "strings" - - "github.com/jmoiron/sqlx" - . "github.com/ory/ladon" - "github.com/ory/ladon/compiler" - "github.com/pkg/errors" - "github.com/rubenv/sql-migrate" -) - -// SQLManager is a postgres implementation for Manager to store policies persistently. -type SQLManager struct { - db *sqlx.DB - database string -} - -// NewSQLManager initializes a new SQLManager for given db instance. -func NewSQLManager(db *sqlx.DB, schema []string) *SQLManager { - database := db.DriverName() - switch database { - case "pgx", "pq": - database = "postgres" - } - - return &SQLManager{ - db: db, - database: database, - } -} - -// CreateSchemas creates ladon_policy tables -func (s *SQLManager) CreateSchemas(schema, table string) (int, error) { - if _, ok := Migrations[s.database]; !ok { - return 0, errors.Errorf("Database %s is not supported", s.database) - } - - source := Migrations[s.database].Migrations - - migrate.SetSchema(schema) - migrate.SetTable(table) - n, err := migrate.Exec(s.db.DB, s.database, source, migrate.Up) - if err != nil { - return 0, errors.Wrapf(err, "Could not migrate sql schema, applied %d migrations", n) - } - return n, nil -} - -// Update updates an existing policy. -func (s *SQLManager) Update(policy Policy) error { - tx, err := s.db.Beginx() - if err != nil { - return errors.WithStack(err) - } - - if err := s.delete(policy.GetID(), tx); err != nil { - if rollErr := tx.Rollback(); rollErr != nil { - return errors.Wrap(err, rollErr.Error()) - } - return errors.WithStack(err) - } - - if err := s.create(policy, tx); err != nil { - if rollErr := tx.Rollback(); rollErr != nil { - return errors.Wrap(err, rollErr.Error()) - } - return errors.WithStack(err) - } - - if err = tx.Commit(); err != nil { - if rollErr := tx.Rollback(); rollErr != nil { - return errors.Wrap(err, rollErr.Error()) - } - return errors.WithStack(err) - } - - return nil -} - -// Create inserts a new policy -func (s *SQLManager) Create(policy Policy) (err error) { - tx, err := s.db.Beginx() - if err != nil { - return errors.WithStack(err) - } - - if err := s.create(policy, tx); err != nil { - if rollErr := tx.Rollback(); rollErr != nil { - return errors.Wrap(err, rollErr.Error()) - } - return errors.WithStack(err) - } - - if err = tx.Commit(); err != nil { - if rollErr := tx.Rollback(); rollErr != nil { - return errors.Wrap(err, rollErr.Error()) - } - return errors.WithStack(err) - } - - return nil -} - -func (s *SQLManager) create(policy Policy, tx *sqlx.Tx) (err error) { - conditions := []byte("{}") - if policy.GetConditions() != nil { - cs := policy.GetConditions() - conditions, err = json.Marshal(&cs) - if err != nil { - return errors.WithStack(err) - } - } - - meta := []byte("{}") - if policy.GetMeta() != nil { - meta = policy.GetMeta() - } - - if _, ok := Migrations[s.database]; !ok { - return errors.Errorf("Database %s is not supported", s.database) - } - - if _, err = tx.Exec(s.db.Rebind(Migrations[s.database].QueryInsertPolicy), policy.GetID(), policy.GetDescription(), policy.GetEffect(), conditions, meta); err != nil { - return errors.WithStack(err) - } - - type relation struct { - p []string - t string - } - var relations = []relation{ - {p: policy.GetActions(), t: "action"}, - {p: policy.GetResources(), t: "resource"}, - {p: policy.GetSubjects(), t: "subject"}, - } - - for _, rel := range relations { - var query string - var queryRel string - - switch rel.t { - case "action": - query = Migrations[s.database].QueryInsertPolicyActions - queryRel = Migrations[s.database].QueryInsertPolicyActionsRel - case "resource": - query = Migrations[s.database].QueryInsertPolicyResources - queryRel = Migrations[s.database].QueryInsertPolicyResourcesRel - case "subject": - query = Migrations[s.database].QueryInsertPolicySubjects - queryRel = Migrations[s.database].QueryInsertPolicySubjectsRel - } - - for _, template := range rel.p { - h := sha256.New() - h.Write([]byte(template)) - id := fmt.Sprintf("%x", h.Sum(nil)) - - compiled, err := compiler.CompileRegex(template, policy.GetStartDelimiter(), policy.GetEndDelimiter()) - if err != nil { - return errors.WithStack(err) - } - - if _, err := tx.Exec(s.db.Rebind(query), id, template, compiled.String(), strings.Index(template, string(policy.GetStartDelimiter())) > -1); err != nil { - return errors.WithStack(err) - } - if _, err := tx.Exec(s.db.Rebind(queryRel), policy.GetID(), id); err != nil { - return errors.WithStack(err) - } - } - } - - return nil -} - -func (s *SQLManager) findPolicies(query string, args ...interface{}) (Policies, error) { - rows, err := s.db.Query(query, args...) - if err == sql.ErrNoRows { - return nil, NewErrResourceNotFound(err) - } else if err != nil { - return nil, errors.WithStack(err) - } - defer rows.Close() - - return scanRows(rows) -} - -func (s *SQLManager) FindRequestCandidates(r *Request) (Policies, error) { - return s.FindPoliciesForSubject(r.Subject) -} - -func (s *SQLManager) FindPoliciesForSubject(subject string) (Policies, error) { - query := Migrations[s.database].QueryPoliciesForSubject - return s.findPolicies(s.db.Rebind(query), subject, subject) -} - -func (s *SQLManager) FindPoliciesForResource(resource string) (Policies, error) { - query := Migrations[s.database].QueryPoliciesForResource - return s.findPolicies(s.db.Rebind(query), resource, resource) -} - -func scanRows(rows *sql.Rows) (Policies, error) { - var policies = map[string]*DefaultPolicy{} - - for rows.Next() { - var p DefaultPolicy - var conditions []byte - var resource, subject, action sql.NullString - p.Actions = []string{} - p.Subjects = []string{} - p.Resources = []string{} - - if err := rows.Scan(&p.ID, &p.Effect, &conditions, &p.Description, &p.Meta, &subject, &resource, &action); err == sql.ErrNoRows { - return nil, NewErrResourceNotFound(err) - } else if err != nil { - return nil, errors.WithStack(err) - } - - p.Conditions = Conditions{} - if err := json.Unmarshal(conditions, &p.Conditions); err != nil { - return nil, errors.WithStack(err) - } - - if c, ok := policies[p.ID]; ok { - if action.Valid { - policies[p.ID].Actions = append(c.Actions, action.String) - } - - if subject.Valid { - policies[p.ID].Subjects = append(c.Subjects, subject.String) - } - - if resource.Valid { - policies[p.ID].Resources = append(c.Resources, resource.String) - } - } else { - if action.Valid { - p.Actions = []string{action.String} - } - - if subject.Valid { - p.Subjects = []string{subject.String} - } - - if resource.Valid { - p.Resources = []string{resource.String} - } - - policies[p.ID] = &p - } - } - - var result = make(Policies, len(policies)) - var count int - for _, v := range policies { - v.Actions = uniq(v.Actions) - v.Resources = uniq(v.Resources) - v.Subjects = uniq(v.Subjects) - result[count] = v - count++ - } - - return result, nil -} - -var getQuery = `SELECT - p.id, p.effect, p.conditions, p.description, p.meta, - subject.template as subject, resource.template as resource, action.template as action -FROM - ladon_policy as p - -LEFT JOIN ladon_policy_subject_rel as rs ON rs.policy = p.id -LEFT JOIN ladon_policy_action_rel as ra ON ra.policy = p.id -LEFT JOIN ladon_policy_resource_rel as rr ON rr.policy = p.id - -LEFT JOIN ladon_subject as subject ON rs.subject = subject.id -LEFT JOIN ladon_action as action ON ra.action = action.id -LEFT JOIN ladon_resource as resource ON rr.resource = resource.id - -WHERE p.id=?` - -var getAllQuery = `SELECT - p.id, p.effect, p.conditions, p.description, p.meta, - subject.template as subject, resource.template as resource, action.template as action -FROM - (SELECT * from ladon_policy ORDER BY id LIMIT ? OFFSET ?) as p - -LEFT JOIN ladon_policy_subject_rel as rs ON rs.policy = p.id -LEFT JOIN ladon_policy_action_rel as ra ON ra.policy = p.id -LEFT JOIN ladon_policy_resource_rel as rr ON rr.policy = p.id - -LEFT JOIN ladon_subject as subject ON rs.subject = subject.id -LEFT JOIN ladon_action as action ON ra.action = action.id -LEFT JOIN ladon_resource as resource ON rr.resource = resource.id` - -// GetAll returns all policies -func (s *SQLManager) GetAll(limit, offset int64) (Policies, error) { - query := s.db.Rebind(getAllQuery) - - rows, err := s.db.Query(query, limit, offset) - if err != nil { - return nil, errors.WithStack(err) - } - defer rows.Close() - - return scanRows(rows) -} - -// Get retrieves a policy. -func (s *SQLManager) Get(id string) (Policy, error) { - query := s.db.Rebind(getQuery) - - rows, err := s.db.Query(query, id) - if err == sql.ErrNoRows { - return nil, NewErrResourceNotFound(err) - } else if err != nil { - return nil, errors.WithStack(err) - } - defer rows.Close() - - policies, err := scanRows(rows) - if err != nil { - return nil, errors.WithStack(err) - } else if len(policies) == 0 { - return nil, NewErrResourceNotFound(sql.ErrNoRows) - } - - return policies[0], nil -} - -// Delete removes a policy. -func (s *SQLManager) Delete(id string) error { - tx, err := s.db.Beginx() - if err != nil { - return errors.WithStack(err) - } - - if err := s.delete(id, tx); err != nil { - if rollErr := tx.Rollback(); rollErr != nil { - return errors.Wrap(err, rollErr.Error()) - } - return errors.WithStack(err) - } - - if err = tx.Commit(); err != nil { - if rollErr := tx.Rollback(); rollErr != nil { - return errors.Wrap(err, rollErr.Error()) - } - return errors.WithStack(err) - } - - return nil -} - -// Delete removes a policy. -func (s *SQLManager) delete(id string, tx *sqlx.Tx) error { - _, err := tx.Exec(s.db.Rebind("DELETE FROM ladon_policy WHERE id=?"), id) - return errors.WithStack(err) -} - -func uniq(input []string) []string { - u := make([]string, 0, len(input)) - m := make(map[string]bool) - - for _, val := range input { - if _, ok := m[val]; !ok { - m[val] = true - u = append(u, val) - } - } - - return u -} diff --git a/manager/sql/manager_sql_migration_0_5_to_0_6.go b/manager/sql/manager_sql_migration_0_5_to_0_6.go deleted file mode 100644 index 58647b2..0000000 --- a/manager/sql/manager_sql_migration_0_5_to_0_6.go +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright © 2016-2018 Aeneas Rekkas - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @author Aeneas Rekkas - * @copyright 2015-2018 Aeneas Rekkas - * @license Apache-2.0 - */ - -package sql - -import ( - "database/sql" - "encoding/json" - "fmt" - "log" - - "github.com/jmoiron/sqlx" - "github.com/ory/ladon" - . "github.com/ory/ladon" - "github.com/ory/ladon/compiler" - "github.com/pkg/errors" -) - -type SQLManagerMigrateFromMajor0Minor6ToMajor0Minor7 struct { - DB *sqlx.DB - SQLManager *SQLManager -} - -func (s *SQLManagerMigrateFromMajor0Minor6ToMajor0Minor7) GetManager() ladon.Manager { - return s.SQLManager -} - -// Get retrieves a policy. -func (s *SQLManagerMigrateFromMajor0Minor6ToMajor0Minor7) Migrate() error { - rows, err := s.DB.Query(s.DB.Rebind("SELECT id, description, effect, conditions, meta FROM ladon_policy")) - if err != nil { - return errors.WithStack(err) - } - defer rows.Close() - - var pols = Policies{} - for rows.Next() { - var p DefaultPolicy - var conditions []byte - - if err := rows.Scan(&p.ID, &p.Description, &p.Effect, &conditions, &p.Meta); err != nil { - return errors.WithStack(err) - } - - p.Conditions = Conditions{} - if err := json.Unmarshal(conditions, &p.Conditions); err != nil { - return errors.WithStack(err) - } - - subjects, err := getLinkedSQL(s.DB, "ladon_policy_subject", p.GetID()) - if err != nil { - return errors.WithStack(err) - } - permissions, err := getLinkedSQL(s.DB, "ladon_policy_permission", p.GetID()) - if err != nil { - return errors.WithStack(err) - } - resources, err := getLinkedSQL(s.DB, "ladon_policy_resource", p.GetID()) - if err != nil { - return errors.WithStack(err) - } - - log.Printf("[DEBUG] Found policy %s", p.GetID()) - - p.Actions = permissions - p.Subjects = subjects - p.Resources = resources - pols = append(pols, &p) - } - - log.Printf("[DEBUG] Found %d policies, migrating", len(pols)) - - for _, p := range pols { - log.Printf("[DEBUG] Inserting policy %s", p.GetID()) - if err := s.SQLManager.Create(p); err != nil { - log.Printf("[DEBUG] Unable to insert policy %s: %s", p.GetID(), err) - return errors.WithStack(err) - } - } - - log.Printf("[DEBUG] Migrated %d policies successfully", len(pols)) - - return nil -} - -func getLinkedSQL(db *sqlx.DB, table, policy string) ([]string, error) { - urns := []string{} - rows, err := db.Query(db.Rebind(fmt.Sprintf("SELECT template FROM %s WHERE policy=?", table)), policy) - if err == sql.ErrNoRows { - return nil, errors.Wrap(ladon.ErrNotFound, "") - } else if err != nil { - return nil, errors.WithStack(err) - } - - defer rows.Close() - for rows.Next() { - var urn string - if err = rows.Scan(&urn); err != nil { - return []string{}, errors.WithStack(err) - } - urns = append(urns, urn) - } - return urns, nil -} - -// Create inserts a new policy -func (s *SQLManagerMigrateFromMajor0Minor6ToMajor0Minor7) Create(policy Policy) (err error) { - conditions := []byte("{}") - if policy.GetConditions() != nil { - cs := policy.GetConditions() - conditions, err = json.Marshal(&cs) - if err != nil { - return errors.WithStack(err) - } - } - - meta := []byte("{}") - if policy.GetMeta() != nil { - meta = policy.GetMeta() - } - - if tx, err := s.DB.Begin(); err != nil { - return errors.WithStack(err) - } else if _, err = tx.Exec(s.DB.Rebind("INSERT INTO ladon_policy (id, description, effect, conditions, meta) VALUES (?, ?, ?, ?, ?)"), policy.GetID(), policy.GetDescription(), policy.GetEffect(), conditions, meta); err != nil { - if err := tx.Rollback(); err != nil { - return errors.WithStack(err) - } - return errors.WithStack(err) - } else if err = createLinkSQL(s.DB, tx, "ladon_policy_subject", policy, policy.GetSubjects()); err != nil { - return err - } else if err = createLinkSQL(s.DB, tx, "ladon_policy_permission", policy, policy.GetActions()); err != nil { - return err - } else if err = createLinkSQL(s.DB, tx, "ladon_policy_resource", policy, policy.GetResources()); err != nil { - return err - } else if err = tx.Commit(); err != nil { - if err := tx.Rollback(); err != nil { - return errors.WithStack(err) - } - return errors.WithStack(err) - } - - return nil -} - -func createLinkSQL(db *sqlx.DB, tx *sql.Tx, table string, p Policy, templates []string) error { - for _, template := range templates { - reg, err := compiler.CompileRegex(template, p.GetStartDelimiter(), p.GetEndDelimiter()) - - // Execute SQL statement - query := db.Rebind(fmt.Sprintf("INSERT INTO %s (policy, template, compiled) VALUES (?, ?, ?)", table)) - if _, err = tx.Exec(query, p.GetID(), template, reg.String()); err != nil { - if rb := tx.Rollback(); rb != nil { - return errors.WithStack(rb) - } - return errors.WithStack(err) - } - } - return nil -} diff --git a/manager/sql/manager_sql_test.go b/manager/sql/manager_sql_test.go deleted file mode 100644 index d3979cf..0000000 --- a/manager/sql/manager_sql_test.go +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright © 2016-2018 Aeneas Rekkas - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @author Aeneas Rekkas - * @copyright 2015-2018 Aeneas Rekkas - * @license Apache-2.0 - */ - -package sql_test - -import ( - "flag" - "fmt" - "log" - "testing" - - _ "github.com/go-sql-driver/mysql" - "github.com/jmoiron/sqlx" - _ "github.com/lib/pq" - "github.com/ory/ladon" - "github.com/ory/ladon/manager/sql" - "github.com/ory/sqlcon/dockertest" - "github.com/stretchr/testify/require" - "sync" -) - -type testDB struct { - m *sql.SQLManager - db *sqlx.DB -} - -type testQuery struct { - checkRegexState string -} - -var queries = map[string]testQuery{ - "postgres": { - checkRegexState: "SELECT COUNT(m.id) FROM %v m JOIN %v r ON m.id = r.%v WHERE r.policy = $1 AND m.has_regex != $2", - }, - "mysql": { - checkRegexState: "SELECT COUNT(m.id) FROM %v m JOIN %v r ON m.id = r.%v WHERE r.policy = ? AND m.has_regex != ?", - }, -} -var policyRelations = []string{"action", "resource", "subject"} -var managers = map[string]testDB{} -var managersMutex = &sync.Mutex{} - -func TestMain(m *testing.M) { - runner := dockertest.Register() - - flag.Parse() - if !testing.Short() { - dockertest.Parallel([]func(){ - func() { - db, err := dockertest.ConnectToTestPostgreSQL() - if err != nil { - log.Fatalf("Could not connect to PostgresSQL database: %v", err) - return - } - s := sql.NewSQLManager(db, nil) - s.CreateSchemas("", "") - managersMutex.Lock() - managers["postgres"] = testDB{m: s, db: db} - managersMutex.Unlock() - }, - func() { - db, err := dockertest.ConnectToTestMySQL() - if err != nil { - log.Fatalf("Could not connect to MySQL database: %v", err) - return - } - s := sql.NewSQLManager(db, nil) - s.CreateSchemas("", "") - managersMutex.Lock() - managers["mysql"] = testDB{m: s, db: db} - managersMutex.Unlock() - }, - }) - } - - runner.Exit(m.Run()) -} - -func TestCreateExplicitPolicy(t *testing.T) { - for k, v := range managers { - id := "test-explicit" - p := &ladon.DefaultPolicy{ - Actions: []string{"view", "edit"}, - Description: "An explicit (non-regex) test policy", - Effect: ladon.AllowAccess, - ID: id, - Resources: []string{"blog:post:1"}, - Subjects: []string{"otto"}, - } - err := v.m.Create(p) - require.NoError(t, err) - expectRegexStateForAllRelations(t, k, v, id, false) - } -} - -func TestCreateRegexPolicy(t *testing.T) { - for k, v := range managers { - id := "test-regex" - p := &ladon.DefaultPolicy{ - Actions: []string{"<(view|edit)>"}, - Description: "An regex test policy", - Effect: ladon.AllowAccess, - ID: id, - Resources: []string{"blog:post:<.*>"}, - Subjects: []string{"<(otto|hugo)>"}, - } - err := v.m.Create(p) - require.NoError(t, err) - expectRegexStateForAllRelations(t, k, v, id, true) - } -} - -func expectRegexStateForAllRelations(t *testing.T, dbType string, tdb testDB, policyID string, regexState bool) { - for _, v := range policyRelations { - expectRegexState(t, dbType, tdb, policyID, v, regexState) - } -} - -func expectRegexState(t *testing.T, dbType string, tdb testDB, policyID string, rel string, regexState bool) { - tbl := fmt.Sprintf("ladon_%v", rel) - relTbl := fmt.Sprintf("ladon_policy_%v_rel", rel) - query := fmt.Sprintf(queries[dbType].checkRegexState, tbl, relTbl, rel) - var count int - row := tdb.db.QueryRow(query, policyID, regexState) - require.NoError(t, row.Scan(&count)) - require.Equal(t, 0, count, "Expected has_regex to be %v but got %v entries with a different state", regexState, count) -} diff --git a/manager_all_test.go b/manager_all_test.go index 031ff2a..aaf9e72 100644 --- a/manager_all_test.go +++ b/manager_all_test.go @@ -22,69 +22,22 @@ package ladon_test import ( "fmt" - "log" - "os" - "sync" "testing" . "github.com/ory/ladon" - "github.com/ory/ladon/integration" . "github.com/ory/ladon/manager/memory" - . "github.com/ory/ladon/manager/sql" - "github.com/stretchr/testify/require" ) var managers = map[string]Manager{} -var migrators = map[string]ManagerMigrator{} func TestMain(m *testing.M) { - var wg sync.WaitGroup - wg.Add(3) - connectMySQL(&wg) - connectPG(&wg) - connectMEM(&wg) - wg.Wait() - - s := m.Run() - integration.KillAll() - os.Exit(s) + connectMEM() } -func connectMEM(wg *sync.WaitGroup) { - defer wg.Done() +func connectMEM() { managers["memory"] = NewMemoryManager() } -func connectPG(wg *sync.WaitGroup) { - defer wg.Done() - var db = integration.ConnectToPostgres("ladon") - s := NewSQLManager(db, nil) - if _, err := s.CreateSchemas("", ""); err != nil { - log.Fatalf("Could not create postgres schema: %v", err) - } - - managers["postgres"] = s - migrators["postgres"] = &SQLManagerMigrateFromMajor0Minor6ToMajor0Minor7{ - DB: db, - SQLManager: s, - } -} - -func connectMySQL(wg *sync.WaitGroup) { - defer wg.Done() - var db = integration.ConnectToMySQL() - s := NewSQLManager(db, nil) - if _, err := s.CreateSchemas("", ""); err != nil { - log.Fatalf("Could not create mysql schema: %v", err) - } - - managers["mysql"] = s - migrators["mysql"] = &SQLManagerMigrateFromMajor0Minor6ToMajor0Minor7{ - DB: db, - SQLManager: s, - } -} - func TestManagers(t *testing.T) { t.Run("type=get errors", func(t *testing.T) { for k, s := range managers { @@ -107,35 +60,4 @@ func TestManagers(t *testing.T) { t.Run(fmt.Sprintf("manager=%s", k), TestHelperFindPoliciesForResource(k, s)) } }) - - t.Run("type=migrate 6 to 7", func(t *testing.T) { - for k, s := range map[string]ManagerMigrator{ - "postgres": migrators["postgres"], - "mysql": migrators["mysql"], - } { - t.Run(fmt.Sprintf("manager=%s", k), func(t *testing.T) { - - // This create part is only necessary to populate the data store with some values. If you - // migrate you won't need this - for _, c := range TestManagerPolicies { - t.Run(fmt.Sprintf("create=%s", k), func(t *testing.T) { - require.NoError(t, s.Create(c)) - }) - } - - require.NoError(t, s.Migrate()) - - for _, c := range TestManagerPolicies { - t.Run(fmt.Sprintf("fetch=%s", k), func(t *testing.T) { - get, err := s.GetManager().Get(c.GetID()) - require.NoError(t, err) - - AssertPolicyEqual(t, c, get) - - require.NoError(t, s.GetManager().Delete(c.GetID())) - }) - } - }) - } - }) } diff --git a/manager_mock_test.go b/manager_mock_test.go index 59ffaaf..969d83c 100644 --- a/manager_mock_test.go +++ b/manager_mock_test.go @@ -25,6 +25,7 @@ package ladon_test import ( gomock "github.com/golang/mock/gomock" + ladon "github.com/ory/ladon" ) diff --git a/manager_test_helper.go b/manager_test_helper.go index 265710c..cde1dad 100644 --- a/manager_test_helper.go +++ b/manager_test_helper.go @@ -361,7 +361,7 @@ func testEq(a, b []string) error { } if !found { - return errors.Errorf("No match found: %s from %v in %v", i, a, b) + return errors.Errorf("No match found: %d from %v in %v", i, a, b) } } diff --git a/matcher_regexp.go b/matcher_regexp.go index 7db11c6..6357a9a 100644 --- a/matcher_regexp.go +++ b/matcher_regexp.go @@ -25,8 +25,9 @@ import ( "strings" "github.com/hashicorp/golang-lru" - "github.com/ory/ladon/compiler" "github.com/pkg/errors" + + "github.com/ory/ladon/compiler" ) func NewRegexpMatcher(size int) *RegexpMatcher { diff --git a/policy_test.go b/policy_test.go index 51df875..a5a1217 100644 --- a/policy_test.go +++ b/policy_test.go @@ -25,9 +25,10 @@ import ( "fmt" "testing" - . "github.com/ory/ladon" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + . "github.com/ory/ladon" ) var policyConditions = Conditions{ diff --git a/warden_test.go b/warden_test.go index 54cf4c3..d9abb17 100644 --- a/warden_test.go +++ b/warden_test.go @@ -25,9 +25,10 @@ import ( "testing" "github.com/golang/mock/gomock" - . "github.com/ory/ladon" "github.com/pkg/errors" "github.com/stretchr/testify/assert" + + . "github.com/ory/ladon" ) func TestWardenIsGranted(t *testing.T) {