diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 000000000..ee45064ad
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,152 @@
+# Golang CircleCI 2.0 configuration file
+#
+# Check https://circleci.com/docs/2.0/language-go/ for more details
+version: 2
+jobs:
+ format:
+ docker:
+ - image: circleci/golang:1.9
+ working_directory: /go/src/github.com/ory/keto
+ steps:
+ - checkout
+ - run: curl -L -s https://github.com/golang/dep/releases/download/v0.3.2/dep-linux-amd64 -o /go/bin/dep && chmod +x /go/bin/dep
+ - run: go get -u golang.org/x/tools/cmd/goimports
+ - run: dep ensure -vendor-only
+ - run: ./scripts/test-format.sh
+
+ swagger:
+ docker:
+ - image: circleci/golang:1.9
+ working_directory: /go/src/github.com/ory/keto
+ steps:
+ - checkout
+ - run: curl -L -s https://github.com/golang/dep/releases/download/v0.3.2/dep-linux-amd64 -o /go/bin/dep && chmod +x /go/bin/dep
+ - run: go get -u github.com/go-swagger/go-swagger/cmd/swagger golang.org/x/tools/cmd/goimports
+ - run: dep ensure -vendor-only
+ - run: ./scripts/run-genswag.sh
+
+ test:
+ docker:
+ - image: circleci/golang:1.9
+ environment:
+ - TEST_DATABASE_POSTGRESQL=postgres://test:test@localhost:5432/hydra?sslmode=disable
+ - TEST_DATABASE_MYSQL=root:test@(localhost:3306)/mysql?parseTime=true
+ - image: postgres:9.5
+ environment:
+ - POSTGRES_USER=test
+ - POSTGRES_PASSWORD=test
+ - POSTGRES_DB=hydra
+ - image: mysql:5.7
+ environment:
+ - MYSQL_ROOT_PASSWORD=test
+ working_directory: /go/src/github.com/ory/keto
+ steps:
+ - checkout
+ - run: go get -u github.com/go-swagger/go-swagger/cmd/swagger github.com/bradfitz/goimports github.com/mattn/goveralls golang.org/x/tools/cmd/cover github.com/ory/go-acc
+ - run: curl -L -s https://github.com/golang/dep/releases/download/v0.3.2/dep-linux-amd64 -o /go/bin/dep && chmod +x /go/bin/dep
+
+ # Installation
+ - run: curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
+ - run: dep ensure -vendor-only
+ - run: go install github.com/ory/keto
+
+ # Tests
+ - run: go-acc -o coverage.txt $(glide novendor)
+ - run: go test -race -short $(glide novendor | grep -v cmd)
+
+ # Submit coverage details
+ # - run: goveralls -service=circle-ci -coverprofile=coverage.txt -repotoken=$COVERALLS_REPO_TOKEN
+
+ test-docker:
+ docker:
+ - image: library/docker:17.10
+ steps:
+ - checkout
+ - setup_remote_docker:
+ version: 17.10.0-ce
+ - run: docker build -f Dockerfile -t keto-test .
+ - run: docker run keto-test version
+
+ release:
+ docker:
+ - image: circleci/golang:1.9
+ working_directory: /go/src/github.com/ory/keto
+ steps:
+ - checkout
+ - setup_remote_docker:
+ version: 17.10.0-ce
+ - run: docker build --build-arg git_tag=$(git describe --tags) --build-arg git_commit=$(git rev-parse HEAD) -f Dockerfile -t oryd/keto:$CIRCLE_TAG .
+ - run: docker login --username "$DOCKER_USERNAME" --password "$DOCKER_PASSWORD"
+ - run: docker push oryd/keto:$CIRCLE_TAG
+
+ publish-docs:
+ docker:
+ - image: alpine/git:1.0.4
+ working_directory: /go/src/github.com/ory/keto
+ steps:
+ - checkout
+ - run: git config --global user.email "hi@ory.am"
+ - run: git config --global user.name "ORY Continuous Integration"
+ - run: "git clone https://arekkas:$DOCS_TOKEN_PUSH@github.com/ory/docs.git ../docs"
+ - run: "cp ./docs/api.swagger.json ../docs/apis/keto.json"
+ - run: "(cd ../docs && git add -A && git commit -a -m \"Updates ORY Keto Swagger definitions\" && git push origin) || exit 0"
+
+ changelog:
+ docker:
+ - image: circleci/ruby:2.4-node
+ steps:
+ - checkout
+ - run: gem install github_changelog_generator
+ - run: sudo npm i -g doctoc
+ - run: github_changelog_generator -u ory -p hydra -o CHANGELOG.md --token $GITHUB_TOKEN
+ - run: doctoc CHANGELOG.md
+ - run: doctoc README.md
+ - run: git config --global user.email "circleci@ory.am"
+ - run: git config --global user.name "ORY Continuous Integration"
+ - run: git add CHANGELOG.md
+ - run: |
+ git commit -m "docs: Incorporates changes from version $(git describe --tags)" -- CHANGELOG.md
+ - run: git remote rm origin
+ - run: git remote add origin https://arekkas:$GITHUB_TOKEN@github.com/ory/keto.git
+ - run: git push origin HEAD:master
+
+workflows:
+ version: 2
+ "test, build, and relase":
+ jobs:
+ - format:
+ filters:
+ tags:
+ only: /.*/
+ - test:
+ filters:
+ tags:
+ only: /.*/
+ - swagger:
+ filters:
+ tags:
+ only: /.*/
+ - publish-docs:
+ filters:
+ branches:
+ only: master
+ - changelog:
+ filters:
+ branches:
+ only: master
+ - test-docker:
+ requires:
+ - test
+ - swagger
+ - format
+ filters:
+ tags:
+ only: /.*/
+ - release:
+ requires:
+ - test-docker
+ filters:
+ tags:
+ only: /.*/
+ branches:
+ ignore: /.*/
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..602c86f7e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+node_modules/
+vendor/
+.idea/
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 000000000..ff8687a76
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,27 @@
+FROM golang:1.9-alpine
+
+ARG git_tag
+ARG git_commit
+
+RUN apk add --no-cache git build-base curl
+RUN curl -L -s https://github.com/golang/dep/releases/download/v0.3.2/dep-linux-amd64 -o $GOPATH/bin/dep
+RUN chmod +x $GOPATH/bin/dep
+
+WORKDIR /go/src/github.com/ory/keto
+
+ADD ./Gopkg.lock ./Gopkg.lock
+ADD ./Gopkg.toml ./Gopkg.toml
+RUN dep ensure -vendor-only
+
+ADD . .
+
+RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -X github.com/ory/keto/cmd.Version=$git_tag -X github.com/ory/keto/cmd.BuildTime=`TZ=UTC date -u '+%Y-%m-%dT%H:%M:%SZ'` -X github.com/ory/keto/cmd.GitHash=$git_commit" -a -installsuffix cgo -o keto
+
+FROM scratch
+
+COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
+COPY --from=0 /go/src/github.com/ory/keto/keto /usr/bin/keto
+
+ENTRYPOINT ["keto"]
+
+CMD ["serve"]
diff --git a/Dockerfile-alpine b/Dockerfile-alpine
new file mode 100644
index 000000000..eabb5801c
--- /dev/null
+++ b/Dockerfile-alpine
@@ -0,0 +1,27 @@
+FROM golang:1.9-alpine
+
+ARG git_tag
+ARG git_commit
+
+RUN apk add --no-cache git build-base curl
+RUN curl -L -s https://github.com/golang/dep/releases/download/v0.3.2/dep-linux-amd64 -o $GOPATH/bin/dep
+RUN chmod +x $GOPATH/bin/dep
+
+WORKDIR /go/src/github.com/ory/keto
+
+ADD ./Gopkg.lock ./Gopkg.lock
+ADD ./Gopkg.toml ./Gopkg.toml
+RUN dep ensure -vendor-only
+
+ADD . .
+
+RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -X github.com/ory/keto/cmd.Version=$git_tag -X github.com/ory/keto/cmd.BuildTime=`TZ=UTC date -u '+%Y-%m-%dT%H:%M:%SZ'` -X github.com/ory/keto/cmd.GitHash=$git_commit" -a -installsuffix cgo -o keto
+
+FROM alpine:3.7
+
+COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
+COPY --from=0 /go/src/github.com/ory/keto/keto /usr/bin/keto
+
+ENTRYPOINT ["keto"]
+
+CMD ["serve"]
diff --git a/Gopkg.lock b/Gopkg.lock
new file mode 100644
index 000000000..1170c954f
--- /dev/null
+++ b/Gopkg.lock
@@ -0,0 +1,524 @@
+# 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 = "7da180ee92d8bd8bb8c37fc560e673e6557c392f"
+ version = "v0.4.7"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/Nvveen/Gotty"
+ packages = ["."]
+ revision = "cd527374f1e5bff4938207604a14f2e38a9cf512"
+
+[[projects]]
+ name = "github.com/akutz/goof"
+ packages = ["."]
+ revision = "2321ba37c3513692d6f799d91fa66f28d11c7d7a"
+ version = "v0.1.2"
+
+[[projects]]
+ name = "github.com/akutz/gotil"
+ packages = ["."]
+ revision = "6fa2e80bd3ac40f15788cfc3d12ebba49a0add92"
+ version = "v0.1.0"
+
+[[projects]]
+ name = "github.com/asaskevich/govalidator"
+ packages = ["."]
+ revision = "73945b6115bfbbcc57d89b7316e28109364124e1"
+ version = "v7"
+
+[[projects]]
+ name = "github.com/cenkalti/backoff"
+ packages = ["."]
+ revision = "2ea60e5f094469f9e65adb9cd103795b73ae743e"
+ version = "v2.0.0"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/containerd/continuity"
+ packages = ["pathdriver"]
+ revision = "3e8f2ea4b190484acb976a5b378d373429639a1a"
+
+[[projects]]
+ name = "github.com/davecgh/go-spew"
+ packages = ["spew"]
+ revision = "346938d642f2ec3594ed81d874461961cd0faa76"
+ version = "v1.1.0"
+
+[[projects]]
+ name = "github.com/dgrijalva/jwt-go"
+ packages = ["."]
+ revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
+ version = "v3.2.0"
+
+[[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/fsnotify/fsnotify"
+ packages = ["."]
+ revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
+ version = "v1.4.7"
+
+[[projects]]
+ name = "github.com/go-resty/resty"
+ packages = ["."]
+ revision = "f8815663de1e64d57cdd4ee9e2b2fa96977a030e"
+ version = "v1.4"
+
+[[projects]]
+ name = "github.com/go-sql-driver/mysql"
+ packages = ["."]
+ revision = "a0583e0143b1624142adab07e0e97fe106d99561"
+ version = "v1.3"
+
+[[projects]]
+ name = "github.com/golang/protobuf"
+ packages = ["proto"]
+ revision = "925541529c1fa6821df4e44ce2723319eb2be768"
+ version = "v1.0.0"
+
+[[projects]]
+ name = "github.com/gorilla/context"
+ packages = ["."]
+ revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a"
+ version = "v1.1"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/gtank/cryptopasta"
+ packages = ["."]
+ revision = "1f550f6f2f69009f6ae57347c188e0a67cd4e500"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/hashicorp/golang-lru"
+ packages = [
+ ".",
+ "simplelru"
+ ]
+ revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/hashicorp/hcl"
+ packages = [
+ ".",
+ "hcl/ast",
+ "hcl/parser",
+ "hcl/printer",
+ "hcl/scanner",
+ "hcl/strconv",
+ "hcl/token",
+ "json/parser",
+ "json/scanner",
+ "json/token"
+ ]
+ revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
+
+[[projects]]
+ name = "github.com/inconshreveable/mousetrap"
+ packages = ["."]
+ revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
+ version = "v1.0"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/jmoiron/sqlx"
+ packages = [
+ ".",
+ "reflectx"
+ ]
+ revision = "2aeb6a910c2b94f2d5eb53d9895d80e27264ec41"
+
+[[projects]]
+ name = "github.com/julienschmidt/httprouter"
+ packages = ["."]
+ revision = "8c199fb6259ffc1af525cc3ad52ee60ba8359669"
+ version = "v1.1"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/kardianos/osext"
+ packages = ["."]
+ revision = "ae77be60afb1dcacde03767a8c37337fad28ac14"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/lib/pq"
+ packages = [
+ ".",
+ "oid"
+ ]
+ revision = "d34b9ff171c21ad295489235aec8b6626023cd04"
+
+[[projects]]
+ name = "github.com/magiconair/properties"
+ packages = [
+ ".",
+ "assert"
+ ]
+ revision = "c3beff4c2358b44d0493c7dda585e7db7ff28ae6"
+ version = "v1.7.6"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/meatballhat/negroni-logrus"
+ packages = ["."]
+ revision = "31067281800f66f57548a7a32d9c6c5f963fef83"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/mitchellh/mapstructure"
+ packages = ["."]
+ revision = "00c29f56e2386353d58c599509e8dc3801b0d716"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/mohae/deepcopy"
+ packages = ["."]
+ revision = "c48cc78d482608239f6c4c92a4abd87eb8761c90"
+
+[[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 = "2e92e7784b6fb199fd168aa46269a2f1b34f299e"
+ version = "v3.3.0"
+
+[[projects]]
+ name = "github.com/ory/fosite"
+ packages = [
+ ".",
+ "handler/oauth2",
+ "handler/openid",
+ "handler/pkce",
+ "storage",
+ "token/hmac",
+ "token/jwt"
+ ]
+ revision = "018b5c12b71b0da443255f4a5cf0ac9543bbf9f7"
+ version = "v0.17.0"
+
+[[projects]]
+ name = "github.com/ory/graceful"
+ packages = ["."]
+ revision = "3d30c83329259f53a904d428b38d8cb8fba7bd77"
+ version = "v0.1.0"
+
+[[projects]]
+ name = "github.com/ory/herodot"
+ packages = ["."]
+ revision = "e16e86900c6c08ed085117308701a823b3da77fc"
+ version = "v0.1.3"
+
+[[projects]]
+ name = "github.com/ory/hydra"
+ packages = [
+ "pkg",
+ "rand/sequence"
+ ]
+ revision = "f6ddee8f9a2d65dfa6c02adc402c1a61fa03d4a0"
+ version = "v0.11.12"
+
+[[projects]]
+ name = "github.com/ory/ladon"
+ packages = [
+ ".",
+ "compiler",
+ "manager/memory",
+ "manager/sql"
+ ]
+ revision = "de3d0b6a2633d9d8de06e9bd6f87e01aad6d3c38"
+ version = "v0.8.9"
+
+[[projects]]
+ name = "github.com/ory/pagination"
+ packages = ["."]
+ revision = "abd7ec33a01fdec119267449c8f3bad187f881f6"
+ version = "v0.0.1"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/ory/sqlcon"
+ packages = [
+ ".",
+ "dockertest"
+ ]
+ revision = "b6d3402e52a4f1946d1ae3c8f058d6793948cc1e"
+
+[[projects]]
+ name = "github.com/pborman/uuid"
+ packages = ["."]
+ revision = "e790cca94e6cc75c7064b1332e63811d4aae1a53"
+ version = "v1.1"
+
+[[projects]]
+ name = "github.com/pelletier/go-toml"
+ packages = ["."]
+ revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8"
+ version = "v1.1.0"
+
+[[projects]]
+ name = "github.com/pkg/errors"
+ packages = ["."]
+ revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
+ version = "v0.8.0"
+
+[[projects]]
+ name = "github.com/pkg/profile"
+ packages = ["."]
+ revision = "5b67d428864e92711fcbd2f8629456121a56d91f"
+ version = "v1.2.1"
+
+[[projects]]
+ name = "github.com/pmezard/go-difflib"
+ packages = ["difflib"]
+ revision = "792786c7400a136282c1664665ae0a8db921c6c2"
+ version = "v1.0.0"
+
+[[projects]]
+ name = "github.com/rs/cors"
+ packages = ["."]
+ revision = "feef513b9575b32f84bafa580aad89b011259019"
+ version = "v1.3.0"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/rubenv/sql-migrate"
+ packages = [
+ ".",
+ "sqlparse"
+ ]
+ revision = "081fe17d19ff4e2dd9f5a0c1158e6bcf74da6906"
+
+[[projects]]
+ name = "github.com/sirupsen/logrus"
+ packages = ["."]
+ revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
+ version = "v1.0.5"
+
+[[projects]]
+ name = "github.com/spf13/afero"
+ packages = [
+ ".",
+ "mem"
+ ]
+ revision = "63644898a8da0bc22138abf860edaf5277b6102e"
+ version = "v1.1.0"
+
+[[projects]]
+ name = "github.com/spf13/cast"
+ packages = ["."]
+ revision = "8965335b8c7107321228e3e3702cab9832751bac"
+ version = "v1.2.0"
+
+[[projects]]
+ name = "github.com/spf13/cobra"
+ packages = ["."]
+ revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
+ version = "v0.0.2"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/spf13/jwalterweatherman"
+ packages = ["."]
+ revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
+
+[[projects]]
+ name = "github.com/spf13/pflag"
+ packages = ["."]
+ revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
+ version = "v1.0.1"
+
+[[projects]]
+ name = "github.com/spf13/viper"
+ packages = ["."]
+ revision = "b5e8006cbee93ec955a89ab31e0e3ce3204f3736"
+ version = "v1.0.2"
+
+[[projects]]
+ name = "github.com/square/go-jose"
+ packages = ["json"]
+ revision = "76dd09796242edb5b897103a75df2645c028c960"
+ version = "v2.1.6"
+
+[[projects]]
+ name = "github.com/stretchr/testify"
+ packages = [
+ "assert",
+ "require"
+ ]
+ revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
+ version = "v1.2.1"
+
+[[projects]]
+ name = "github.com/urfave/negroni"
+ packages = ["."]
+ revision = "5dbbc83f748fc3ad38585842b0aedab546d0ea1e"
+ version = "v0.3.0"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/crypto"
+ packages = [
+ "bcrypt",
+ "blowfish",
+ "ssh/terminal"
+ ]
+ revision = "d6449816ce06963d9d136eee5a56fca5b0616e7e"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/net"
+ packages = [
+ "context",
+ "context/ctxhttp",
+ "idna",
+ "publicsuffix"
+ ]
+ revision = "61147c48b25b599e5b561d2e9c4f3e1ef489ca41"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/oauth2"
+ packages = [
+ ".",
+ "clientcredentials",
+ "internal"
+ ]
+ revision = "921ae394b9430ed4fb549668d7b087601bd60a81"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/sys"
+ packages = [
+ "unix",
+ "windows"
+ ]
+ revision = "3b87a42e500a6dc65dae1a55d0b641295971163e"
+
+[[projects]]
+ name = "golang.org/x/text"
+ packages = [
+ "collate",
+ "collate/build",
+ "internal/colltab",
+ "internal/gen",
+ "internal/tag",
+ "internal/triegen",
+ "internal/ucd",
+ "language",
+ "secure/bidirule",
+ "transform",
+ "unicode/bidi",
+ "unicode/cldr",
+ "unicode/norm",
+ "unicode/rangetable"
+ ]
+ revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
+ version = "v0.3.0"
+
+[[projects]]
+ name = "google.golang.org/appengine"
+ packages = [
+ "internal",
+ "internal/base",
+ "internal/datastore",
+ "internal/log",
+ "internal/remote_api",
+ "internal/urlfetch",
+ "urlfetch"
+ ]
+ revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a"
+ version = "v1.0.0"
+
+[[projects]]
+ name = "gopkg.in/gorp.v1"
+ packages = ["."]
+ revision = "c87af80f3cc5036b55b83d77171e156791085e2e"
+ version = "v1.7.1"
+
+[[projects]]
+ name = "gopkg.in/yaml.v2"
+ packages = ["."]
+ revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
+ version = "v2.2.1"
+
+[solve-meta]
+ analyzer-name = "dep"
+ analyzer-version = 1
+ inputs-digest = "adc5b2bf12459e1c97928d71e62187d90253deaecb412df40fc3c7a2a02fd91e"
+ solver-name = "gps-cdcl"
+ solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
new file mode 100644
index 000000000..bab966324
--- /dev/null
+++ b/Gopkg.toml
@@ -0,0 +1,142 @@
+# 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"
+#
+# [prune]
+# non-go = false
+# go-tests = true
+# unused-packages = true
+
+
+[[constraint]]
+ name = "github.com/akutz/gotil"
+ version = "0.1.0"
+
+[[constraint]]
+ name = "github.com/go-resty/resty"
+ version = "1.4.0"
+
+[[constraint]]
+ name = "github.com/go-sql-driver/mysql"
+ version = "1.3.0"
+
+[[constraint]]
+ name = "github.com/gorilla/context"
+ version = "1.1.0"
+
+[[constraint]]
+ branch = "master"
+ name = "github.com/jmoiron/sqlx"
+
+[[constraint]]
+ name = "github.com/julienschmidt/httprouter"
+ version = "1.1.0"
+
+[[constraint]]
+ branch = "master"
+ name = "github.com/lib/pq"
+
+[[constraint]]
+ name = "github.com/magiconair/properties"
+ version = "1.7.6"
+
+[[constraint]]
+ branch = "master"
+ name = "github.com/meatballhat/negroni-logrus"
+
+[[constraint]]
+ name = "github.com/ory/fosite"
+ version = "0.17.0"
+
+[[constraint]]
+ name = "github.com/ory/graceful"
+ version = "0.1.0"
+
+[[constraint]]
+ name = "github.com/ory/herodot"
+ version = "0.1.3"
+
+[[constraint]]
+ name = "github.com/ory/hydra"
+ version = "0.11.12"
+
+[[constraint]]
+ name = "github.com/ory/ladon"
+ version = "0.8.9"
+
+[[constraint]]
+ name = "github.com/ory/pagination"
+ version = "0.0.1"
+
+[[constraint]]
+ branch = "master"
+ name = "github.com/ory/sqlcon"
+
+[[constraint]]
+ name = "github.com/pborman/uuid"
+ version = "1.1.0"
+
+[[constraint]]
+ name = "github.com/pkg/errors"
+ version = "0.8.0"
+
+[[constraint]]
+ name = "github.com/pkg/profile"
+ version = "1.2.1"
+
+[[constraint]]
+ name = "github.com/rs/cors"
+ version = "1.3.0"
+
+[[constraint]]
+ branch = "master"
+ name = "github.com/rubenv/sql-migrate"
+
+[[constraint]]
+ name = "github.com/sirupsen/logrus"
+ version = "1.0.5"
+
+[[constraint]]
+ name = "github.com/spf13/cobra"
+ version = "0.0.2"
+
+[[constraint]]
+ name = "github.com/spf13/viper"
+ version = "1.0.2"
+
+[[constraint]]
+ name = "github.com/square/go-jose"
+ version = "2.1.6"
+
+[[constraint]]
+ name = "github.com/stretchr/testify"
+ version = "1.2.1"
+
+[[constraint]]
+ name = "github.com/urfave/negroni"
+ version = "0.3.0"
+
+[[constraint]]
+ branch = "master"
+ name = "golang.org/x/oauth2"
+
+[prune]
+ go-tests = true
+ unused-packages = true
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/README.md b/README.md
index 49ecd3c69..50e56a091 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,41 @@
-# hades
\ No newline at end of file
+# ORY Keto
+
+
+
+This service is a policy decision point. It uses a set of access control policies, similar to
+[AWS IAM Policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html), in order to determine whether
+a subject (user, application, service, car, ...) is authorized to perform a certain action on a resource.
+
+
+
+
+
+
+
+## Introduction
+
+With ORY Keto, you can model Access Control Lists, Role Based Access Control, and fine-grained permission sets.
+This server implementation uses [ORY Ladon](https://github.com/ory/ladon) as the decision engine.
+
+ORY Keto is possible to resolve credentials using various authentication mechanisms:
+
+* OAuth 2.0 Access Tokens using the OAuth 2.0 Introspection standard.
+* Plaintext when you already know the user ID.
+* JSON Web Tokens (coming soon).
+* SAML (coming soon).
+
+## Installation
+
+## Documentation
+
+## API Documentation
+
+The RESTful API Documentation is available on our Website.
+
+## User Guide
+
+Coming soon...
diff --git a/authentication/authenticator.go b/authentication/authenticator.go
new file mode 100644
index 000000000..941e78944
--- /dev/null
+++ b/authentication/authenticator.go
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2015-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 authentication
+
+import (
+ "net/http"
+
+ "github.com/ory/herodot"
+ "github.com/pkg/errors"
+)
+
+var ErrorNotResponsible = errors.New("This authenticator is not applicable to this request")
+
+var ErrUnauthorized = &herodot.DefaultError{
+ ErrorField: "The provided credentials are invalid or expired",
+}
+
+type Session interface {
+ GrantAccess()
+ DenyAccess()
+ GetSubject() string
+}
+
+// swagger:model authenticationDefaultSession
+type DefaultSession struct {
+ // Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.
+ // This is usually a uuid but you can choose a urn or some other id too.
+ Subject string `json:"subject"`
+
+ // Allowed is true if the request is allowed and false otherwise.
+ Allowed bool `json:"allowed"`
+}
+
+func (s *DefaultSession) GrantAccess() {
+ s.Allowed = true
+}
+
+func (s *DefaultSession) DenyAccess() {
+ s.Allowed = false
+}
+
+func (s *DefaultSession) GetSubject() string {
+ return s.Subject
+}
+
+type Authenticator interface {
+ Authenticate(r *http.Request) (Session, error)
+}
diff --git a/authentication/oauth2_introspection.go b/authentication/oauth2_introspection.go
new file mode 100644
index 000000000..d0436ab2f
--- /dev/null
+++ b/authentication/oauth2_introspection.go
@@ -0,0 +1,163 @@
+/*
+ * Copyright © 2015-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 authentication
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "net/http"
+
+ "strings"
+ "time"
+
+ "github.com/pkg/errors"
+ "golang.org/x/oauth2/clientcredentials"
+)
+
+// swagger:model authenticationOAuth2Session
+type OAuth2Session struct {
+ // Here, it's subject
+ *DefaultSession
+
+ // GrantedScopes is a list of scopes that the subject authorized when asked for consent.
+ GrantedScopes []string `json:"grantedScopes"`
+
+ // Issuer is the id of the issuer, typically an hydra instance.
+ Issuer string `json:"issuer"`
+
+ // ClientID is the id of the OAuth2 client that requested the token.
+ ClientID string `json:"clientId"`
+
+ // IssuedAt is the token creation time stamp.
+ IssuedAt time.Time `json:"issuedAt"`
+
+ // ExpiresAt is the expiry timestamp.
+ ExpiresAt time.Time `json:"expiresAt"`
+
+ NotBefore time.Time `json:"notBefore,omitempty"`
+ Username string `json:"username,omitempty"`
+ Audience string `json:"audience,omitempty"`
+
+ // Extra represents arbitrary session data.
+ Extra map[string]interface{} `json:"accessTokenExtra"`
+}
+
+type IntrospectionResponse struct {
+ Active bool `json:"active"`
+ Scope string `json:"scope,omitempty"`
+ ClientID string `json:"client_id,omitempty"`
+ // Here, it's sub
+ Subject string `json:"sub,omitempty"`
+ ExpiresAt int64 `json:"exp,omitempty"`
+ IssuedAt int64 `json:"iat,omitempty"`
+ NotBefore int64 `json:"nbf,omitempty"`
+ Username string `json:"username,omitempty"`
+ Audience string `json:"aud,omitempty"`
+ Issuer string `json:"iss,omitempty"`
+}
+
+type OAuth2IntrospectionAuthentication struct {
+ client *http.Client
+ introspectionURL string
+}
+
+// swagger:model AuthenticationOAuth2IntrospectionRequest
+type AuthenticationOAuth2IntrospectionRequest struct {
+ // Token is the token to introspect.
+ Token string `json:"token"`
+
+ // Scopes is an array of scopes that are required.
+ Scopes []string `json:"scopes"`
+}
+
+func NewOAuth2Session() *OAuth2Session {
+ return &OAuth2Session{
+ DefaultSession: new(DefaultSession),
+ }
+}
+
+func NewOAuth2IntrospectionAuthentication(clientID, clientSecret, tokenURL, introspectionURL string, scopes []string) *OAuth2IntrospectionAuthentication {
+ c := clientcredentials.Config{
+ ClientID: clientID,
+ ClientSecret: clientSecret,
+ TokenURL: tokenURL,
+ Scopes: scopes,
+ }
+
+ return &OAuth2IntrospectionAuthentication{
+ client: c.Client(context.Background()),
+ introspectionURL: introspectionURL,
+ }
+}
+
+func (a *OAuth2IntrospectionAuthentication) Authenticate(r *http.Request) (Session, error) {
+ var token AuthenticationOAuth2IntrospectionRequest
+
+ err := json.NewDecoder(r.Body).Decode(&token)
+ if err != nil || token.Token == "" {
+ return nil, errors.WithStack(ErrorNotResponsible)
+ }
+
+ body, err := json.Marshal(token)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ req, err := http.NewRequest("POST", a.introspectionURL, bytes.NewBuffer(body))
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ resp, err := a.client.Do(req)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return nil, errors.Errorf("Introspection returned status code %d but expected %d", resp.StatusCode, http.StatusOK)
+ }
+
+ var ir IntrospectionResponse
+ if err := json.NewDecoder(resp.Body).Decode(&ir); err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ if !ir.Active {
+ return nil, errors.WithStack(ErrUnauthorized)
+ }
+
+ return &OAuth2Session{
+ DefaultSession: &DefaultSession{
+ Subject: ir.Subject,
+ },
+ GrantedScopes: strings.Split(ir.Scope, " "),
+ ClientID: ir.ClientID,
+ ExpiresAt: time.Unix(ir.ExpiresAt, 0).UTC(),
+ IssuedAt: time.Unix(ir.IssuedAt, 0).UTC(),
+ NotBefore: time.Unix(ir.NotBefore, 0).UTC(),
+ Username: ir.Username,
+ Audience: ir.Audience,
+ Issuer: ir.Issuer,
+ }, nil
+}
diff --git a/authentication/oauth2_introspection_test.go b/authentication/oauth2_introspection_test.go
new file mode 100644
index 000000000..cfc914010
--- /dev/null
+++ b/authentication/oauth2_introspection_test.go
@@ -0,0 +1,137 @@
+/*
+ * Copyright © 2015-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 authentication
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+ "time"
+
+ "github.com/julienschmidt/httprouter"
+ "github.com/ory/herodot"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestOAuth2Introspection(t *testing.T) {
+ h := httprouter.New()
+ var cb func(w http.ResponseWriter, r *http.Request, req AuthenticationOAuth2IntrospectionRequest) *IntrospectionResponse
+
+ h.POST("/oauth2/introspect", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ var req AuthenticationOAuth2IntrospectionRequest
+ err := json.NewDecoder(r.Body).Decode(&req)
+ require.Nil(t, err)
+
+ ir := cb(w, r, req)
+ herodot.NewJSONWriter(logrus.New()).Write(w, r, ir)
+ })
+ ts := httptest.NewServer(h)
+
+ authenticator := &OAuth2IntrospectionAuthentication{
+ client: http.DefaultClient,
+ introspectionURL: ts.URL + "/oauth2/introspect",
+ }
+
+ now := time.Now().UTC().Round(time.Minute)
+
+ for k, tc := range []struct {
+ d string
+ cb func(w http.ResponseWriter, r *http.Request, req AuthenticationOAuth2IntrospectionRequest) *IntrospectionResponse
+ req *AuthenticationOAuth2IntrospectionRequest
+ expectedErr error
+ expectedSession *OAuth2Session
+ }{
+ {
+ cb: func(w http.ResponseWriter, r *http.Request, req AuthenticationOAuth2IntrospectionRequest) *IntrospectionResponse {
+ assert.Equal(t, "foo-token", req.Token)
+ assert.EqualValues(t, []string{"foo-scope", "foo-scope-a"}, req.Scopes)
+ return &IntrospectionResponse{Active: false}
+ },
+ req: &AuthenticationOAuth2IntrospectionRequest{Token: "foo-token", Scopes: []string{"foo-scope", "foo-scope-a"}},
+ expectedErr: ErrUnauthorized,
+ },
+ {
+ cb: func(w http.ResponseWriter, r *http.Request, req AuthenticationOAuth2IntrospectionRequest) *IntrospectionResponse {
+ return &IntrospectionResponse{
+ Active: true,
+ Scope: "scope",
+ ClientID: "scope-ip",
+ Subject: "subject",
+ ExpiresAt: now.Unix(),
+ IssuedAt: now.Unix(),
+ NotBefore: now.Unix(),
+ Username: "username",
+ Audience: "audience",
+ Issuer: "issuer",
+ }
+ },
+ req: &AuthenticationOAuth2IntrospectionRequest{Token: "foo-token", Scopes: []string{"foo-scope", "foo-scope-a"}},
+ expectedSession: &OAuth2Session{
+ DefaultSession: &DefaultSession{
+ Subject: "subject",
+ Allowed: false,
+ },
+ GrantedScopes: []string{"scope"},
+ ClientID: "scope-ip",
+ ExpiresAt: now,
+ IssuedAt: now,
+ NotBefore: now,
+ Username: "username",
+ Audience: "audience",
+ Issuer: "issuer",
+ },
+ },
+ } {
+ t.Run(fmt.Sprintf("case=%d/description=%s", k, tc.d), func(t *testing.T) {
+ cb = tc.cb
+
+ out, err := json.Marshal(tc.req)
+ require.NoError(t, err)
+
+ r := &http.Request{Body: ioutil.NopCloser(bytes.NewReader(out))}
+
+ session, err := authenticator.Authenticate(r)
+ if tc.expectedErr == nil {
+ if err != nil {
+ require.NoError(t, err, "%+v", err.(stackTracer).StackTrace())
+ }
+ assert.EqualValues(t, tc.expectedSession, session)
+ } else {
+ if err == nil {
+ require.Error(t, err)
+ }
+ assert.EqualError(t, err, tc.expectedErr.Error(), "%+v", err.(stackTracer).StackTrace())
+ }
+ })
+ }
+}
+
+type stackTracer interface {
+ StackTrace() errors.StackTrace
+}
diff --git a/authentication/plaintext.go b/authentication/plaintext.go
new file mode 100644
index 000000000..2103b3946
--- /dev/null
+++ b/authentication/plaintext.go
@@ -0,0 +1,49 @@
+/*
+ * Copyright © 2015-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 authentication
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/pkg/errors"
+)
+
+func NewPlaintextAuthentication() *PlaintextAuthentication {
+ return &PlaintextAuthentication{}
+}
+
+type PlaintextAuthentication struct {
+ client *http.Client
+ introspectionURL string
+}
+
+func (a *PlaintextAuthentication) Authenticate(r *http.Request) (Session, error) {
+ var session DefaultSession
+
+ err := json.NewDecoder(r.Body).Decode(&session)
+ if err != nil {
+ return nil, errors.WithStack(ErrorNotResponsible)
+ }
+
+ return &session, nil
+}
diff --git a/authentication/plaintext_test.go b/authentication/plaintext_test.go
new file mode 100644
index 000000000..73f052386
--- /dev/null
+++ b/authentication/plaintext_test.go
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2015-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 authentication
+
+import (
+ "bytes"
+ "encoding/json"
+ "io/ioutil"
+ "net/http"
+ "testing"
+
+ "github.com/magiconair/properties/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestPlaintext(t *testing.T) {
+ out, err := json.Marshal(&DefaultSession{Subject: "foo"})
+ require.NoError(t, err)
+
+ r := &http.Request{
+ Body: ioutil.NopCloser(bytes.NewReader(out)),
+ }
+
+ a := NewPlaintextAuthentication()
+ session, err := a.Authenticate(r)
+ require.NoError(t, err)
+
+ assert.Equal(t, "foo", session.GetSubject())
+}
diff --git a/cmd/client/handler.go b/cmd/client/handler.go
new file mode 100644
index 000000000..687e165a1
--- /dev/null
+++ b/cmd/client/handler.go
@@ -0,0 +1,29 @@
+// Copyright © 2017 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.
+
+package client
+
+type Handler struct {
+ Policies *PolicyHandler
+ Roles *RoleHandler
+ Warden *WardenHandler
+}
+
+func NewHandler() *Handler {
+ return &Handler{
+ Policies: newPolicyHandler(),
+ Roles: newRoleHandler(),
+ Warden: newWardenHandler(),
+ }
+}
diff --git a/cmd/client/handler_helper.go b/cmd/client/handler_helper.go
new file mode 100644
index 000000000..2274c10af
--- /dev/null
+++ b/cmd/client/handler_helper.go
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2015-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 client
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+
+ "strings"
+
+ keto "github.com/ory/keto/sdk/go/keto/swagger"
+ "github.com/spf13/cobra"
+)
+
+func getBasePath(cmd *cobra.Command) string {
+ location, err := cmd.Flags().GetString("url")
+ if err != nil || location == "" {
+ fmt.Println(cmd.UsageString())
+ fatalf("Please set the location of ORY Keto by using the --url flag or the KETO_URL environment variable.")
+ }
+ return strings.TrimRight(location, "/")
+}
+
+func must(err error, message string, args ...interface{}) {
+ if err == nil {
+ return
+ }
+
+ fmt.Fprintf(os.Stderr, message+"\n", args...)
+ os.Exit(1)
+}
+
+func checkResponse(response *keto.APIResponse, err error, expectedStatusCode int) {
+ must(err, "Command failed because error \"%s\" occurred.\n", err)
+
+ if response.StatusCode != expectedStatusCode {
+ fmt.Fprintf(os.Stderr, "Command failed because status code %d was expeceted but code %d was received.\n", expectedStatusCode, response.StatusCode)
+ os.Exit(1)
+ return
+ }
+}
+
+func formatResponse(response interface{}) string {
+ out, err := json.MarshalIndent(response, "", "\t")
+ must(err, `Command failed because an error ("%s") occurred while prettifying output.`, err)
+ return string(out)
+}
+
+func fatalf(message string, args ...interface{}) {
+ fmt.Printf(message+"\n", args)
+ os.Exit(1)
+}
diff --git a/cmd/client/handler_policy.go b/cmd/client/handler_policy.go
new file mode 100644
index 000000000..2eabfe5f0
--- /dev/null
+++ b/cmd/client/handler_policy.go
@@ -0,0 +1,283 @@
+/*
+ * Copyright © 2015-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 client
+
+import (
+ "fmt"
+ "os"
+
+ "net/http"
+
+ keto "github.com/ory/keto/sdk/go/keto/swagger"
+ "github.com/ory/ladon"
+ "github.com/spf13/cobra"
+ "github.com/square/go-jose/json"
+)
+
+type PolicyHandler struct{}
+
+func (h *PolicyHandler) newPolicyManager(cmd *cobra.Command) *keto.PolicyApi {
+ c := keto.NewPolicyApiWithBasePath(getBasePath(cmd))
+
+ if token, err := cmd.Flags().GetString("bearer-token"); err == nil && token != "" {
+ c.Configuration.DefaultHeader["Authorization"] = "Bearer " + token
+ }
+
+ if term, _ := cmd.Flags().GetBool("fake-tls-termination"); term {
+ c.Configuration.DefaultHeader["X-Forwarded-Proto"] = "https"
+ }
+ return c
+}
+
+func newPolicyHandler() *PolicyHandler {
+ return &PolicyHandler{}
+}
+
+func (h *PolicyHandler) ImportPolicy(cmd *cobra.Command, args []string) {
+ if len(args) == 0 {
+ fmt.Println(cmd.UsageString())
+ return
+ }
+
+ m := h.newPolicyManager(cmd)
+
+ for _, path := range args {
+ reader, err := os.Open(path)
+ must(err, "Could not open file %s: %s", path, err)
+
+ var p keto.Policy
+ err = json.NewDecoder(reader).Decode(&p)
+ must(err, "Could not parse JSON: %s", err)
+
+ _, response, err := m.CreatePolicy(p)
+ checkResponse(response, err, http.StatusCreated)
+ fmt.Printf("Imported policy %s from %s.\n", p.Id, path)
+ }
+
+ return
+}
+
+func (h *PolicyHandler) CreatePolicy(cmd *cobra.Command, args []string) {
+ m := h.newPolicyManager(cmd)
+
+ if files, _ := cmd.Flags().GetStringSlice("files"); len(files) > 0 {
+ fmt.Println("Importing policies using the -f flag is deprecated and will be removed in the future.")
+ fmt.Println(`Please use "hydra policies import" instead.`)
+ h.ImportPolicy(cmd, files)
+ return
+ }
+
+ id, _ := cmd.Flags().GetString("id")
+ description, _ := cmd.Flags().GetString("description")
+ subjects, _ := cmd.Flags().GetStringSlice("subjects")
+ resources, _ := cmd.Flags().GetStringSlice("resources")
+ actions, _ := cmd.Flags().GetStringSlice("actions")
+ isAllow, _ := cmd.Flags().GetBool("allow")
+
+ if len(subjects) == 0 || len(resources) == 0 || len(actions) == 0 {
+ fmt.Println(cmd.UsageString())
+ fmt.Println("")
+ fmt.Println("Got empty subject, resource or action list")
+ return
+ }
+
+ effect := ladon.DenyAccess
+ if isAllow {
+ effect = ladon.AllowAccess
+ }
+
+ result, response, err := m.CreatePolicy(keto.Policy{
+ Id: id,
+ Description: description,
+ Subjects: subjects,
+ Resources: resources,
+ Actions: actions,
+ Effect: effect,
+ })
+ checkResponse(response, err, http.StatusCreated)
+ fmt.Printf("Created policy %s.\n", result.Id)
+}
+
+func (h *PolicyHandler) AddResourceToPolicy(cmd *cobra.Command, args []string) {
+ m := h.newPolicyManager(cmd)
+ if len(args) < 2 {
+ fmt.Print(cmd.UsageString())
+ return
+ }
+
+ p, response, err := m.GetPolicy(args[0])
+ checkResponse(response, err, http.StatusOK)
+
+ p.Resources = append(p.Resources, args[1:]...)
+
+ _, response, err = m.UpdatePolicy(p.Id, *p)
+ checkResponse(response, err, http.StatusOK)
+ fmt.Printf("Added resources to policy %s", p.Id)
+}
+
+func (h *PolicyHandler) RemoveResourceFromPolicy(cmd *cobra.Command, args []string) {
+ m := h.newPolicyManager(cmd)
+ if len(args) < 2 {
+ fmt.Print(cmd.UsageString())
+ return
+ }
+
+ p, response, err := m.GetPolicy(args[0])
+ checkResponse(response, err, http.StatusOK)
+
+ resources := []string{}
+ for _, r := range p.Resources {
+ var filter bool
+ for _, a := range args[1:] {
+ if r == a {
+ filter = true
+ }
+ }
+ if !filter {
+ resources = append(resources, r)
+ }
+ }
+ p.Resources = resources
+
+ _, response, err = m.UpdatePolicy(p.Id, *p)
+ checkResponse(response, err, http.StatusOK)
+ fmt.Printf("Removed resources from policy %s", p.Id)
+}
+
+func (h *PolicyHandler) AddSubjectToPolicy(cmd *cobra.Command, args []string) {
+ m := h.newPolicyManager(cmd)
+ if len(args) < 2 {
+ fmt.Print(cmd.UsageString())
+ return
+ }
+
+ p, response, err := m.GetPolicy(args[0])
+ checkResponse(response, err, http.StatusOK)
+
+ p.Subjects = append(p.Subjects, args[1:]...)
+
+ _, response, err = m.UpdatePolicy(p.Id, *p)
+ checkResponse(response, err, http.StatusOK)
+ fmt.Printf("Added subjects to policy %s", p.Id)
+}
+
+func (h *PolicyHandler) RemoveSubjectFromPolicy(cmd *cobra.Command, args []string) {
+ m := h.newPolicyManager(cmd)
+ if len(args) < 2 {
+ fmt.Print(cmd.UsageString())
+ return
+ }
+
+ p, response, err := m.GetPolicy(args[0])
+ checkResponse(response, err, http.StatusOK)
+
+ subjects := []string{}
+ for _, r := range p.Subjects {
+ var filter bool
+ for _, a := range args[1:] {
+ if r == a {
+ filter = true
+ }
+ }
+ if !filter {
+ subjects = append(subjects, r)
+ }
+ }
+ p.Subjects = subjects
+
+ _, response, err = m.UpdatePolicy(p.Id, *p)
+ checkResponse(response, err, http.StatusOK)
+ fmt.Printf("Removed subjects from policy %s.\n", p.Id)
+}
+
+func (h *PolicyHandler) AddActionToPolicy(cmd *cobra.Command, args []string) {
+ m := h.newPolicyManager(cmd)
+ if len(args) < 2 {
+ fmt.Print(cmd.UsageString())
+ return
+ }
+
+ p, response, err := m.GetPolicy(args[0])
+ checkResponse(response, err, http.StatusOK)
+
+ p.Actions = append(p.Actions, args[1:]...)
+
+ _, response, err = m.UpdatePolicy(p.Id, *p)
+ checkResponse(response, err, http.StatusOK)
+ fmt.Printf("Added actions to policy %s.\n", p.Id)
+}
+
+func (h *PolicyHandler) RemoveActionFromPolicy(cmd *cobra.Command, args []string) {
+ m := h.newPolicyManager(cmd)
+ if len(args) < 2 {
+ fmt.Print(cmd.UsageString())
+ return
+ }
+
+ p, response, err := m.GetPolicy(args[0])
+ checkResponse(response, err, http.StatusOK)
+
+ actions := []string{}
+ for _, r := range p.Actions {
+ var filter bool
+ for _, a := range args[1:] {
+ if r == a {
+ filter = true
+ }
+ }
+ if !filter {
+ actions = append(actions, r)
+ }
+ }
+ p.Actions = actions
+
+ _, response, err = m.UpdatePolicy(p.Id, *p)
+ checkResponse(response, err, http.StatusOK)
+ fmt.Printf("Removed actions from policy %s.\n", p.Id)
+}
+
+func (h *PolicyHandler) GetPolicy(cmd *cobra.Command, args []string) {
+ m := h.newPolicyManager(cmd)
+ if len(args) == 0 {
+ fmt.Print(cmd.UsageString())
+ return
+ }
+
+ p, response, err := m.GetPolicy(args[0])
+ checkResponse(response, err, http.StatusOK)
+
+ fmt.Printf("%s\n", formatResponse(p))
+}
+
+func (h *PolicyHandler) DeletePolicy(cmd *cobra.Command, args []string) {
+ m := h.newPolicyManager(cmd)
+ if len(args) == 0 {
+ fmt.Print(cmd.UsageString())
+ return
+ }
+
+ for _, arg := range args {
+ response, err := m.DeletePolicy(arg)
+ checkResponse(response, err, http.StatusNoContent)
+ fmt.Printf("Policy %s deleted.\n", arg)
+ }
+}
diff --git a/cmd/client/handler_role.go b/cmd/client/handler_role.go
new file mode 100644
index 000000000..16e531e74
--- /dev/null
+++ b/cmd/client/handler_role.go
@@ -0,0 +1,134 @@
+/*
+ * Copyright © 2015-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 client
+
+import (
+ "fmt"
+ "net/http"
+
+ keto "github.com/ory/keto/sdk/go/keto/swagger"
+ "github.com/spf13/cobra"
+)
+
+type RoleHandler struct {
+}
+
+func (h *RoleHandler) newPolicyManager(cmd *cobra.Command) *keto.RoleApi {
+ c := keto.NewRoleApiWithBasePath(getBasePath(cmd))
+
+ if token, err := cmd.Flags().GetString("bearer-token"); err == nil && token != "" {
+ c.Configuration.DefaultHeader["Authorization"] = "Bearer " + token
+ }
+
+ if term, _ := cmd.Flags().GetBool("fake-tls-termination"); term {
+ c.Configuration.DefaultHeader["X-Forwarded-Proto"] = "https"
+ }
+ return c
+}
+
+func newRoleHandler() *RoleHandler {
+ return &RoleHandler{}
+}
+
+func (h *RoleHandler) CreateRole(cmd *cobra.Command, args []string) {
+ if len(args) != 1 {
+ fmt.Print(cmd.UsageString())
+ return
+ }
+ m := h.newPolicyManager(cmd)
+
+ _, response, err := m.CreateRole(keto.Role{Id: args[0]})
+ checkResponse(response, err, http.StatusCreated)
+ fmt.Printf("Group %s created.\n", args[0])
+}
+
+func (h *RoleHandler) DeleteRole(cmd *cobra.Command, args []string) {
+ if len(args) != 1 {
+ fmt.Print(cmd.UsageString())
+ return
+ }
+
+ m := h.newPolicyManager(cmd)
+ response, err := m.DeleteRole(args[0])
+ checkResponse(response, err, http.StatusNoContent)
+ fmt.Printf("Group %s deleted.\n", args[0])
+}
+
+func (h *RoleHandler) RoleAddMembers(cmd *cobra.Command, args []string) {
+ if len(args) < 2 {
+ fmt.Print(cmd.UsageString())
+ return
+ }
+
+ m := h.newPolicyManager(cmd)
+ response, err := m.AddMembersToRole(args[0], keto.RoleMembers{Members: args[1:]})
+ checkResponse(response, err, http.StatusNoContent)
+ fmt.Printf("Members %v added to group %s.\n", args[1:], args[0])
+}
+
+func (h *RoleHandler) RoleRemoveMembers(cmd *cobra.Command, args []string) {
+ if len(args) < 2 {
+ fmt.Print(cmd.UsageString())
+ return
+ }
+
+ m := h.newPolicyManager(cmd)
+ response, err := m.RemoveMembersFromRole(args[0], keto.RoleMembers{Members: args[1:]})
+ checkResponse(response, err, http.StatusNoContent)
+ fmt.Printf("Members %v removed from group %s.\n", args[1:], args[0])
+}
+
+func (h *RoleHandler) FindRoles(cmd *cobra.Command, args []string) {
+ if len(args) != 1 {
+ fmt.Print(cmd.UsageString())
+ return
+ }
+
+ m := h.newPolicyManager(cmd)
+ groups, response, err := m.ListRoles(args[0], 500, 0)
+ checkResponse(response, err, http.StatusOK)
+ formatResponse(groups)
+}
+
+func (h *RoleHandler) ListRoles(cmd *cobra.Command, args []string) {
+ if len(args) != 0 {
+ fmt.Print(cmd.UsageString())
+ return
+ }
+
+ m := h.newPolicyManager(cmd)
+ groups, response, err := m.ListRoles("", 500, 0)
+ checkResponse(response, err, http.StatusOK)
+ formatResponse(groups)
+}
+
+func (h *RoleHandler) GetRole(cmd *cobra.Command, args []string) {
+ if len(args) != 1 {
+ fmt.Print(cmd.UsageString())
+ return
+ }
+
+ m := h.newPolicyManager(cmd)
+ groups, response, err := m.GetRole(args[0])
+ checkResponse(response, err, http.StatusOK)
+ formatResponse(groups)
+}
diff --git a/cmd/client/handler_warden.go b/cmd/client/handler_warden.go
new file mode 100644
index 000000000..9290f3d8c
--- /dev/null
+++ b/cmd/client/handler_warden.go
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2015-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 client
+
+import (
+ "fmt"
+ "net/http"
+
+ keto "github.com/ory/keto/sdk/go/keto/swagger"
+ "github.com/spf13/cobra"
+)
+
+type WardenHandler struct{}
+
+func (h *WardenHandler) newWardenManager(cmd *cobra.Command) *keto.WardenApi {
+ c := keto.NewWardenApiWithBasePath(getBasePath(cmd))
+
+ if token, err := cmd.Flags().GetString("bearer-token"); err == nil && token != "" {
+ c.Configuration.DefaultHeader["Authorization"] = "Bearer " + token
+ }
+
+ if term, _ := cmd.Flags().GetBool("fake-tls-termination"); term {
+ c.Configuration.DefaultHeader["X-Forwarded-Proto"] = "https"
+ }
+ return c
+}
+
+func newWardenHandler() *WardenHandler {
+ return &WardenHandler{}
+}
+
+func (h *WardenHandler) IsOAuth2AccessTokenAuthorized(cmd *cobra.Command, args []string) {
+ token, _ := cmd.Flags().GetString("token")
+ scopes, _ := cmd.Flags().GetStringArray("scopes")
+ action, _ := cmd.Flags().GetString("actions")
+ resource, _ := cmd.Flags().GetString("resources")
+
+ m := h.newWardenManager(cmd)
+ _, response, err := m.IsOAuth2AccessTokenAuthorized(keto.WardenOAuth2AccessRequest{
+ Token: token,
+ Scopes: scopes,
+ Action: action,
+ Resource: resource,
+ })
+ checkResponse(response, err, http.StatusOK)
+ fmt.Printf("%s\n", response.Payload)
+}
+func (h *WardenHandler) IsSubjectAuthorized(cmd *cobra.Command, args []string) {
+ subject, _ := cmd.Flags().GetString("subject")
+ action, _ := cmd.Flags().GetString("actions")
+ resource, _ := cmd.Flags().GetString("resources")
+
+ m := h.newWardenManager(cmd)
+ _, response, err := m.IsSubjectAuthorized(keto.WardenSubjectAccessRequest{
+ Action: action,
+ Subject: subject,
+ Resource: resource,
+ })
+ checkResponse(response, err, http.StatusOK)
+ fmt.Printf("%s\n", response.Payload)
+}
diff --git a/cmd/groups.go b/cmd/groups.go
new file mode 100644
index 000000000..8e57d9e05
--- /dev/null
+++ b/cmd/groups.go
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// groupsCmd represents the groups command
+var groupsCmd = &cobra.Command{
+ Use: "groups",
+ Short: "Manage warden groups",
+}
+
+func init() {
+ RootCmd.AddCommand(groupsCmd)
+ groupsCmd.PersistentFlags().Bool("fake-tls-termination", false, `fake tls termination by adding "X-Forwarded-Proto: https"" to http headers`)
+ groupsCmd.PersistentFlags().Bool("dry", false, "do not execute the command but show the corresponding curl command instead")
+}
diff --git a/cmd/groups_create.go b/cmd/groups_create.go
new file mode 100644
index 000000000..90058835a
--- /dev/null
+++ b/cmd/groups_create.go
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// createCmd represents the create command
+var createCmd = &cobra.Command{
+ Use: "create ",
+ Short: "Create a warden group",
+ Long: `This command creates a warden group.
+
+Example:
+ hydra groups create my-group
+`,
+ Run: cmdHandler.Groups.CreateGroup,
+}
+
+func init() {
+ groupsCmd.AddCommand(createCmd)
+}
diff --git a/cmd/groups_delete.go b/cmd/groups_delete.go
new file mode 100644
index 000000000..acf7a4921
--- /dev/null
+++ b/cmd/groups_delete.go
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// deleteCmd represents the delete command
+var deleteCmd = &cobra.Command{
+ Use: "delete ",
+ Short: "Delete a warden group",
+ Long: `This command deletes a warden group.
+
+Example:
+ hydra groups delete my-group
+`,
+ Run: cmdHandler.Groups.DeleteGroup,
+}
+
+func init() {
+ groupsCmd.AddCommand(deleteCmd)
+
+}
diff --git a/cmd/groups_find.go b/cmd/groups_find.go
new file mode 100644
index 000000000..c578f78f4
--- /dev/null
+++ b/cmd/groups_find.go
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// findCmd represents the find command
+var findCmd = &cobra.Command{
+ Use: "find ",
+ Short: "Find all groups a subject belongs to",
+ Long: `This command finds all groups a subject belongs to.
+
+Example:
+ hydra groups find peter
+`,
+ Run: cmdHandler.Groups.FindGroups,
+}
+
+func init() {
+ groupsCmd.AddCommand(findCmd)
+}
diff --git a/cmd/groups_list.go b/cmd/groups_list.go
new file mode 100644
index 000000000..d4c607f37
--- /dev/null
+++ b/cmd/groups_list.go
@@ -0,0 +1,52 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// listCmd represents the list command
+var listCmd = &cobra.Command{
+ Use: "list",
+ Short: "List all groups",
+ Long: `This command lists all groups.
+
+Example:
+ hydra groups list
+`,
+ Run: cmdHandler.Groups.ListGroups,
+}
+
+func init() {
+ groupsCmd.AddCommand(listCmd)
+
+ // Here you will define your flags and configuration settings.
+
+ // Cobra supports Persistent Flags which will work for this command
+ // and all subcommands, e.g.:
+ // listCmd.PersistentFlags().String("foo", "", "A help for foo")
+
+ // Cobra supports local flags which will only run when this command
+ // is called directly, e.g.:
+ // listCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+
+}
diff --git a/cmd/groups_members.go b/cmd/groups_members.go
new file mode 100644
index 000000000..b01ed355e
--- /dev/null
+++ b/cmd/groups_members.go
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+var groupsMembersCmd = &cobra.Command{
+ Use: "members",
+ Short: "Manage warden group members",
+}
+
+func init() {
+ groupsCmd.AddCommand(groupsMembersCmd)
+}
diff --git a/cmd/groups_members_add.go b/cmd/groups_members_add.go
new file mode 100644
index 000000000..886128c1b
--- /dev/null
+++ b/cmd/groups_members_add.go
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+var addCmd = &cobra.Command{
+ Use: "add [...]",
+ Short: "Add members to a warden group",
+ Long: `This command adds members to a warden group.
+
+Example:
+ hydra groups members add my-group peter julia
+`,
+ Run: cmdHandler.Groups.AddMembers,
+}
+
+func init() {
+ groupsMembersCmd.AddCommand(addCmd)
+}
diff --git a/cmd/groups_members_remove.go b/cmd/groups_members_remove.go
new file mode 100644
index 000000000..b964a5b61
--- /dev/null
+++ b/cmd/groups_members_remove.go
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+var removeCmd = &cobra.Command{
+ Use: "remove [...]",
+ Short: "Remove members from a warden group",
+ Long: `This command removes members from a warden group.
+
+Example:
+ hydra groups members remove my-group peter julia
+`,
+ Run: cmdHandler.Groups.RemoveMembers,
+}
+
+func init() {
+ groupsMembersCmd.AddCommand(removeCmd)
+}
diff --git a/cmd/migrate.go b/cmd/migrate.go
new file mode 100644
index 000000000..9c06aa4f4
--- /dev/null
+++ b/cmd/migrate.go
@@ -0,0 +1,33 @@
+// Copyright © 2018 NAME HERE
+//
+// 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.
+
+package cmd
+
+import (
+ "fmt"
+
+ "github.com/spf13/cobra"
+)
+
+// migrateCmd represents the migrate command
+var migrateCmd = &cobra.Command{
+ Use: "migrate",
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Print(cmd.UsageString())
+ },
+}
+
+func init() {
+ RootCmd.AddCommand(migrateCmd)
+}
diff --git a/cmd/migrate_hydra.go b/cmd/migrate_hydra.go
new file mode 100644
index 000000000..901271b17
--- /dev/null
+++ b/cmd/migrate_hydra.go
@@ -0,0 +1,40 @@
+// Copyright © 2018 NAME HERE
+//
+// 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.
+
+package cmd
+
+import (
+ "github.com/ory/keto/cmd/server"
+ "github.com/spf13/cobra"
+)
+
+// migrateHydraCmd represents the hydra command
+var migrateHydraCmd = &cobra.Command{
+ Use: "hydra ",
+ Short: "Applies SQL migration plans that migrate groups and policies from ORY Hydra < v1.0.0",
+ Long: `It is recommended to run this command close to the SQL instance (e.g. same subnet) instead of over the public internet.
+This decreases risk of failure and decreases time required.
+
+### WARNING ###
+
+Before running this command on an existing database, create a back up!
+`,
+ Run: server.RunMigrateHydra(logger),
+}
+
+func init() {
+ RootCmd.AddCommand(migrateHydraCmd)
+
+ migrateHydraCmd.Flags().Bool("read-from-env", false, "Instead of reading the database URL from the command line arguments, the value of environment variable DATABASE_URL will be used.")
+}
diff --git a/cmd/migrate_sql.go b/cmd/migrate_sql.go
new file mode 100644
index 000000000..08a9184bc
--- /dev/null
+++ b/cmd/migrate_sql.go
@@ -0,0 +1,40 @@
+// Copyright © 2018 NAME HERE
+//
+// 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.
+
+package cmd
+
+import (
+ "github.com/ory/keto/cmd/server"
+ "github.com/spf13/cobra"
+)
+
+// migrateSqlCmd represents the sql command
+var migrateSqlCmd = &cobra.Command{
+ Use: "sql ",
+ Short: "Applies SQL migration plans and creates the database schemas",
+ Long: `It is recommended to run this command close to the SQL instance (e.g. same subnet) instead of over the public internet.
+This decreases risk of failure and decreases time required.
+
+### WARNING ###
+
+Before running this command on an existing database, create a back up!
+`,
+ Run: server.RunMigrateSQL(logger),
+}
+
+func init() {
+ RootCmd.AddCommand(migrateSqlCmd)
+
+ migrateSqlCmd.Flags().Bool("read-from-env", false, "Instead of reading the database URL from the command line arguments, the value of environment variable DATABASE_URL will be used.")
+}
diff --git a/cmd/policies.go b/cmd/policies.go
new file mode 100644
index 000000000..ccc15295f
--- /dev/null
+++ b/cmd/policies.go
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// policiesCmd represents the policies command
+var policiesCmd = &cobra.Command{
+ Use: "policies",
+ Short: "Manage access control policies",
+}
+
+func init() {
+ RootCmd.AddCommand(policiesCmd)
+ clientDefaultFlags(policiesCmd)
+}
diff --git a/cmd/policies_actions.go b/cmd/policies_actions.go
new file mode 100644
index 000000000..c71c20402
--- /dev/null
+++ b/cmd/policies_actions.go
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// policyActionsCmd represents the actions command
+var policyActionsCmd = &cobra.Command{
+ Use: "actions",
+ Short: "Manage which actions a policy applies to",
+}
+
+func init() {
+ policiesCmd.AddCommand(policyActionsCmd)
+}
diff --git a/cmd/policies_actions_add.go b/cmd/policies_actions_add.go
new file mode 100644
index 000000000..f4eaeda48
--- /dev/null
+++ b/cmd/policies_actions_add.go
@@ -0,0 +1,34 @@
+// Copyright © 2017 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.
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// policyActionsAddCmd represents the add command
+var policyActionsAddCmd = &cobra.Command{
+ Use: "add [...]",
+ Short: "Add actions to the regex matching list",
+ Long: `You can use regular expressions in your matches. Encapsulate them in < >.
+
+Example:
+ keto policies actions add my-policy create delete <[get|update]>`,
+ Run: cmdHandler.Policies.AddActionToPolicy,
+}
+
+func init() {
+ policyActionsCmd.AddCommand(policyActionsAddCmd)
+}
diff --git a/cmd/policies_actions_remove.go b/cmd/policies_actions_remove.go
new file mode 100644
index 000000000..8af112571
--- /dev/null
+++ b/cmd/policies_actions_remove.go
@@ -0,0 +1,34 @@
+// Copyright © 2017 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.
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// policiesActionsRemoveCmd represents the remove command
+var policiesActionsRemoveCmd = &cobra.Command{
+ Use: "remove [...]",
+ Short: "Remove actions from the regex matching list",
+ Long: `You can use regular expressions in your matches. Encapsulate them in < >.
+
+Example:
+ keto policies actions remove my-policy create delete <[get|update]>`,
+ Run: cmdHandler.Policies.RemoveActionFromPolicy,
+}
+
+func init() {
+ policyActionsCmd.AddCommand(policiesActionsRemoveCmd)
+}
diff --git a/cmd/policies_create.go b/cmd/policies_create.go
new file mode 100644
index 000000000..495d515b6
--- /dev/null
+++ b/cmd/policies_create.go
@@ -0,0 +1,43 @@
+// Copyright © 2017 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.
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// policiesCreateCmd represents the create command
+var policiesCreateCmd = &cobra.Command{
+ Use: "create",
+ Short: "Create a new policy",
+ Long: `To create a policy, either specify the files flag or pass arguments to create it directly from the CLI.
+
+Example
+ keto policies create -f policy-a.json,policy-b.json
+ keto policies create -s peter,max -r blog,users -a post,ban --allow`,
+ Run: cmdHandler.Policies.CreatePolicy,
+}
+
+func init() {
+ policiesCmd.AddCommand(policiesCreateCmd)
+
+ policiesCreateCmd.Flags().StringSliceP("files", "f", []string{}, "A list of paths to JSON encoded policy files")
+ policiesCreateCmd.Flags().StringP("id", "i", "", "The policy's id")
+ policiesCreateCmd.Flags().StringP("description", "d", "", "The policy's description")
+ policiesCreateCmd.Flags().StringSliceP("resources", "r", []string{}, "A list of resource regex strings this policy will match to (required)")
+ policiesCreateCmd.Flags().StringSliceP("subjects", "s", []string{}, "A list of subject regex strings this policy will match to (required)")
+ policiesCreateCmd.Flags().StringSliceP("actions", "a", []string{}, "A list of action regex strings this policy will match to (required)")
+ policiesCreateCmd.Flags().Bool("allow", false, "A list of action regex strings this policy will match to")
+}
diff --git a/cmd/policies_delete.go b/cmd/policies_delete.go
new file mode 100644
index 000000000..3821c42f4
--- /dev/null
+++ b/cmd/policies_delete.go
@@ -0,0 +1,30 @@
+// Copyright © 2017 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.
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// policiesDeleteCmd represents the delete command
+var policiesDeleteCmd = &cobra.Command{
+ Use: "delete ",
+ Short: "Delete a policy",
+ Run: cmdHandler.Policies.DeletePolicy,
+}
+
+func init() {
+ policiesCmd.AddCommand(policiesDeleteCmd)
+}
diff --git a/cmd/policies_get.go b/cmd/policies_get.go
new file mode 100644
index 000000000..5184bf6c1
--- /dev/null
+++ b/cmd/policies_get.go
@@ -0,0 +1,30 @@
+// Copyright © 2017 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.
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// policiesGetCmd represents the delete command
+var policiesGetCmd = &cobra.Command{
+ Use: "get ",
+ Short: "View a policy",
+ Run: cmdHandler.Policies.GetPolicy,
+}
+
+func init() {
+ policiesCmd.AddCommand(policiesGetCmd)
+}
diff --git a/cmd/policies_import.go b/cmd/policies_import.go
new file mode 100644
index 000000000..0f4b881e4
--- /dev/null
+++ b/cmd/policies_import.go
@@ -0,0 +1,31 @@
+// Copyright © 2017 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.
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// policiesImportCmd represents the import command
+var policiesImportCmd = &cobra.Command{
+ Use: "import [...]",
+ Short: "Import policies from JSON files",
+ Run: cmdHandler.Policies.ImportPolicy,
+}
+
+func init() {
+ policiesCmd.AddCommand(policiesImportCmd)
+
+}
diff --git a/cmd/policies_resources.go b/cmd/policies_resources.go
new file mode 100644
index 000000000..9c67995bb
--- /dev/null
+++ b/cmd/policies_resources.go
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// policiesResourcesCmd represents the resources command
+var policiesResourcesCmd = &cobra.Command{
+ Use: "resources",
+ Short: "Manage which resources a policy applies to",
+}
+
+func init() {
+ policiesCmd.AddCommand(policiesResourcesCmd)
+}
diff --git a/cmd/policies_resources_add.go b/cmd/policies_resources_add.go
new file mode 100644
index 000000000..3183e5024
--- /dev/null
+++ b/cmd/policies_resources_add.go
@@ -0,0 +1,34 @@
+// Copyright © 2017 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.
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// policyResourcesAddCmd represents the add command
+var policyResourcesAddCmd = &cobra.Command{
+ Use: "add [...]",
+ Short: "Add subjects to the regex matching list",
+ Long: `You can use regular expressions in your matches. Encapsulate them in < >.
+
+Example:
+ keto policies resources add my-policy some-item-123 some-item-<[234|345]>`,
+ Run: cmdHandler.Policies.AddResourceToPolicy,
+}
+
+func init() {
+ policiesResourcesCmd.AddCommand(policyResourcesAddCmd)
+}
diff --git a/cmd/policies_resources_remove.go b/cmd/policies_resources_remove.go
new file mode 100644
index 000000000..5f7c67d9e
--- /dev/null
+++ b/cmd/policies_resources_remove.go
@@ -0,0 +1,34 @@
+// Copyright © 2017 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.
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// policyResourcesRemoveCmd represents the remove command
+var policyResourcesRemoveCmd = &cobra.Command{
+ Use: "remove [...]",
+ Short: "Remove resources from the regex matching list",
+ Long: `You can use regular expressions in your matches. Encapsulate them in < >.
+
+Example:
+ keto policies resources remove my-policy some-item-123 some-item-<[234|345]>`,
+ Run: cmdHandler.Policies.RemoveResourceFromPolicy,
+}
+
+func init() {
+ policiesResourcesCmd.AddCommand(policyResourcesRemoveCmd)
+}
diff --git a/cmd/policies_subjects.go b/cmd/policies_subjects.go
new file mode 100644
index 000000000..2d3148e36
--- /dev/null
+++ b/cmd/policies_subjects.go
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// policiesSubjectsCmd represents the subjects command
+var policiesSubjectsCmd = &cobra.Command{
+ Use: "subjects",
+ Short: "Manage which subjects a policy applies to",
+}
+
+func init() {
+ policiesCmd.AddCommand(policiesSubjectsCmd)
+}
diff --git a/cmd/policies_subjects_add.go b/cmd/policies_subjects_add.go
new file mode 100644
index 000000000..14055dff1
--- /dev/null
+++ b/cmd/policies_subjects_add.go
@@ -0,0 +1,34 @@
+// Copyright © 2017 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.
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// policiesSubjectsAddCmd represents the add command
+var policiesSubjectsAddCmd = &cobra.Command{
+ Use: "add [...]",
+ Short: "Add subjects to the regex matching list",
+ Long: `You can use regular expressions in your matches. Encapsulate them in < >.
+
+Example:
+ keto policies subjects hydra add my-policy john@org.com <[peter|max]>@org.com`,
+ Run: cmdHandler.Policies.AddSubjectToPolicy,
+}
+
+func init() {
+ policiesSubjectsCmd.AddCommand(policiesSubjectsAddCmd)
+}
diff --git a/cmd/policies_subjects_remove.go b/cmd/policies_subjects_remove.go
new file mode 100644
index 000000000..fabd7d8fe
--- /dev/null
+++ b/cmd/policies_subjects_remove.go
@@ -0,0 +1,34 @@
+// Copyright © 2017 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.
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// policiesSubjectsRemoveCmd represents the remove command
+var policiesSubjectsRemoveCmd = &cobra.Command{
+ Use: "remove [...]",
+ Short: "Remove subjects from the regex matching list",
+ Long: `You can use regular expressions in your matches. Encapsulate them in < >.
+
+Example:
+ keto policies subjects remove my-policy john@org.com <[peter|max]>@org.com`,
+ Run: cmdHandler.Policies.RemoveSubjectFromPolicy,
+}
+
+func init() {
+ policiesSubjectsCmd.AddCommand(policiesSubjectsRemoveCmd)
+}
diff --git a/cmd/policies_view.go b/cmd/policies_view.go
new file mode 100644
index 000000000..86bd0e7c1
--- /dev/null
+++ b/cmd/policies_view.go
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// policiesGetCmd represents the delete command
+var policiesGetCmd = &cobra.Command{
+ Use: "get ",
+ Short: "View a policy",
+ Run: cmdHandler.Policies.GetPolicy,
+}
+
+func init() {
+ policiesCmd.AddCommand(policiesGetCmd)
+}
diff --git a/cmd/roles.go b/cmd/roles.go
new file mode 100644
index 000000000..36168d815
--- /dev/null
+++ b/cmd/roles.go
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// rolesCmd represents the groups command
+var rolesCmd = &cobra.Command{
+ Use: "roles",
+ Short: "Manage roles",
+}
+
+func init() {
+ RootCmd.AddCommand(rolesCmd)
+ clientDefaultFlags(rolesCmd)
+}
diff --git a/cmd/roles_create.go b/cmd/roles_create.go
new file mode 100644
index 000000000..5ffe31378
--- /dev/null
+++ b/cmd/roles_create.go
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// rolesCreateCmd represents the create command
+var rolesCreateCmd = &cobra.Command{
+ Use: "create ",
+ Short: "Create a role",
+ Long: `This command creates a role.
+
+Example:
+ keto roles create my-role
+`,
+ Run: cmdHandler.Roles.CreateRole,
+}
+
+func init() {
+ rolesCmd.AddCommand(rolesCreateCmd)
+}
diff --git a/cmd/roles_delete.go b/cmd/roles_delete.go
new file mode 100644
index 000000000..fb70a3557
--- /dev/null
+++ b/cmd/roles_delete.go
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// rolesDeleteCmd represents the delete command
+var rolesDeleteCmd = &cobra.Command{
+ Use: "delete ",
+ Short: "Delete a role",
+ Long: `This command deletes a role.
+
+Example:
+ keto roles delete my-group
+`,
+ Run: cmdHandler.Roles.DeleteRole,
+}
+
+func init() {
+ rolesCmd.AddCommand(rolesDeleteCmd)
+
+}
diff --git a/cmd/roles_find.go b/cmd/roles_find.go
new file mode 100644
index 000000000..19ce76bfa
--- /dev/null
+++ b/cmd/roles_find.go
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// rolesFindCmd represents the find command
+var rolesFindCmd = &cobra.Command{
+ Use: "find ",
+ Short: "Find all roles a subject belongs to",
+ Long: `This command finds all roles a subject belongs to.
+
+Example:
+ keto roles find peter
+`,
+ Run: cmdHandler.Roles.FindRoles,
+}
+
+func init() {
+ rolesCmd.AddCommand(rolesFindCmd)
+}
diff --git a/cmd/roles_get.go b/cmd/roles_get.go
new file mode 100644
index 000000000..a822bed60
--- /dev/null
+++ b/cmd/roles_get.go
@@ -0,0 +1,46 @@
+// Copyright © 2018 NAME HERE
+//
+// 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.
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// rolesGetCmd represents the get command
+var rolesGetCmd = &cobra.Command{
+ Use: "get",
+ Short: "Get a roles",
+ Long: `This command retrieves a role.
+
+Example:
+ keto roles get my-role
+`,
+ Run: cmdHandler.Roles.GetRole,
+}
+
+func init() {
+ rolesCmd.AddCommand(rolesGetCmd)
+
+ // Here you will define your flags and configuration settings.
+
+ // Cobra supports Persistent Flags which will work for this command
+ // and all subcommands, e.g.:
+ // rolesGetCmd.PersistentFlags().String("foo", "", "A help for foo")
+
+ // Cobra supports local flags which will only run when this command
+ // is called directly, e.g.:
+ // rolesGetCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+
+}
diff --git a/cmd/roles_list.go b/cmd/roles_list.go
new file mode 100644
index 000000000..1a8ff5365
--- /dev/null
+++ b/cmd/roles_list.go
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// rolesListCmd represents the list command
+var rolesListCmd = &cobra.Command{
+ Use: "list",
+ Short: "List all roles",
+ Long: `This command lists all roles.
+
+Example:
+ keto roles list
+`,
+ Run: cmdHandler.Roles.ListRoles,
+}
+
+func init() {
+ rolesCmd.AddCommand(rolesListCmd)
+
+ // Here you will define your flags and configuration settings.
+
+ // Cobra supports Persistent Flags which will work for this command
+ // and all subcommands, e.g.:
+ // rolesListCmd.PersistentFlags().String("foo", "", "A help for foo")
+
+ // Cobra supports local flags which will only run when this command
+ // is called directly, e.g.:
+ // rolesListCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+
+}
diff --git a/cmd/roles_members.go b/cmd/roles_members.go
new file mode 100644
index 000000000..be178bb5d
--- /dev/null
+++ b/cmd/roles_members.go
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+var rolesMembersCmd = &cobra.Command{
+ Use: "members",
+ Short: "Manage role members",
+}
+
+func init() {
+ rolesCmd.AddCommand(rolesMembersCmd)
+}
diff --git a/cmd/roles_members_add.go b/cmd/roles_members_add.go
new file mode 100644
index 000000000..7de82339b
--- /dev/null
+++ b/cmd/roles_members_add.go
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+var rolesMembersAdd = &cobra.Command{
+ Use: "add [...]",
+ Short: "Add members to a role",
+ Long: `This command adds members to a role.
+
+Example:
+ keto roles members add my-group peter julia
+`,
+ Run: cmdHandler.Roles.RoleAddMembers,
+}
+
+func init() {
+ rolesMembersCmd.AddCommand(rolesMembersAdd)
+}
diff --git a/cmd/roles_members_remove.go b/cmd/roles_members_remove.go
new file mode 100644
index 000000000..f233633c3
--- /dev/null
+++ b/cmd/roles_members_remove.go
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+var rolesMembersRemoveCmd = &cobra.Command{
+ Use: "remove [...]",
+ Short: "Remove members from a role",
+ Long: `This command removes members from a role.
+
+Example:
+ keto roles members remove my-group peter julia
+`,
+ Run: cmdHandler.Roles.RoleRemoveMembers,
+}
+
+func init() {
+ rolesMembersCmd.AddCommand(rolesMembersRemoveCmd)
+}
diff --git a/cmd/root.go b/cmd/root.go
new file mode 100644
index 000000000..5eebb0feb
--- /dev/null
+++ b/cmd/root.go
@@ -0,0 +1,95 @@
+// Copyright © 2018 NAME HERE
+//
+// 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.
+
+package cmd
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/ory/keto/cmd/client"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/cobra"
+ "github.com/spf13/viper"
+)
+
+var cfgFile string
+
+var (
+ Version = "dev-master"
+ BuildTime = "undefined"
+ GitHash = "undefined"
+)
+
+// RootCmd represents the base command when called without any subcommands
+var RootCmd = &cobra.Command{
+ Use: "keto",
+}
+
+var logger = logrus.New()
+
+var cmdHandler = client.NewHandler()
+
+// Execute adds all child commands to the root command sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+ if err := RootCmd.Execute(); err != nil {
+ fmt.Println(err)
+ os.Exit(-1)
+ }
+}
+
+func init() {
+ logLevel, err := logrus.ParseLevel(os.Getenv("LOG_LEVEL"))
+ if err != nil {
+ logLevel = logrus.InfoLevel
+ }
+
+ logger.Level = logLevel
+ cobra.OnInitialize(initConfig)
+
+ // Here you will define your flags and configuration settings.
+ // Cobra supports Persistent Flags, which, if defined here,
+ // will be global for your application.
+
+ RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.keto.yaml)")
+ // Cobra also supports local flags, which will only run
+ // when this action is called directly.
+ RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}
+
+// initConfig reads in config file and ENV variables if set.
+func initConfig() {
+ if cfgFile != "" { // enable ability to specify config file via flag
+ viper.SetConfigFile(cfgFile)
+ }
+
+ viper.SetConfigName(".keto") // name of config file (without extension)
+ viper.AddConfigPath("$HOME") // adding home directory as first search path
+ viper.AutomaticEnv() // read in environment variables that match
+
+ viper.SetDefault("LOG_LEVEL", "info")
+ viper.SetDefault("PORT", "4466")
+
+ // If a config file is found, read it in.
+ if err := viper.ReadInConfig(); err == nil {
+ fmt.Println("Using config file:", viper.ConfigFileUsed())
+ }
+}
+
+func clientDefaultFlags(c *cobra.Command) {
+ c.PersistentFlags().String("bearer-token", os.Getenv("KETO_BEARER_TOKEN"), "Provide a token to be used if the server is protected by HTTP Bearer Authorization, defaults to environment variable KETO_BEARER_TOKEN.")
+ c.PersistentFlags().Bool("fake-tls-termination", false, `fake tls termination by adding "X-Forwarded-Proto: https"" to http headers`)
+ c.PersistentFlags().String("url", os.Getenv("KETO_URL"), "The URL of the ORY Keto server, defaults to environment variable KETO_URL.")
+}
diff --git a/cmd/root_test.go b/cmd/root_test.go
new file mode 100644
index 000000000..c3c4d3ebc
--- /dev/null
+++ b/cmd/root_test.go
@@ -0,0 +1,108 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+
+ "github.com/akutz/gotil"
+ "github.com/pborman/uuid"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestExecute(t *testing.T) {
+ var osArgs = make([]string, len(os.Args))
+ var path = filepath.Join(os.TempDir(), fmt.Sprintf("keto-%s.yml", uuid.New()))
+ port := gotil.RandomTCPPort()
+ os.Setenv("DATABASE_URL", "memory")
+ os.Setenv("PORT", fmt.Sprintf("%d", port))
+
+ copy(osArgs, os.Args)
+
+ for _, c := range []struct {
+ args []string
+ wait func() bool
+ expectErr bool
+ }{
+ {
+ args: []string{"serve"},
+ wait: func() bool {
+ time.Sleep(time.Second * 5)
+ return !gotil.IsTCPPortAvailable(port)
+ },
+ },
+ {args: []string{"roles", "list", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"roles", "create", "role-a", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"roles", "get", "role-a", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"roles", "members", "add", "role-a", "member-a", "member-b", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"roles", "members", "remove", "role-a", "member-a", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"roles", "find", "member-a", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"roles", "delete", "role-a", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"policies", "create", "-i", "foobar", "-s", "peter,max", "-r", "blog,users", "-a", "post,ban", "--allow", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"policies", "actions", "add", "foobar", "update|create", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"policies", "actions", "remove", "foobar", "update|create", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"policies", "resources", "add", "foobar", "printer", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"policies", "resources", "remove", "foobar", "printer", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"policies", "subjects", "add", "foobar", "ken", "tracy", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"policies", "subjects", "remove", "foobar", "ken", "tracy", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"policies", "get", "foobar", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"policies", "delete", "foobar", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"warden", "authorize", "subject", "--subject", "foo", "--action", "bar", "--resource", "baz", "--url", fmt.Sprintf("http://127.0.0.1:%d", port)}},
+ {args: []string{"help", "migrate", "sql"}},
+ {args: []string{"help", "migrate", "hydra"}},
+ {args: []string{"version"}},
+ } {
+ c.args = append(c.args, []string{"--config", path}...)
+ RootCmd.SetArgs(c.args)
+
+ t.Run(fmt.Sprintf("command=%v", c.args), func(t *testing.T) {
+ if c.wait != nil {
+ go func() {
+ assert.Nil(t, RootCmd.Execute())
+ }()
+ }
+
+ if c.wait != nil {
+ var count = 0
+ for c.wait() {
+ t.Logf("Port not open yet, retrying attempt #%d...", count)
+ count++
+ if count > 200 {
+ t.FailNow()
+ }
+ time.Sleep(time.Second * 2)
+ }
+ } else {
+ err := RootCmd.Execute()
+ if c.expectErr {
+ assert.Error(t, err)
+ } else {
+ assert.NoError(t, err)
+ }
+ }
+ })
+ }
+}
diff --git a/cmd/serve.go b/cmd/serve.go
new file mode 100644
index 000000000..24d99fd12
--- /dev/null
+++ b/cmd/serve.go
@@ -0,0 +1,131 @@
+// Copyright © 2018 NAME HERE
+//
+// 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.
+
+package cmd
+
+import (
+ "os"
+ "strconv"
+
+ "github.com/ory/keto/cmd/server"
+ "github.com/spf13/cobra"
+)
+
+// serveCmd represents the serve command
+var serveCmd = &cobra.Command{
+ Use: "serve",
+ Short: "Starts the server and serves the HTTP REST API",
+ Long: `
+This command exposes a variety of controls via environment variables. You can
+set environments using "export KEY=VALUE" (Linux/macOS) or "set KEY=VALUE" (Windows). On Linux,
+you can also set environments by pre-pending key value pairs: "KEY=VALUE KEY2=VALUE2 hydra"
+
+All possible controls are listed below. The host process additionally exposes a few flags, which are listed below
+the controls section.
+
+
+CORE CONTROLS
+=============
+
+- DATABASE_URL: A URL to a persistent backend. Hydra supports various backends:
+ - Memory: If DATABASE_URL is "memory", data will be written to memory and is lost when you restart this instance.
+ Example: DATABASE_URL=memory
+
+ - Postgres: If DATABASE_URL is a DSN starting with postgres:// PostgreSQL will be used as storage backend.
+ Example: DATABASE_URL=postgres://user:password@host:123/database
+
+ If PostgreSQL is not serving TLS, append ?sslmode=disable to the url:
+ DATABASE_URL=postgres://user:password@host:123/database?sslmode=disable
+
+ - MySQL: If DATABASE_URL is a DSN starting with mysql:// MySQL will be used as storage backend.
+ Example: DATABASE_URL=mysql://user:password@tcp(host:123)/database?parseTime=true
+
+ Be aware that the ?parseTime=true parameter is mandatory, or timestamps will not work.
+
+- PORT: The port hydra should listen on.
+ Defaults to PORT=4466
+
+- HOST: The host interface hydra should listen on. Leave empty to listen on all interfaces.
+ Example: HOST=localhost
+
+- LOG_LEVEL: Set the log level, supports "panic", "fatal", "error", "warn", "info" and "debug". Defaults to "info".
+ Example: LOG_LEVEL=panic
+
+- LOG_FORMAT: Leave empty for text based log format, or set to "json" for JSON formatting.
+ Example: LOG_FORMAT="json"
+
+
+AUTHENTICATORS
+==============
+
+- The OAuth 2.0 Token Introspection Authenticator is capable of resolving OAuth2 access tokens to a subject and a set
+ of granted scopes using the OAuth 2.0 Introspection standard.
+
+ - OAUTH2_CLIENT_ID: The client ID to be used when performing the OAuth 2.0 Introspection request.
+ Example: OAUTH2_CLIENT_ID=my_client
+
+ - OAUTH2_CLIENT_SECRET: The client secret to be used when performing the OAuth 2.0 Introspection request.
+ Example: OAUTH2_CLIENT_ID=my_secret
+
+ - OAUTH2_SCOPES: The scope(s) (comma separated) required to perform the introspection request. If no scopes are
+ required, leave this value empty.
+ Example: OAUTH2_CLIENT_ID=scopeA,scopeB
+
+ - OAUTH2_TOKEN_URL: The OAuth2 Token Endpoint URL of the server
+ Example: OAUTH2_CLIENT_ID=https://my-server/oauth2/token
+
+ - OAUTH2_INTROSPECTION_URL: The OAuth2 Introspection Endpoint URL of the server
+ Example: OAUTH2_INTROSPECTION_URL=https://my-server/oauth2/introspection
+
+
+CORS CONTROLS
+==============
+- CORS_ALLOWED_ORIGINS: A list of origins (comma separated values) a cross-domain request can be executed from.
+ If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*)
+ to replace 0 or more characters (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality.
+ Only one wildcard can be used per origin. The default value is *.
+ Example: CORS_ALLOWED_ORIGINS=http://*.domain.com,http://*.domain2.com
+
+- CORS_ALLOWED_METHODS: A list of methods (comma separated values) the client is allowed to use with cross-domain
+ requests. Default value is simple methods (GET and POST).
+ Example: CORS_ALLOWED_METHODS=POST,GET,PUT
+
+- CORS_ALLOWED_CREDENTIALS: Indicates whether the request can include user credentials like cookies, HTTP authentication
+ or client side SSL certificates. The default is false.
+
+- CORS_DEBUG: Debugging flag adds additional output to debug server side CORS issues.
+
+- CORS_MAX_AGE: Indicates how long (in seconds) the results of a preflight request can be cached. The default is 0 which stands for no max age.
+
+- CORS_ALLOWED_HEADERS: A list of non simple headers (comma separated values) the client is allowed to use with cross-domain requests.
+
+- CORS_EXPOSED_HEADERS: Indicates which headers (comma separated values) are safe to expose to the API of a CORS API specification.
+
+
+DEBUG CONTROLS
+==============
+
+- PROFILING: Set "PROFILING=cpu" to enable cpu profiling and "PROFILING=memory" to enable memory profiling.
+ It is not possible to do both at the same time.
+ Example: PROFILING=cpu
+`,
+ Run: server.RunServe(logger, Version, GitHash, BuildTime),
+}
+
+func init() {
+ RootCmd.AddCommand(serveCmd)
+
+ disableTelemetryEnv, _ := strconv.ParseBool(os.Getenv("DISABLE_TELEMETRY"))
+ serveCmd.Flags().Bool("disable-telemetry", disableTelemetryEnv, "Disable anonymized telemetry reports - for more information please visit https://www.ory.sh/docs/guides/telemetry")
+}
diff --git a/cmd/server/migrate.go b/cmd/server/migrate.go
new file mode 100644
index 000000000..5f0e2e9fb
--- /dev/null
+++ b/cmd/server/migrate.go
@@ -0,0 +1,105 @@
+/*
+ * Copyright © 2015-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 server
+
+import (
+ "net/url"
+
+ "github.com/ory/keto/role"
+ "github.com/ory/ladon/manager/sql"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/cobra"
+ "github.com/spf13/viper"
+
+ "fmt"
+
+ _ "github.com/go-sql-driver/mysql"
+ _ "github.com/lib/pq"
+ "github.com/ory/keto/legacy"
+ "github.com/rubenv/sql-migrate"
+)
+
+func getMigrationSql(cmd *cobra.Command, args []string, logger *logrus.Logger) (string, *url.URL) {
+ var db string
+
+ if a, b := cmd.Flags().GetBool("read-from-env"); a && b == nil {
+ db = viper.GetString("DATABASE_URL")
+ } else {
+ if len(args) == 0 {
+ fmt.Print(cmd.UsageString())
+ logger.Fatalf("Argument 1 is missing")
+ }
+ db = args[0]
+ }
+
+ u, err := url.Parse(db)
+ if err != nil {
+ logger.WithError(err).WithField("database_url", db).Fatal("Unable to parse DATABASE_URL, make sure it has the right format")
+ }
+
+ return db, u
+}
+
+func RunMigrateSQL(logger *logrus.Logger) func(cmd *cobra.Command, args []string) {
+ return func(cmd *cobra.Command, args []string) {
+ dbUrl, u := getMigrationSql(cmd, args, logger)
+
+ db, err := connectToSql(dbUrl, u.Scheme)
+ if err != nil {
+ logger.WithError(err).WithField("database_url", u.Scheme+"://*:*@"+u.Host+u.Path+"?"+u.RawQuery).Fatal("Unable to parse DATABASE_URL, make sure it has the right format")
+ }
+
+ logger.Info("Applying SQL migrations...")
+ if n, err := role.NewSQLManager(db).CreateSchemas(); err != nil {
+ logger.WithError(err).WithField("migrations", n).WithField("table", "policies").Print("An error occurred while trying to apply SQL migrations")
+ } else {
+ logger.WithField("migrations", n).WithField("table", "role").Print("Successfully applied SQL migrations")
+ }
+
+ if n, err := sql.NewSQLManager(db, nil).CreateSchemas("", "keto_policy_migrations"); err != nil {
+ logger.WithError(err).WithField("migrations", n).WithField("table", "policies").Print("An error occurred while trying to apply SQL migrations")
+ } else {
+ logger.WithField("migrations", n).WithField("table", "policies").Print("Successfully applied SQL migrations")
+ }
+
+ logger.Info("Done applying SQL migrations")
+ }
+}
+
+func RunMigrateHydra(logger *logrus.Logger) func(cmd *cobra.Command, args []string) {
+ return func(cmd *cobra.Command, args []string) {
+ dbUrl, u := getMigrationSql(cmd, args, logger)
+
+ db, err := connectToSql(dbUrl, u.Scheme)
+ if err != nil {
+ logger.WithError(err).WithField("database_url", u.Scheme+"://*:*@"+u.Host+u.Path+"?"+u.RawQuery).Fatal("Unable to parse DATABASE_URL, make sure it has the right format")
+ }
+
+ migrate.SetTable("keto_legacy_hydra_migrations")
+ n, err := migrate.Exec(db.DB, db.DriverName(), legacy.HydraLegacyMigrations[db.DriverName()], migrate.Up)
+ if err != nil {
+ logger.WithError(err).WithField("migrations", n).WithField("table", "policies").Print("An error occurred while trying to apply SQL migrations")
+ }
+ logger.WithField("migrations", n).WithField("table", "role").Print("Successfully applied SQL migrations")
+ logger.Info("Done applying SQL migrations")
+ }
+}
diff --git a/cmd/server/serve.go b/cmd/server/serve.go
new file mode 100644
index 000000000..a9495fa79
--- /dev/null
+++ b/cmd/server/serve.go
@@ -0,0 +1,142 @@
+/*
+ * Copyright © 2015-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 server
+
+import (
+ "fmt"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "github.com/gorilla/context"
+ "github.com/julienschmidt/httprouter"
+ "github.com/meatballhat/negroni-logrus"
+ "github.com/ory/graceful"
+ "github.com/ory/herodot"
+ "github.com/ory/keto/authentication"
+ "github.com/ory/keto/policy"
+ "github.com/ory/keto/role"
+ "github.com/ory/keto/warden"
+ "github.com/ory/ladon"
+ "github.com/ory/metrics-middleware"
+ "github.com/rs/cors"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/cobra"
+ "github.com/spf13/viper"
+ "github.com/urfave/negroni"
+)
+
+func parseCorsOptions() cors.Options {
+ allowCredentials, _ := strconv.ParseBool(viper.GetString("CORS_ALLOWED_CREDENTIALS"))
+ debug, _ := strconv.ParseBool(viper.GetString("CORS_DEBUG"))
+ maxAge, _ := strconv.Atoi(viper.GetString("CORS_MAX_AGE"))
+ return cors.Options{
+ AllowedOrigins: strings.Split(viper.GetString("CORS_ALLOWED_ORIGINS"), ","),
+ AllowedMethods: strings.Split(viper.GetString("CORS_ALLOWED_METHODS"), ","),
+ AllowedHeaders: strings.Split(viper.GetString("CORS_ALLOWED_HEADERS"), ","),
+ ExposedHeaders: strings.Split(viper.GetString("CORS_EXPOSED_HEADERS"), ","),
+ AllowCredentials: allowCredentials,
+ MaxAge: maxAge,
+ Debug: debug,
+ }
+}
+
+func RunServe(
+ logger *logrus.Logger,
+ buildVersion, buildHash string, buildTime string,
+) func(cmd *cobra.Command, args []string) {
+ return func(cmd *cobra.Command, args []string) {
+ router := httprouter.New()
+
+ m, err := newManagers(viper.GetString("DATABASE_URL"), logger)
+ if err != nil {
+ logger.
+ WithError(err).
+ Fatal("Unable to initialise backends")
+ }
+
+ authenticators := map[string]authentication.Authenticator{
+ "subjects": authentication.NewPlaintextAuthentication(),
+ "oauth2": authentication.NewOAuth2IntrospectionAuthentication(
+ viper.GetString("OAUTH2_CLIENT_ID"),
+ viper.GetString("OAUTH2_CLIENT_SECRET"),
+ viper.GetString("OAUTH2_TOKEN_URL"),
+ viper.GetString("OAUTH2_INTROSPECTION_URL"),
+ strings.Split(viper.GetString("OAUTH2_SCOPES"), ","),
+ ),
+ }
+
+ decider := &ladon.Ladon{
+ Manager: m.policyManager,
+ AuditLogger: &warden.AuditLoggerLogrus{Logger: logger},
+ Matcher: ladon.DefaultMatcher,
+ }
+ firewall := warden.NewWarden(decider, m.roleManager, logger)
+ writer := herodot.NewJSONWriter(logger)
+ roleHandler := role.NewHandler(m.roleManager, writer)
+ policyHandler := policy.NewHandler(m.policyManager, writer)
+ wardenHandler := warden.NewHandler(writer, firewall, authenticators)
+
+ roleHandler.SetRoutes(router)
+ policyHandler.SetRoutes(router)
+ wardenHandler.SetRoutes(router)
+
+ n := negroni.New()
+ n.Use(negronilogrus.NewMiddlewareFromLogger(logger, "keto"))
+ n.UseHandler(router)
+ corsHandler := cors.New(parseCorsOptions()).Handler(n)
+
+ if ok, _ := cmd.Flags().GetBool("disable-telemetry"); !ok {
+ m := metrics.NewMetricsManager(
+ metrics.Hash("DATABASE_URL"),
+ viper.GetString("DATABASE_URL") != "memory",
+ "jk32cFATnj9GKbQdFL7fBB9qtKZdX9j7",
+ []string{
+ "/policies",
+ "/roles",
+ "/warden/oauth2/authorize",
+ "/warden/subjects/authorize",
+ },
+ nil,
+ )
+ go m.RegisterSegment(buildVersion, buildHash, buildTime)
+ go m.CommitMemoryStatistics()
+ n.Use(m)
+ }
+
+ address := fmt.Sprintf("%s:%s", viper.GetString("HOST"), viper.GetString("PORT"))
+ var srv = graceful.WithDefaults(&http.Server{
+ Addr: address,
+ Handler: context.ClearHandler(corsHandler),
+ })
+
+ if err := graceful.Graceful(func() error {
+ logger.Infof("Setting up http server on %s", address)
+ return srv.ListenAndServe()
+ }, srv.Shutdown); err != nil {
+ logger.
+ WithError(err).
+ Fatal("Could not gracefully run server")
+ }
+
+ }
+}
diff --git a/cmd/server/sql.go b/cmd/server/sql.go
new file mode 100644
index 000000000..416cb8c1a
--- /dev/null
+++ b/cmd/server/sql.go
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2015-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 server
+
+import (
+ "net/url"
+ "runtime"
+ "time"
+
+ _ "github.com/go-sql-driver/mysql"
+ "github.com/jmoiron/sqlx"
+ _ "github.com/lib/pq"
+ "github.com/ory/keto/role"
+ "github.com/ory/ladon"
+ "github.com/ory/ladon/manager/memory"
+ "github.com/ory/ladon/manager/sql"
+ "github.com/ory/sqlcon"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+func connectToSql(url string, dbt string) (*sqlx.DB, error) {
+ db, err := sqlx.Open(dbt, url)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ maxConns := maxParallelism() * 2
+ maxConnLifetime := time.Duration(0)
+ maxIdleConns := maxParallelism()
+ db.SetMaxOpenConns(maxConns)
+ db.SetMaxIdleConns(maxIdleConns)
+ db.SetConnMaxLifetime(maxConnLifetime)
+ return db, nil
+}
+
+func maxParallelism() int {
+ maxProcs := runtime.GOMAXPROCS(0)
+ numCPU := runtime.NumCPU()
+ if maxProcs < numCPU {
+ return maxProcs
+ }
+ return numCPU
+}
+
+type managers struct {
+ roleManager role.Manager
+ policyManager ladon.Manager
+}
+
+func newManagers(db string, logger logrus.FieldLogger) (*managers, error) {
+ if db == "memory" {
+ return &managers{
+ roleManager: role.NewMemoryManager(),
+ policyManager: memory.NewMemoryManager(),
+ }, nil
+ } else if db == "" {
+ return nil, errors.New("No database URL provided")
+ }
+
+ u, err := url.Parse(db)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ switch u.Scheme {
+ case "postgres":
+ case "mysql":
+ sdb, err := sqlcon.NewSQLConnection(db, logger)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ return &managers{
+ roleManager: role.NewSQLManager(sdb.GetDatabase()),
+ policyManager: sql.NewSQLManager(sdb.GetDatabase(), nil),
+ }, nil
+ }
+
+ return nil, errors.Errorf("The provided database URL %s can not be handled", db)
+}
diff --git a/cmd/version.go b/cmd/version.go
new file mode 100644
index 000000000..271198a8e
--- /dev/null
+++ b/cmd/version.go
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2015-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 cmd
+
+import (
+ "fmt"
+
+ "github.com/spf13/cobra"
+)
+
+// versionCmd represents the version command
+var versionCmd = &cobra.Command{
+ Use: "version",
+ Short: "Display this build's version, build time, and git hash",
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Printf("Version: %s\n", Version)
+ fmt.Printf("Git Hash: %s\n", GitHash)
+ fmt.Printf("Build Time: %s\n", BuildTime)
+ },
+}
+
+func init() {
+ RootCmd.AddCommand(versionCmd)
+}
diff --git a/cmd/warden.go b/cmd/warden.go
new file mode 100644
index 000000000..cea4a50c9
--- /dev/null
+++ b/cmd/warden.go
@@ -0,0 +1,45 @@
+// Copyright © 2018 NAME HERE
+//
+// 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.
+
+package cmd
+
+import (
+ "fmt"
+
+ "github.com/spf13/cobra"
+)
+
+// wardenCmd represents the warden command
+var wardenCmd = &cobra.Command{
+ Use: "warden",
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Print(cmd.UsageString())
+ },
+}
+
+func init() {
+ RootCmd.AddCommand(wardenCmd)
+ clientDefaultFlags(wardenCmd)
+
+ // Here you will define your flags and configuration settings.
+
+ // Cobra supports Persistent Flags which will work for this command
+ // and all subcommands, e.g.:
+ // wardenCmd.PersistentFlags().String("foo", "", "A help for foo")
+
+ // Cobra supports local flags which will only run when this command
+ // is called directly, e.g.:
+ // wardenCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+
+}
diff --git a/cmd/warden_authorize.go b/cmd/warden_authorize.go
new file mode 100644
index 000000000..c80f73b75
--- /dev/null
+++ b/cmd/warden_authorize.go
@@ -0,0 +1,44 @@
+// Copyright © 2018 NAME HERE
+//
+// 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.
+
+package cmd
+
+import (
+ "fmt"
+
+ "github.com/spf13/cobra"
+)
+
+// authorizeCmd represents the authorize command
+var authorizeCmd = &cobra.Command{
+ Use: "authorize",
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Print(cmd.UsageString())
+ },
+}
+
+func init() {
+ wardenCmd.AddCommand(authorizeCmd)
+
+ // Here you will define your flags and configuration settings.
+
+ // Cobra supports Persistent Flags which will work for this command
+ // and all subcommands, e.g.:
+ // authorizeCmd.PersistentFlags().String("foo", "", "A help for foo")
+
+ // Cobra supports local flags which will only run when this command
+ // is called directly, e.g.:
+ // authorizeCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+
+}
diff --git a/cmd/warden_oauth2.go b/cmd/warden_oauth2.go
new file mode 100644
index 000000000..d11fa1985
--- /dev/null
+++ b/cmd/warden_oauth2.go
@@ -0,0 +1,44 @@
+// Copyright © 2018 NAME HERE
+//
+// 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.
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// oauth2Cmd represents the oauth2 command
+var oauth2Cmd = &cobra.Command{
+ Use: "oauth2",
+ Short: "Checks if an OAuth 2.0 Access Token is authorized to perform a certain request",
+ Run: cmdHandler.Warden.IsOAuth2AccessTokenAuthorized,
+}
+
+func init() {
+ authorizeCmd.AddCommand(oauth2Cmd)
+
+ // Here you will define your flags and configuration settings.
+
+ // Cobra supports Persistent Flags which will work for this command
+ // and all subcommands, e.g.:
+ // oauth2Cmd.PersistentFlags().String("foo", "", "A help for foo")
+
+ // Cobra supports local flags which will only run when this command
+ // is called directly, e.g.:
+ // oauth2Cmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+ oauth2Cmd.Flags().String("token", "", "The request's bearer token")
+ oauth2Cmd.Flags().StringArray("scopes", []string{}, "The request's required scopes")
+ oauth2Cmd.Flags().String("action", "", "The request's action")
+ oauth2Cmd.Flags().String("resource", "", "The request's resource")
+}
diff --git a/cmd/warden_subject.go b/cmd/warden_subject.go
new file mode 100644
index 000000000..8eabb7ce0
--- /dev/null
+++ b/cmd/warden_subject.go
@@ -0,0 +1,43 @@
+// Copyright © 2018 NAME HERE
+//
+// 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.
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+// subjectCmd represents the subject command
+var subjectCmd = &cobra.Command{
+ Use: "subject",
+ Short: "Checks if a subject is authorized to perform a certain request",
+ Run: cmdHandler.Warden.IsSubjectAuthorized,
+}
+
+func init() {
+ authorizeCmd.AddCommand(subjectCmd)
+
+ // Here you will define your flags and configuration settings.
+
+ // Cobra supports Persistent Flags which will work for this command
+ // and all subcommands, e.g.:
+ // subjectCmd.PersistentFlags().String("foo", "", "A help for foo")
+
+ // Cobra supports local flags which will only run when this command
+ // is called directly, e.g.:
+
+ subjectCmd.Flags().String("subject", "", "The request's subject")
+ subjectCmd.Flags().String("action", "", "The request's action")
+ subjectCmd.Flags().String("resource", "", "The request's resource")
+}
diff --git a/doc.go b/doc.go
new file mode 100644
index 000000000..11dbb8c68
--- /dev/null
+++ b/doc.go
@@ -0,0 +1,31 @@
+// Package main ORY Keto
+//
+// Schemes: http, https
+// Host:
+// BasePath: /
+// Version: Latest
+// License: Apache 2.0 https://github.com/ory/keto/blob/master/LICENSE
+// Contact: ORY https://www.ory.sh
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// swagger:meta
+package main
+
+// Copyright © 2017 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.
diff --git a/doc_swagger.go b/doc_swagger.go
new file mode 100644
index 000000000..9466ef0a4
--- /dev/null
+++ b/doc_swagger.go
@@ -0,0 +1,40 @@
+// Copyright © 2017 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.
+
+package main
+
+// The standard error format
+//
+// swagger:response genericError
+type genericError struct {
+ // in: body
+ Body struct {
+ Code int `json:"code,omitempty"`
+
+ Status string `json:"status,omitempty"`
+
+ Request string `json:"request,omitempty"`
+
+ Reason string `json:"reason,omitempty"`
+
+ Details []map[string]interface{} `json:"details,omitempty"`
+
+ Message string `json:"message"`
+ }
+}
+
+// An empty response
+//
+// swagger:response emptyResponse
+type emptyResponse struct{}
diff --git a/docs/api.swagger.json b/docs/api.swagger.json
new file mode 100644
index 000000000..17b611117
--- /dev/null
+++ b/docs/api.swagger.json
@@ -0,0 +1,1279 @@
+{
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "swagger": "2.0",
+ "info": {
+ "description": "Package main ORY Keto",
+ "contact": {
+ "name": "ORY",
+ "url": "https://www.ory.sh",
+ "email": "hi@ory.am"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "https://github.com/ory/keto/blob/master/LICENSE"
+ },
+ "version": "Latest"
+ },
+ "basePath": "/",
+ "paths": {
+ "/policies": {
+ "get": {
+ "description": "List Access Control Policies",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "tags": [
+ "policy"
+ ],
+ "operationId": "listPolicies",
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "Offset",
+ "description": "The offset from where to start looking.",
+ "name": "offset",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "Limit",
+ "description": "The maximum amount of policies returned.",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/policyList"
+ },
+ "401": {
+ "$ref": "#/responses/genericError"
+ },
+ "403": {
+ "$ref": "#/responses/genericError"
+ },
+ "500": {
+ "$ref": "#/responses/genericError"
+ }
+ }
+ },
+ "post": {
+ "description": "Create an Access Control Policy",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "tags": [
+ "policy"
+ ],
+ "operationId": "createPolicy",
+ "parameters": [
+ {
+ "name": "Body",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/policy"
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "policy",
+ "schema": {
+ "$ref": "#/definitions/policy"
+ }
+ },
+ "401": {
+ "$ref": "#/responses/genericError"
+ },
+ "403": {
+ "$ref": "#/responses/genericError"
+ },
+ "500": {
+ "$ref": "#/responses/genericError"
+ }
+ }
+ }
+ },
+ "/policies/{id}": {
+ "get": {
+ "description": "Get an Access Control Policy",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "tags": [
+ "policy"
+ ],
+ "operationId": "getPolicy",
+ "parameters": [
+ {
+ "type": "string",
+ "x-go-name": "ID",
+ "description": "The id of the policy.",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "policy",
+ "schema": {
+ "$ref": "#/definitions/policy"
+ }
+ },
+ "401": {
+ "$ref": "#/responses/genericError"
+ },
+ "403": {
+ "$ref": "#/responses/genericError"
+ },
+ "500": {
+ "$ref": "#/responses/genericError"
+ }
+ }
+ },
+ "put": {
+ "security": [
+ {
+ "oauth2": [
+ "hydra.policies"
+ ]
+ }
+ ],
+ "description": "Update an Access Control Policy",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "tags": [
+ "policy"
+ ],
+ "operationId": "updatePolicy",
+ "parameters": [
+ {
+ "type": "string",
+ "x-go-name": "ID",
+ "description": "The id of the policy.",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "name": "Body",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/policy"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "policy",
+ "schema": {
+ "$ref": "#/definitions/policy"
+ }
+ },
+ "401": {
+ "$ref": "#/responses/genericError"
+ },
+ "403": {
+ "$ref": "#/responses/genericError"
+ },
+ "500": {
+ "$ref": "#/responses/genericError"
+ }
+ }
+ },
+ "delete": {
+ "security": [
+ {
+ "oauth2": [
+ "hydra.policies"
+ ]
+ }
+ ],
+ "description": "Delete an Access Control Policy",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "tags": [
+ "policy"
+ ],
+ "operationId": "deletePolicy",
+ "parameters": [
+ {
+ "type": "string",
+ "x-go-name": "ID",
+ "description": "The id of the policy.",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "$ref": "#/responses/emptyResponse"
+ },
+ "401": {
+ "$ref": "#/responses/genericError"
+ },
+ "403": {
+ "$ref": "#/responses/genericError"
+ },
+ "500": {
+ "$ref": "#/responses/genericError"
+ }
+ }
+ }
+ },
+ "/roles": {
+ "get": {
+ "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to retrieve all roles that are stored in the system.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "tags": [
+ "role"
+ ],
+ "summary": "List all roles",
+ "operationId": "listRoles",
+ "parameters": [
+ {
+ "type": "string",
+ "x-go-name": "Member",
+ "description": "The id of the member to look up.",
+ "name": "member",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "Limit",
+ "description": "The maximum amount of policies returned.",
+ "name": "limit",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "Offset",
+ "description": "The offset from where to start looking.",
+ "name": "offset",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/listRolesResponse"
+ },
+ "401": {
+ "$ref": "#/responses/genericError"
+ },
+ "403": {
+ "$ref": "#/responses/genericError"
+ },
+ "500": {
+ "$ref": "#/responses/genericError"
+ }
+ }
+ },
+ "post": {
+ "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to create a new role. You may define members as well but you don't have to.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "tags": [
+ "role"
+ ],
+ "summary": "Create a role",
+ "operationId": "createRole",
+ "parameters": [
+ {
+ "name": "Body",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/role"
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "role",
+ "schema": {
+ "$ref": "#/definitions/role"
+ }
+ },
+ "401": {
+ "$ref": "#/responses/genericError"
+ },
+ "403": {
+ "$ref": "#/responses/genericError"
+ },
+ "500": {
+ "$ref": "#/responses/genericError"
+ }
+ }
+ }
+ },
+ "/roles/{id}": {
+ "get": {
+ "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to retrieve an existing role. You have to know the role's ID.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "tags": [
+ "role"
+ ],
+ "summary": "Get a role by its ID",
+ "operationId": "getRole",
+ "parameters": [
+ {
+ "type": "string",
+ "x-go-name": "ID",
+ "description": "The id of the role to look up.",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "role",
+ "schema": {
+ "$ref": "#/definitions/role"
+ }
+ },
+ "401": {
+ "$ref": "#/responses/genericError"
+ },
+ "403": {
+ "$ref": "#/responses/genericError"
+ },
+ "500": {
+ "$ref": "#/responses/genericError"
+ }
+ }
+ },
+ "delete": {
+ "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to delete an existing role. You have to know the role's ID.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "tags": [
+ "role"
+ ],
+ "summary": "Get a role by its ID",
+ "operationId": "deleteRole",
+ "parameters": [
+ {
+ "type": "string",
+ "x-go-name": "ID",
+ "description": "The id of the role to look up.",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "$ref": "#/responses/emptyResponse"
+ },
+ "401": {
+ "$ref": "#/responses/genericError"
+ },
+ "403": {
+ "$ref": "#/responses/genericError"
+ },
+ "500": {
+ "$ref": "#/responses/genericError"
+ }
+ }
+ }
+ },
+ "/roles/{id}/members": {
+ "post": {
+ "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to add members (users, applications, ...) to a specific role. You have to know the role's ID.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "tags": [
+ "role"
+ ],
+ "summary": "Add members to a role",
+ "operationId": "addMembersToRole",
+ "parameters": [
+ {
+ "type": "string",
+ "x-go-name": "ID",
+ "description": "The id of the role to modify.",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "name": "Body",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/roleMembers"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "$ref": "#/responses/emptyResponse"
+ },
+ "401": {
+ "$ref": "#/responses/genericError"
+ },
+ "403": {
+ "$ref": "#/responses/genericError"
+ },
+ "500": {
+ "$ref": "#/responses/genericError"
+ }
+ }
+ },
+ "delete": {
+ "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to remove members (users, applications, ...) from a specific role. You have to know the role's ID.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "tags": [
+ "role"
+ ],
+ "summary": "Remove members from a role",
+ "operationId": "removeMembersFromRole",
+ "parameters": [
+ {
+ "type": "string",
+ "x-go-name": "ID",
+ "description": "The id of the role to modify.",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "name": "Body",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/roleMembers"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "$ref": "#/responses/emptyResponse"
+ },
+ "401": {
+ "$ref": "#/responses/genericError"
+ },
+ "403": {
+ "$ref": "#/responses/genericError"
+ },
+ "500": {
+ "$ref": "#/responses/genericError"
+ }
+ }
+ }
+ },
+ "/warden/oauth2/authorize": {
+ "post": {
+ "description": "Checks if a token is valid and if the token subject is allowed to perform an action on a resource.\nThis endpoint requires a token, a scope, a resource name, an action name and a context.\n\n\nIf a token is expired/invalid, has not been granted the requested scope or the subject is not allowed to\nperform the action on the resource, this endpoint returns a 200 response with `{ \"allowed\": false }`.\n\n\nThis endpoint passes all data from the upstream OAuth 2.0 token introspection endpoint. If you use ORY Hydra as an\nupstream OAuth 2.0 provider, data set through the `accessTokenExtra` field in the consent flow will be included in this\nresponse as well.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "tags": [
+ "warden"
+ ],
+ "summary": "Check if an OAuth 2.0 access token is authorized to access a resource",
+ "operationId": "isOAuth2AccessTokenAuthorized",
+ "parameters": [
+ {
+ "name": "Body",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/WardenOAuth2AuthorizationRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/wardenOAuth2Authorization"
+ },
+ "401": {
+ "$ref": "#/responses/genericError"
+ },
+ "403": {
+ "$ref": "#/responses/genericError"
+ },
+ "500": {
+ "$ref": "#/responses/genericError"
+ }
+ }
+ }
+ },
+ "/warden/subjects/authorize": {
+ "post": {
+ "description": "Checks if a subject (e.g. user ID, API key, ...) is allowed to perform a certain action on a resource.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "tags": [
+ "warden"
+ ],
+ "summary": "Check if a subject is authorized to access a resource",
+ "operationId": "isSubjectAuthorized",
+ "parameters": [
+ {
+ "name": "Body",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/WardenSubjectAuthorizationRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/wardenSubjectAuthorization"
+ },
+ "401": {
+ "$ref": "#/responses/genericError"
+ },
+ "403": {
+ "$ref": "#/responses/genericError"
+ },
+ "500": {
+ "$ref": "#/responses/genericError"
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "AuthenticationOAuth2IntrospectionRequest": {
+ "type": "object",
+ "properties": {
+ "scopes": {
+ "description": "Scopes is an array of scopes that are required.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "x-go-name": "Scopes"
+ },
+ "token": {
+ "description": "Token is the token to introspect.",
+ "type": "string",
+ "x-go-name": "Token"
+ }
+ },
+ "x-go-package": "github.com/ory/keto/authentication"
+ },
+ "Authenticator": {
+ "type": "object",
+ "x-go-package": "github.com/ory/keto/authentication"
+ },
+ "Firewall": {
+ "type": "object",
+ "title": "Firewall offers various validation strategies for access tokens.",
+ "x-go-package": "github.com/ory/keto/warden"
+ },
+ "Handler": {
+ "type": "object",
+ "properties": {
+ "H": {
+ "$ref": "#/definitions/Writer"
+ },
+ "Manager": {
+ "$ref": "#/definitions/Manager"
+ }
+ },
+ "x-go-package": "github.com/ory/keto/role"
+ },
+ "IntrospectionResponse": {
+ "type": "object",
+ "properties": {
+ "active": {
+ "type": "boolean",
+ "x-go-name": "Active"
+ },
+ "aud": {
+ "type": "string",
+ "x-go-name": "Audience"
+ },
+ "client_id": {
+ "type": "string",
+ "x-go-name": "ClientID"
+ },
+ "exp": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "ExpiresAt"
+ },
+ "iat": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "IssuedAt"
+ },
+ "iss": {
+ "type": "string",
+ "x-go-name": "Issuer"
+ },
+ "nbf": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "NotBefore"
+ },
+ "scope": {
+ "type": "string",
+ "x-go-name": "Scope"
+ },
+ "sub": {
+ "description": "Here, it's sub",
+ "type": "string",
+ "x-go-name": "Subject"
+ },
+ "username": {
+ "type": "string",
+ "x-go-name": "Username"
+ }
+ },
+ "x-go-package": "github.com/ory/keto/authentication"
+ },
+ "Manager": {
+ "type": "object",
+ "x-go-package": "github.com/ory/keto/role"
+ },
+ "OAuth2IntrospectionAuthentication": {
+ "type": "object",
+ "x-go-package": "github.com/ory/keto/authentication"
+ },
+ "Session": {
+ "type": "object",
+ "properties": {
+ "GetSubject": {
+ "type": "string"
+ }
+ },
+ "x-go-package": "github.com/ory/keto/authentication"
+ },
+ "WardenOAuth2AuthorizationRequest": {
+ "type": "object",
+ "properties": {
+ "action": {
+ "description": "Action is the action that is requested on the resource.",
+ "type": "string",
+ "x-go-name": "Action"
+ },
+ "context": {
+ "description": "Context is the request's environmental context.",
+ "type": "object",
+ "additionalProperties": {
+ "type": "object"
+ },
+ "x-go-name": "Context"
+ },
+ "resource": {
+ "description": "Resource is the resource that access is requested to.",
+ "type": "string",
+ "x-go-name": "Resource"
+ },
+ "scopes": {
+ "description": "Scopes is an array of scopes that are required.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "x-go-name": "Scopes"
+ },
+ "token": {
+ "description": "Token is the token to introspect.",
+ "type": "string",
+ "x-go-name": "Token"
+ }
+ },
+ "x-go-name": "swaggerWardenTokenAccessRequest",
+ "x-go-package": "github.com/ory/keto/warden"
+ },
+ "WardenSubjectAuthorizationRequest": {
+ "type": "object",
+ "title": "AccessRequest is the warden's request object.",
+ "properties": {
+ "action": {
+ "description": "Action is the action that is requested on the resource.",
+ "type": "string",
+ "x-go-name": "Action"
+ },
+ "context": {
+ "description": "Context is the request's environmental context.",
+ "type": "object",
+ "additionalProperties": {
+ "type": "object"
+ },
+ "x-go-name": "Context"
+ },
+ "resource": {
+ "description": "Resource is the resource that access is requested to.",
+ "type": "string",
+ "x-go-name": "Resource"
+ },
+ "subject": {
+ "description": "Subejct is the subject that is requesting access.",
+ "type": "string",
+ "x-go-name": "Subject"
+ }
+ },
+ "x-go-name": "AccessRequest",
+ "x-go-package": "github.com/ory/keto/warden"
+ },
+ "Writer": {
+ "description": "Writer is a helper to write arbitrary data to a ResponseWriter",
+ "type": "object",
+ "x-go-package": "github.com/ory/keto/vendor/github.com/ory/herodot"
+ },
+ "authenticationDefaultSession": {
+ "type": "object",
+ "properties": {
+ "allowed": {
+ "description": "Allowed is true if the request is allowed and false otherwise.",
+ "type": "boolean",
+ "x-go-name": "Allowed"
+ },
+ "subject": {
+ "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.",
+ "type": "string",
+ "x-go-name": "Subject"
+ }
+ },
+ "x-go-name": "DefaultSession",
+ "x-go-package": "github.com/ory/keto/authentication"
+ },
+ "authenticationOAuth2Session": {
+ "type": "object",
+ "properties": {
+ "accessTokenExtra": {
+ "description": "Extra represents arbitrary session data.",
+ "type": "object",
+ "additionalProperties": {
+ "type": "object"
+ },
+ "x-go-name": "Extra"
+ },
+ "allowed": {
+ "description": "Allowed is true if the request is allowed and false otherwise.",
+ "type": "boolean",
+ "x-go-name": "Allowed"
+ },
+ "audience": {
+ "type": "string",
+ "x-go-name": "Audience"
+ },
+ "clientId": {
+ "description": "ClientID is the id of the OAuth2 client that requested the token.",
+ "type": "string",
+ "x-go-name": "ClientID"
+ },
+ "expiresAt": {
+ "description": "ExpiresAt is the expiry timestamp.",
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "ExpiresAt"
+ },
+ "grantedScopes": {
+ "description": "GrantedScopes is a list of scopes that the subject authorized when asked for consent.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "x-go-name": "GrantedScopes"
+ },
+ "issuedAt": {
+ "description": "IssuedAt is the token creation time stamp.",
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "IssuedAt"
+ },
+ "issuer": {
+ "description": "Issuer is the id of the issuer, typically an hydra instance.",
+ "type": "string",
+ "x-go-name": "Issuer"
+ },
+ "notBefore": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "NotBefore"
+ },
+ "subject": {
+ "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.",
+ "type": "string",
+ "x-go-name": "Subject"
+ },
+ "username": {
+ "type": "string",
+ "x-go-name": "Username"
+ }
+ },
+ "x-go-name": "OAuth2Session",
+ "x-go-package": "github.com/ory/keto/authentication"
+ },
+ "policy": {
+ "type": "object",
+ "properties": {
+ "actions": {
+ "description": "Actions impacted by the policy.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "x-go-name": "Actions"
+ },
+ "conditions": {
+ "description": "Conditions under which the policy is active.",
+ "type": "object",
+ "additionalProperties": {
+ "type": "object",
+ "properties": {
+ "options": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "object"
+ },
+ "x-go-name": "Options"
+ },
+ "type": {
+ "type": "string",
+ "x-go-name": "Type"
+ }
+ }
+ },
+ "x-go-name": "Conditions"
+ },
+ "description": {
+ "description": "Description of the policy.",
+ "type": "string",
+ "x-go-name": "Description"
+ },
+ "effect": {
+ "description": "Effect of the policy",
+ "type": "string",
+ "x-go-name": "Effect"
+ },
+ "id": {
+ "description": "ID of the policy.",
+ "type": "string",
+ "x-go-name": "ID"
+ },
+ "resources": {
+ "description": "Resources impacted by the policy.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "x-go-name": "Resources"
+ },
+ "subjects": {
+ "description": "Subjects impacted by the policy.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "x-go-name": "Subjects"
+ }
+ },
+ "x-go-name": "swaggerPolicy",
+ "x-go-package": "github.com/ory/keto/policy"
+ },
+ "role": {
+ "description": "Role represents a group of users that share the same role. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.",
+ "type": "object",
+ "properties": {
+ "id": {
+ "description": "ID is the role's unique id.",
+ "type": "string",
+ "x-go-name": "ID"
+ },
+ "members": {
+ "description": "Members is who belongs to the role.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "x-go-name": "Members"
+ }
+ },
+ "x-go-name": "Role",
+ "x-go-package": "github.com/ory/keto/role"
+ },
+ "roleMembers": {
+ "type": "object",
+ "properties": {
+ "members": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "x-go-name": "Members"
+ }
+ },
+ "x-go-name": "membersRequest",
+ "x-go-package": "github.com/ory/keto/role"
+ },
+ "swaggerCreatePolicyParameters": {
+ "type": "object",
+ "properties": {
+ "Body": {
+ "$ref": "#/definitions/policy"
+ }
+ },
+ "x-go-package": "github.com/ory/keto/policy"
+ },
+ "swaggerDoesWardenAllowAccessRequestParameters": {
+ "type": "object",
+ "properties": {
+ "Body": {
+ "$ref": "#/definitions/WardenSubjectAuthorizationRequest"
+ }
+ },
+ "x-go-package": "github.com/ory/keto/warden"
+ },
+ "swaggerDoesWardenAllowTokenAccessRqeuestParameters": {
+ "type": "object",
+ "properties": {
+ "Body": {
+ "$ref": "#/definitions/WardenOAuth2AuthorizationRequest"
+ }
+ },
+ "x-go-package": "github.com/ory/keto/warden"
+ },
+ "swaggerGetPolicyParameters": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "description": "The id of the policy.\nin: path",
+ "type": "string",
+ "x-go-name": "ID"
+ }
+ },
+ "x-go-package": "github.com/ory/keto/policy"
+ },
+ "swaggerListPolicyParameters": {
+ "type": "object",
+ "properties": {
+ "limit": {
+ "description": "The maximum amount of policies returned.\nin: query",
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "Limit"
+ },
+ "offset": {
+ "description": "The offset from where to start looking.\nin: query",
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "Offset"
+ }
+ },
+ "x-go-package": "github.com/ory/keto/policy"
+ },
+ "swaggerListPolicyResponse": {
+ "description": "A policy",
+ "type": "object",
+ "properties": {
+ "Body": {
+ "description": "in: body\ntype: array",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/policy"
+ }
+ }
+ },
+ "x-go-package": "github.com/ory/keto/policy"
+ },
+ "swaggerUpdatePolicyParameters": {
+ "type": "object",
+ "properties": {
+ "Body": {
+ "$ref": "#/definitions/policy"
+ },
+ "id": {
+ "description": "The id of the policy.\nin: path",
+ "type": "string",
+ "x-go-name": "ID"
+ }
+ },
+ "x-go-package": "github.com/ory/keto/policy"
+ },
+ "swaggerWardenBaseRequest": {
+ "description": "swager:model authorizedBaseRequest",
+ "type": "object",
+ "properties": {
+ "action": {
+ "description": "Action is the action that is requested on the resource.",
+ "type": "string",
+ "x-go-name": "Action"
+ },
+ "context": {
+ "description": "Context is the request's environmental context.",
+ "type": "object",
+ "additionalProperties": {
+ "type": "object"
+ },
+ "x-go-name": "Context"
+ },
+ "resource": {
+ "description": "Resource is the resource that access is requested to.",
+ "type": "string",
+ "x-go-name": "Resource"
+ }
+ },
+ "x-go-package": "github.com/ory/keto/warden"
+ },
+ "wardenOAuth2AuthorizationResponse": {
+ "type": "object",
+ "properties": {
+ "accessTokenExtra": {
+ "description": "Extra represents arbitrary session data.",
+ "type": "object",
+ "additionalProperties": {
+ "type": "object"
+ },
+ "x-go-name": "Extra"
+ },
+ "allowed": {
+ "description": "Allowed is true if the request is allowed and false otherwise.",
+ "type": "boolean",
+ "x-go-name": "Allowed"
+ },
+ "audience": {
+ "type": "string",
+ "x-go-name": "Audience"
+ },
+ "clientId": {
+ "description": "ClientID is the id of the OAuth2 client that requested the token.",
+ "type": "string",
+ "x-go-name": "ClientID"
+ },
+ "expiresAt": {
+ "description": "ExpiresAt is the expiry timestamp.",
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "ExpiresAt"
+ },
+ "grantedScopes": {
+ "description": "GrantedScopes is a list of scopes that the subject authorized when asked for consent.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "x-go-name": "GrantedScopes"
+ },
+ "issuedAt": {
+ "description": "IssuedAt is the token creation time stamp.",
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "IssuedAt"
+ },
+ "issuer": {
+ "description": "Issuer is the id of the issuer, typically an hydra instance.",
+ "type": "string",
+ "x-go-name": "Issuer"
+ },
+ "notBefore": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "NotBefore"
+ },
+ "subject": {
+ "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.",
+ "type": "string",
+ "x-go-name": "Subject"
+ },
+ "username": {
+ "type": "string",
+ "x-go-name": "Username"
+ }
+ },
+ "x-go-name": "oauth2Authorization",
+ "x-go-package": "github.com/ory/keto/warden"
+ },
+ "wardenSubjectAuthorizationResponse": {
+ "type": "object",
+ "properties": {
+ "allowed": {
+ "description": "Allowed is true if the request is allowed and false otherwise.",
+ "type": "boolean",
+ "x-go-name": "Allowed"
+ },
+ "subject": {
+ "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.",
+ "type": "string",
+ "x-go-name": "Subject"
+ }
+ },
+ "x-go-name": "subjectAuthorization",
+ "x-go-package": "github.com/ory/keto/warden"
+ }
+ },
+ "responses": {
+ "emptyResponse": {
+ "description": "An empty response"
+ },
+ "genericError": {
+ "description": "The standard error format",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "Code"
+ },
+ "details": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "object"
+ }
+ },
+ "x-go-name": "Details"
+ },
+ "message": {
+ "type": "string",
+ "x-go-name": "Message"
+ },
+ "reason": {
+ "type": "string",
+ "x-go-name": "Reason"
+ },
+ "request": {
+ "type": "string",
+ "x-go-name": "Request"
+ },
+ "status": {
+ "type": "string",
+ "x-go-name": "Status"
+ }
+ }
+ }
+ },
+ "listRolesResponse": {
+ "description": "A list of roles the member is belonging to",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/role"
+ }
+ }
+ },
+ "policyList": {
+ "description": "A policy",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/policy"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/legacy/hydra.go b/legacy/hydra.go
new file mode 100644
index 000000000..ed558ad4b
--- /dev/null
+++ b/legacy/hydra.go
@@ -0,0 +1,68 @@
+/*
+ * Copyright © 2015-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 legacy
+
+import "github.com/rubenv/sql-migrate"
+
+var HydraLegacyMigrations = map[string]*migrate.MemoryMigrationSource{
+ "postgres": {
+ Migrations: []*migrate.Migration{
+ {
+ Id: "1",
+ Up: []string{
+ `ALTER TABLE hydra_warden_group RENAME TO keto_role`,
+ `ALTER TABLE hydra_warden_group_member RENAME COLUMN group_id TO role_id`,
+ `ALTER TABLE hydra_warden_group_member RENAME TO keto_role_member`,
+ `ALTER TABLE hydra_policy_migration RENAME TO keto_policy_migration`,
+ `ALTER TABLE hydra_groups_migration RENAME TO keto_role_migration`,
+ },
+ Down: []string{
+ `ALTER TABLE keto_role RENAME TO hydra_warden_group`,
+ `ALTER TABLE hydra_warden_group_member RENAME COLUMN group_id TO role_id`,
+ `ALTER TABLE keto_role_member RENAME TO hydra_warden_group_member`,
+ `ALTER TABLE keto_policy_migration RENAME TO hydra_policy_migration`,
+ `ALTER TABLE keto_role_migration RENAME TO hydra_groups_migration`,
+ },
+ },
+ },
+ },
+ "mysql": {
+ Migrations: []*migrate.Migration{
+ {
+ Id: "1",
+ Up: []string{
+ `RENAME TABLE hydra_warden_group RENAME TO keto_role`,
+ `ALTER TABLE hydra_warden_group_member CHANGE group_id role_id varchar(255)`,
+ `RENAME TABLE hydra_warden_group_member RENAME TO keto_role_member`,
+ `RENAME TABLE hydra_policy_migration RENAME TO keto_policy_migration`,
+ `RENAME TABLE hydra_groups_migration RENAME TO keto_role_migration`,
+ },
+ Down: []string{
+ `RENAME TABLE keto_role TO hydra_warden_group`,
+ `RENAME TABLE keto_role_member TO hydra_warden_group_member`,
+ `RENAME TABLE keto_policy_migration TO hydra_policy_migration`,
+ `RENAME TABLE keto_role_migration TO hydra_groups_migration`,
+ },
+ },
+ },
+ },
+}
diff --git a/main.go b/main.go
new file mode 100644
index 000000000..5d653c1ad
--- /dev/null
+++ b/main.go
@@ -0,0 +1,33 @@
+//go:generate swagger generate spec
+// Copyright © 2017 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.
+
+package main
+
+import (
+ "os"
+
+ "github.com/ory/keto/cmd"
+ "github.com/pkg/profile"
+)
+
+func main() {
+ if os.Getenv("PROFILING") == "cpu" {
+ defer profile.Start(profile.CPUProfile).Stop()
+ } else if os.Getenv("PROFILING") == "memory" {
+ defer profile.Start(profile.MemProfile).Stop()
+ }
+
+ cmd.Execute()
+}
diff --git a/package.json b/package.json
new file mode 100644
index 000000000..be11ad3f9
--- /dev/null
+++ b/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "ory-keto-sdk",
+ "version": "0.0.0",
+ "description": "The official JavaScript / NodeJS SDK for ORY Hydra.",
+ "license": "Apache 2.0",
+ "main": "sdk/js/swagger/src/index.js",
+ "scripts": {
+ "prettier": "prettier --single-quote --parser flow --no-semi --write \"sdk/js/**/*.js\""
+ },
+ "browser": {
+ "fs": false
+ },
+ "dependencies": {
+ "superagent": "3.5.2"
+ },
+ "devDependencies": {
+ "prettier": "^1.7.4"
+ }
+}
diff --git a/policy/doc.go b/policy/doc.go
new file mode 100644
index 000000000..c1e00e355
--- /dev/null
+++ b/policy/doc.go
@@ -0,0 +1,116 @@
+/*
+ * Copyright © 2015-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 policy offers management capabilities for access control policies.
+//
+// Access Control Policies (ACP) are a concept similar to Role Based Access Control and Access Control Lists. ACPs
+// however are more flexible and capable of handling complex and abstract access control scenarios. A ACP answers "**Who**
+// is **able** to do **what** on **something** given a **context**."
+//
+//
+// ACPs have five attributes:
+//
+// - Subject *(who)*: An arbitrary unique subject name, for example "ken" or "printer-service.mydomain.com".
+// - Effect *(able)*: The effect which can be either "allow" or "deny".
+// - Action *(what)*: An arbitrary action name, for example "delete", "create" or "scoped:action:something".
+// - Resource *(something)*: An arbitrary unique resource name, for example "something", "resources.articles.1234" or some uniform resource name like "urn:isbn:3827370191".
+// - Condition *(context)*: An optional condition that evaluates the context (e.g. IP Address, request datetime, resource owner name, department, ...). Different strategies are available to evaluate conditions:
+// - https://github.com/ory/ladon#cidr-condition
+// - https://github.com/ory/ladon#string-equal-condition
+// - https://github.com/ory/ladon#string-match-condition
+// - https://github.com/ory/ladon#subject-condition
+// - https://github.com/ory/ladon#string-pairs-equal-condition
+//
+//
+// You can find more information on ACPs here:
+//
+// - https://github.com/ory/ladon#usage for more information on policy usage.
+//
+// - https://github.com/ory/ladon#concepts
+
+package policy
+
+// swagger:parameters listPolicies
+type swaggerListPolicyParameters struct {
+ // The offset from where to start looking.
+ // in: query
+ Offset int `json:"offset"`
+
+ // The maximum amount of policies returned.
+ // in: query
+ Limit int `json:"limit"`
+}
+
+// swagger:parameters getPolicy deletePolicy
+type swaggerGetPolicyParameters struct {
+ // The id of the policy.
+ // in: path
+ ID string `json:"id"`
+}
+
+// swagger:parameters updatePolicy
+type swaggerUpdatePolicyParameters struct {
+ // The id of the policy.
+ // in: path
+ ID string `json:"id"`
+
+ // in: body
+ Body swaggerPolicy
+}
+
+// swagger:parameters createPolicy
+type swaggerCreatePolicyParameters struct {
+ // in: body
+ Body swaggerPolicy
+}
+
+// A policy
+// swagger:response policyList
+type swaggerListPolicyResponse struct {
+ // in: body
+ // type: array
+ Body []swaggerPolicy
+}
+
+// swagger:model policy
+type swaggerPolicy struct {
+ // ID of the policy.
+ ID string `json:"id"`
+
+ // Description of the policy.
+ Description string `json:"description"`
+
+ // Subjects impacted by the policy.
+ Subjects []string `json:"subjects"`
+ // Effect of the policy
+ Effect string `json:"effect"`
+
+ // Resources impacted by the policy.
+ Resources []string `json:"resources"`
+
+ // Actions impacted by the policy.
+ Actions []string `json:"actions"`
+
+ // Conditions under which the policy is active.
+ Conditions map[string]struct {
+ Type string `json:"type"`
+ Options map[string]interface{} `json:"options"`
+ } `json:"conditions"`
+}
diff --git a/policy/handler.go b/policy/handler.go
new file mode 100644
index 000000000..9c7285e59
--- /dev/null
+++ b/policy/handler.go
@@ -0,0 +1,226 @@
+/*
+ * Copyright © 2015-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 policy
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/julienschmidt/httprouter"
+ "github.com/ory/herodot"
+ "github.com/ory/hydra/pkg"
+ "github.com/ory/ladon"
+ "github.com/ory/pagination"
+ "github.com/pborman/uuid"
+ "github.com/pkg/errors"
+)
+
+const (
+ handlerBasePath = "/policies"
+)
+
+type Handler struct {
+ Manager ladon.Manager
+ H herodot.Writer
+}
+
+func NewHandler(manager ladon.Manager, writer herodot.Writer) *Handler {
+ return &Handler{
+ H: writer,
+ Manager: manager,
+ }
+}
+
+func (h *Handler) SetRoutes(r *httprouter.Router) {
+ r.POST(handlerBasePath, h.Create)
+ r.GET(handlerBasePath, h.List)
+ r.GET(handlerBasePath+"/:id", h.Get)
+ r.PUT(handlerBasePath+"/:id", h.Update)
+ r.DELETE(handlerBasePath+"/:id", h.Delete)
+}
+
+// swagger:route GET /policies policy listPolicies
+//
+// List Access Control Policies
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Responses:
+// 200: policyList
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) List(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ limit, offset := pagination.Parse(r, 500, 0, 1000)
+ policies, err := h.Manager.GetAll(int64(limit), int64(offset))
+ if err != nil {
+ h.H.WriteError(w, r, errors.WithStack(err))
+ return
+ }
+ h.H.Write(w, r, policies)
+}
+
+// swagger:route POST /policies policy createPolicy
+//
+// Create an Access Control Policy
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Responses:
+// 201: policy
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) Create(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ var p = ladon.DefaultPolicy{
+ Conditions: ladon.Conditions{},
+ }
+
+ if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
+ h.H.WriteError(w, r, errors.WithStack(err))
+ return
+ }
+
+ if p.ID == "" {
+ p.ID = uuid.New()
+ }
+
+ if err := h.Manager.Create(&p); err != nil {
+ h.H.WriteError(w, r, errors.WithStack(err))
+ return
+ }
+ h.H.WriteCreated(w, r, "/policies/"+p.ID, &p)
+}
+
+// swagger:route GET /policies/{id} policy getPolicy
+//
+// Get an Access Control Policy
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Responses:
+// 200: policy
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) Get(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+ policy, err := h.Manager.Get(ps.ByName("id"))
+ if err != nil {
+ if err.Error() == "Not found" {
+ h.H.WriteError(w, r, errors.WithStack(pkg.ErrNotFound))
+ return
+ }
+ h.H.WriteError(w, r, errors.WithStack(err))
+ return
+ }
+ h.H.Write(w, r, policy)
+}
+
+// swagger:route DELETE /policies/{id} policy deletePolicy
+//
+// Delete an Access Control Policy
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Security:
+// oauth2: hydra.policies
+//
+// Responses:
+// 204: emptyResponse
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) Delete(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+ id := ps.ByName("id")
+
+ if err := h.Manager.Delete(id); err != nil {
+ h.H.WriteError(w, r, errors.WithStack(err))
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
+
+// swagger:route PUT /policies/{id} policy updatePolicy
+//
+// Update an Access Control Policy
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Security:
+// oauth2: hydra.policies
+//
+// Responses:
+// 200: policy
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) Update(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+ var id = ps.ByName("id")
+ var p = ladon.DefaultPolicy{Conditions: ladon.Conditions{}}
+
+ if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
+ h.H.WriteError(w, r, errors.WithStack(err))
+ return
+ }
+
+ if p.ID != id {
+ h.H.WriteErrorCode(w, r, http.StatusBadRequest, errors.New("Payload ID does not match ID from URL"))
+ return
+ }
+
+ if err := h.Manager.Update(&p); err != nil {
+ h.H.WriteError(w, r, errors.WithStack(err))
+ return
+ }
+
+ h.H.Write(w, r, p)
+}
diff --git a/policy/sdk_test.go b/policy/sdk_test.go
new file mode 100644
index 000000000..44ce77bb4
--- /dev/null
+++ b/policy/sdk_test.go
@@ -0,0 +1,129 @@
+/*
+ * Copyright © 2015-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 policy_test
+
+import (
+ "encoding/json"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/julienschmidt/httprouter"
+ "github.com/ory/herodot"
+ . "github.com/ory/keto/policy"
+ keto "github.com/ory/keto/sdk/go/keto/swagger"
+ "github.com/ory/ladon"
+ "github.com/ory/ladon/manager/memory"
+ "github.com/pborman/uuid"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func mockPolicy(t *testing.T) keto.Policy {
+ originalPolicy := &ladon.DefaultPolicy{
+ ID: uuid.New(),
+ Description: "description",
+ Subjects: []string{""},
+ Effect: ladon.AllowAccess,
+ Resources: []string{""},
+ Actions: []string{"view"},
+ Conditions: ladon.Conditions{
+ "ip": &ladon.CIDRCondition{
+ CIDR: "1234",
+ },
+ "owner": &ladon.EqualsSubjectCondition{},
+ },
+ }
+ out, err := json.Marshal(originalPolicy)
+ require.NoError(t, err)
+
+ var apiPolicy keto.Policy
+ require.NoError(t, json.Unmarshal(out, &apiPolicy))
+ out, err = json.Marshal(&apiPolicy)
+ require.NoError(t, err)
+
+ var checkPolicy ladon.DefaultPolicy
+ require.NoError(t, json.Unmarshal(out, &checkPolicy))
+ require.EqualValues(t, checkPolicy.Conditions["ip"], originalPolicy.Conditions["ip"])
+ require.EqualValues(t, checkPolicy.Conditions["owner"], originalPolicy.Conditions["owner"])
+
+ return apiPolicy
+}
+
+func TestPolicySDK(t *testing.T) {
+ handler := &Handler{
+ Manager: &memory.MemoryManager{Policies: map[string]ladon.Policy{}},
+ H: herodot.NewJSONWriter(nil),
+ }
+
+ router := httprouter.New()
+ handler.SetRoutes(router)
+ server := httptest.NewServer(router)
+
+ client := keto.NewPolicyApiWithBasePath(server.URL)
+
+ p := mockPolicy(t)
+
+ t.Run("TestPolicyManagement", func(t *testing.T) {
+ _, response, err := client.GetPolicy(p.Id)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusNotFound, response.StatusCode)
+
+ result, response, err := client.CreatePolicy(p)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusCreated, response.StatusCode)
+ assert.EqualValues(t, p, *result)
+
+ result, response, err = client.GetPolicy(p.Id)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.EqualValues(t, p, *result)
+
+ p.Subjects = []string{"stan"}
+ result, response, err = client.UpdatePolicy(p.Id, p)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.EqualValues(t, p, *result)
+
+ results, response, err := client.ListPolicies(0, 10)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 1)
+
+ results, response, err = client.ListPolicies(10, 1)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 0)
+
+ result, response, err = client.GetPolicy(p.Id)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.EqualValues(t, p, *result)
+
+ response, err = client.DeletePolicy(p.Id)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusNoContent, response.StatusCode)
+
+ _, response, err = client.GetPolicy(p.Id)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusNotFound, response.StatusCode)
+ })
+}
diff --git a/policy/sdk_test.go.bak b/policy/sdk_test.go.bak
new file mode 100644
index 000000000..a59f76532
--- /dev/null
+++ b/policy/sdk_test.go.bak
@@ -0,0 +1,132 @@
+/*
+ * Copyright © 2015-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 policy
+
+import (
+ "net/http/httptest"
+ "testing"
+
+ "encoding/json"
+ "net/http"
+
+ "github.com/julienschmidt/httprouter"
+ "github.com/ory/fosite"
+ "github.com/ory/herodot"
+ "github.com/ory/hydra/compose"
+ hydra "github.com/ory/hydra/sdk/go/hydra/swagger"
+ "github.com/ory/ladon"
+ "github.com/ory/ladon/manager/memory"
+ "github.com/pborman/uuid"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func mockPolicy(t *testing.T) hydra.Policy {
+ originalPolicy := &ladon.DefaultPolicy{
+ ID: uuid.New(),
+ Description: "description",
+ Subjects: []string{""},
+ Effect: ladon.AllowAccess,
+ Resources: []string{""},
+ Actions: []string{"view"},
+ Conditions: ladon.Conditions{
+ "ip": &ladon.CIDRCondition{
+ CIDR: "1234",
+ },
+ "owner": &ladon.EqualsSubjectCondition{},
+ },
+ }
+ out, err := json.Marshal(originalPolicy)
+ require.NoError(t, err)
+
+ var apiPolicy hydra.Policy
+ require.NoError(t, json.Unmarshal(out, &apiPolicy))
+ out, err = json.Marshal(&apiPolicy)
+ require.NoError(t, err)
+
+ var checkPolicy ladon.DefaultPolicy
+ require.NoError(t, json.Unmarshal(out, &checkPolicy))
+ require.EqualValues(t, checkPolicy.Conditions["ip"], originalPolicy.Conditions["ip"])
+ require.EqualValues(t, checkPolicy.Conditions["owner"], originalPolicy.Conditions["owner"])
+
+ return apiPolicy
+}
+
+func TestPolicySDK(t *testing.T) {
+ handler := &Handler{
+ Manager: &memory.MemoryManager{Policies: map[string]ladon.Policy{}},
+ H: herodot.NewJSONWriter(nil),
+ }
+
+ router := httprouter.New()
+ handler.SetRoutes(router)
+ server := httptest.NewServer(router)
+
+ client := hydra.NewPolicyApiWithBasePath(server.URL)
+ client.Configuration.Transport = httpClient.Transport
+
+ p := mockPolicy(t)
+
+ t.Run("TestPolicyManagement", func(t *testing.T) {
+ _, response, err := client.GetPolicy(p.Id)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusNotFound, response.StatusCode)
+
+ result, response, err := client.CreatePolicy(p)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusCreated, response.StatusCode)
+ assert.EqualValues(t, p, *result)
+
+ result, response, err = client.GetPolicy(p.Id)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.EqualValues(t, p, *result)
+
+ p.Subjects = []string{"stan"}
+ result, response, err = client.UpdatePolicy(p.Id, p)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.EqualValues(t, p, *result)
+
+ results, response, err := client.ListPolicies(0, 10)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 1)
+
+ results, response, err = client.ListPolicies(10, 1)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 0)
+
+ result, response, err = client.GetPolicy(p.Id)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.EqualValues(t, p, *result)
+
+ response, err = client.DeletePolicy(p.Id)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusNoContent, response.StatusCode)
+
+ _, response, err = client.GetPolicy(p.Id)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusNotFound, response.StatusCode)
+ })
+}
diff --git a/role/doc.go b/role/doc.go
new file mode 100644
index 000000000..8fe353896
--- /dev/null
+++ b/role/doc.go
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2015-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 role capabilities for grouping subjects together, making policy management easier.
+//
+// This endpoint is **experimental**, use it at your own risk.
+
+package role
+
+// A list of roles the member is belonging to
+// swagger:response listRolesResponse
+type swaggerlistRolesResponse struct {
+ // in: body
+ // type: array
+ Body []Role
+}
+
+// swagger:parameters listRoles
+type swaggerListGroupsParameters struct {
+ // The id of the member to look up.
+ // in: query
+ Member string `json:"member"`
+
+ // The maximum amount of policies returned.
+ // in: query
+ Limit int `json:"limit"`
+
+ // The offset from where to start looking.
+ // in: query
+ Offset int `json:"offset"`
+}
+
+// swagger:parameters createRole
+type swaggerCreateGroupParameters struct {
+ // in: body
+ Body Role
+}
+
+// swagger:parameters getRole deleteRole
+type swaggerGetGroupParameters struct {
+ // The id of the role to look up.
+ // in: path
+ ID string `json:"id"`
+}
+
+// swagger:parameters removeMembersFromRole addMembersToRole
+type swaggerModifyMembersParameters struct {
+ // The id of the role to modify.
+ // in: path
+ ID string `json:"id"`
+
+ // in: body
+ Body membersRequest
+}
diff --git a/role/handler.go b/role/handler.go
new file mode 100644
index 000000000..a83afaf2d
--- /dev/null
+++ b/role/handler.go
@@ -0,0 +1,297 @@
+/*
+ * Copyright © 2015-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 role
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/julienschmidt/httprouter"
+ "github.com/ory/herodot"
+ "github.com/ory/pagination"
+ "github.com/pkg/errors"
+)
+
+// swagger:model roleMembers
+type membersRequest struct {
+ Members []string `json:"members"`
+}
+
+func NewHandler(manager Manager, writer herodot.Writer) *Handler {
+ return &Handler{
+ H: writer,
+ Manager: manager,
+ }
+}
+
+type Handler struct {
+ Manager Manager
+ H herodot.Writer
+}
+
+const (
+ handlerBasePath = "/roles"
+)
+
+func (h *Handler) SetRoutes(r *httprouter.Router) {
+ r.POST(handlerBasePath, h.CreateRole)
+ r.GET(handlerBasePath, h.ListRoles)
+ r.GET(handlerBasePath+"/:id", h.GetRole)
+ r.DELETE(handlerBasePath+"/:id", h.DeleteRole)
+ r.POST(handlerBasePath+"/:id/members", h.AddRoleMembers)
+ r.DELETE(handlerBasePath+"/:id/members", h.DeleteRoleMembers)
+}
+
+// swagger:route GET /roles role listRoles
+//
+// List all roles
+//
+// A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular
+// user or some other sort of role.
+//
+// This endpoint allows you to retrieve all roles that are stored in the system.
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Responses:
+// 200: listRolesResponse
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) ListRoles(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ limit, offset := pagination.Parse(r, 100, 0, 500)
+ if member := r.URL.Query().Get("member"); member != "" {
+ h.FindGroupNames(w, r, member, limit, offset)
+ return
+ } else {
+ h.listAllRoles(w, r, limit, offset)
+ return
+ }
+}
+
+func (h *Handler) listAllRoles(w http.ResponseWriter, r *http.Request, limit, offset int) {
+ groups, err := h.Manager.ListRoles(limit, offset)
+ if err != nil {
+ h.H.WriteError(w, r, err)
+ return
+ }
+
+ h.H.Write(w, r, groups)
+}
+
+func (h *Handler) FindGroupNames(w http.ResponseWriter, r *http.Request, member string, limit, offset int) {
+ groups, err := h.Manager.FindRolesByMember(member, limit, offset)
+ if err != nil {
+ h.H.WriteError(w, r, err)
+ return
+ }
+
+ h.H.Write(w, r, groups)
+}
+
+// swagger:route POST /roles role createRole
+//
+// Create a role
+//
+// A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular
+// user or some other sort of role.
+//
+// This endpoint allows you to create a new role. You may define members as well but you don't have to.
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Responses:
+// 201: role
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) CreateRole(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ var g Role
+
+ if err := json.NewDecoder(r.Body).Decode(&g); err != nil {
+ h.H.WriteError(w, r, errors.WithStack(err))
+ return
+ }
+
+ if err := h.Manager.CreateRole(&g); err != nil {
+ h.H.WriteError(w, r, err)
+ return
+ }
+
+ h.H.WriteCreated(w, r, handlerBasePath+"/"+g.ID, &g)
+}
+
+// swagger:route GET /roles/{id} role getRole
+//
+// Get a role by its ID
+//
+// A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular
+// user or some other sort of role.
+//
+// This endpoint allows you to retrieve an existing role. You have to know the role's ID.
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Responses:
+// 201: role
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) GetRole(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+ var id = ps.ByName("id")
+
+ g, err := h.Manager.GetRole(id)
+ if err != nil {
+ h.H.WriteError(w, r, err)
+ return
+ }
+
+ h.H.Write(w, r, g)
+}
+
+// swagger:route DELETE /roles/{id} role deleteRole
+//
+// Get a role by its ID
+//
+// A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular
+// user or some other sort of role.
+//
+// This endpoint allows you to delete an existing role. You have to know the role's ID.
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Responses:
+// 204: emptyResponse
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) DeleteRole(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+ var id = ps.ByName("id")
+
+ if err := h.Manager.DeleteRole(id); err != nil {
+ h.H.WriteError(w, r, err)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
+
+// swagger:route POST /roles/{id}/members role addMembersToRole
+//
+// Add members to a role
+//
+// A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular
+// user or some other sort of role.
+//
+// This endpoint allows you to add members (users, applications, ...) to a specific role. You have to know the role's ID.
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Responses:
+// 204: emptyResponse
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) AddRoleMembers(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+ var id = ps.ByName("id")
+
+ var m membersRequest
+ if err := json.NewDecoder(r.Body).Decode(&m); err != nil {
+ h.H.WriteError(w, r, errors.WithStack(err))
+ return
+ }
+
+ if err := h.Manager.AddRoleMembers(id, m.Members); err != nil {
+ h.H.WriteError(w, r, err)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
+
+// swagger:route DELETE /roles/{id}/members role removeMembersFromRole
+//
+// Remove members from a role
+//
+// A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular
+// user or some other sort of role.
+//
+// This endpoint allows you to remove members (users, applications, ...) from a specific role. You have to know the role's ID.
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Responses:
+// 204: emptyResponse
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) DeleteRoleMembers(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+ var id = ps.ByName("id")
+
+ var m membersRequest
+ if err := json.NewDecoder(r.Body).Decode(&m); err != nil {
+ h.H.WriteError(w, r, errors.WithStack(err))
+ return
+ }
+
+ if err := h.Manager.RemoveRoleMembers(id, m.Members); err != nil {
+ h.H.WriteError(w, r, err)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
diff --git a/role/manager.go b/role/manager.go
new file mode 100644
index 000000000..47caf7e43
--- /dev/null
+++ b/role/manager.go
@@ -0,0 +1,45 @@
+/*
+ * Copyright © 2015-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 role
+
+// Role represents a group of users that share the same role. A role could be an administrator, a moderator, a regular
+// user or some other sort of role.
+//
+// swagger:model role
+type Role struct {
+ // ID is the role's unique id.
+ ID string `json:"id"`
+
+ // Members is who belongs to the role.
+ Members []string `json:"members"`
+}
+
+type Manager interface {
+ CreateRole(*Role) error
+ GetRole(id string) (*Role, error)
+ DeleteRole(id string) error
+
+ AddRoleMembers(group string, members []string) error
+ RemoveRoleMembers(group string, members []string) error
+
+ FindRolesByMember(member string, limit, offset int) ([]Role, error)
+ ListRoles(limit, offset int) ([]Role, error)
+}
diff --git a/role/manager_memory.go b/role/manager_memory.go
new file mode 100644
index 000000000..4e7b222c1
--- /dev/null
+++ b/role/manager_memory.go
@@ -0,0 +1,134 @@
+/*
+ * Copyright © 2015-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 role
+
+import (
+ "sync"
+
+ "github.com/ory/hydra/pkg"
+ "github.com/ory/pagination"
+ "github.com/pborman/uuid"
+ "github.com/pkg/errors"
+)
+
+func NewMemoryManager() *MemoryManager {
+ return &MemoryManager{
+ Roles: map[string]Role{},
+ }
+}
+
+type MemoryManager struct {
+ Roles map[string]Role
+ sync.RWMutex
+}
+
+func (m *MemoryManager) CreateRole(g *Role) error {
+ if g.ID == "" {
+ g.ID = uuid.New()
+ }
+ if m.Roles == nil {
+ m.Roles = map[string]Role{}
+ }
+
+ m.Roles[g.ID] = *g
+ return nil
+}
+
+func (m *MemoryManager) GetRole(id string) (*Role, error) {
+ if g, ok := m.Roles[id]; !ok {
+ return nil, errors.WithStack(pkg.ErrNotFound)
+ } else {
+ return &g, nil
+ }
+}
+
+func (m *MemoryManager) DeleteRole(id string) error {
+ delete(m.Roles, id)
+ return nil
+}
+
+func (m *MemoryManager) AddRoleMembers(group string, subjects []string) error {
+ g, err := m.GetRole(group)
+ if err != nil {
+ return err
+ }
+ g.Members = append(g.Members, subjects...)
+ return m.CreateRole(g)
+}
+
+func (m *MemoryManager) RemoveRoleMembers(group string, subjects []string) error {
+ g, err := m.GetRole(group)
+ if err != nil {
+ return err
+ }
+
+ var subs []string
+ for _, s := range g.Members {
+ var remove bool
+ for _, f := range subjects {
+ if f == s {
+ remove = true
+ break
+ }
+ }
+ if !remove {
+ subs = append(subs, s)
+ }
+ }
+
+ g.Members = subs
+ return m.CreateRole(g)
+}
+
+func (m *MemoryManager) FindRolesByMember(member string, limit, offset int) ([]Role, error) {
+ if m.Roles == nil {
+ m.Roles = map[string]Role{}
+ }
+
+ res := make([]Role, 0)
+ for _, g := range m.Roles {
+ for _, s := range g.Members {
+ if s == member {
+ res = append(res, g)
+ break
+ }
+ }
+ }
+
+ start, end := pagination.Index(limit, offset, len(res))
+ return res[start:end], nil
+}
+
+func (m *MemoryManager) ListRoles(limit, offset int) ([]Role, error) {
+ if m.Roles == nil {
+ m.Roles = map[string]Role{}
+ }
+
+ i := 0
+ res := make([]Role, len(m.Roles))
+ for _, g := range m.Roles {
+ res[i] = g
+ i++
+ }
+
+ start, end := pagination.Index(limit, offset, len(res))
+ return res[start:end], nil
+}
diff --git a/role/manager_sql.go b/role/manager_sql.go
new file mode 100644
index 000000000..4a3ab9bb3
--- /dev/null
+++ b/role/manager_sql.go
@@ -0,0 +1,206 @@
+/*
+ * Copyright © 2015-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 role
+
+import (
+ "database/sql"
+
+ "fmt"
+
+ "github.com/jmoiron/sqlx"
+ "github.com/ory/hydra/pkg"
+ "github.com/pborman/uuid"
+ "github.com/pkg/errors"
+ "github.com/rubenv/sql-migrate"
+)
+
+var migrations = &migrate.MemoryMigrationSource{
+ Migrations: []*migrate.Migration{
+ {
+ Id: "1",
+ Up: []string{`CREATE TABLE IF NOT EXISTS keto_role (
+ id varchar(255) NOT NULL PRIMARY KEY
+)`, `CREATE TABLE IF NOT EXISTS keto_role_member (
+ member varchar(255) NOT NULL,
+ role_id varchar(255) NOT NULL,
+ FOREIGN KEY (role_id) REFERENCES keto_role(id) ON DELETE CASCADE,
+ PRIMARY KEY (member, role_id)
+)`},
+ Down: []string{
+ "DROP TABLE hydra_warden_group",
+ "DROP TABLE hydra_warden_group_member",
+ },
+ },
+ },
+}
+
+type SQLManager struct {
+ DB *sqlx.DB
+
+ TableRole string
+ TableMember string
+ TableMigration string
+}
+
+func NewSQLManager(db *sqlx.DB) *SQLManager {
+ return &SQLManager{
+ DB: db,
+ TableRole: "keto_role",
+ TableMember: "keto_role_member",
+ TableMigration: "keto_role_migration",
+ }
+}
+
+func (m *SQLManager) CreateSchemas() (int, error) {
+ migrate.SetTable(m.TableMigration)
+ n, err := migrate.Exec(m.DB.DB, m.DB.DriverName(), migrations, migrate.Up)
+ if err != nil {
+ return 0, errors.Wrapf(err, "Could not migrate sql schema, applied %d migrations", n)
+ }
+ return n, nil
+}
+
+func (m *SQLManager) CreateRole(g *Role) error {
+ if g.ID == "" {
+ g.ID = uuid.New()
+ }
+ if _, err := m.DB.Exec(m.DB.Rebind(fmt.Sprintf("INSERT INTO %s (id) VALUES (?)", m.TableRole)), g.ID); err != nil {
+ return errors.WithStack(err)
+ }
+
+ return m.AddRoleMembers(g.ID, g.Members)
+}
+
+func (m *SQLManager) GetRole(id string) (*Role, error) {
+ var found string
+ if err := m.DB.Get(&found, m.DB.Rebind(fmt.Sprintf("SELECT id from %s WHERE id = ?", m.TableRole)), id); err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ var q []string
+ if err := m.DB.Select(&q, m.DB.Rebind(fmt.Sprintf("SELECT member from %s WHERE role_id = ?", m.TableMember)), found); err == sql.ErrNoRows {
+ return nil, errors.WithStack(pkg.ErrNotFound)
+ } else if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ return &Role{
+ ID: found,
+ Members: q,
+ }, nil
+}
+
+func (m *SQLManager) DeleteRole(id string) error {
+ if _, err := m.DB.Exec(m.DB.Rebind(fmt.Sprintf("DELETE FROM %s WHERE id=?", m.TableRole)), id); err != nil {
+ return errors.WithStack(err)
+ }
+ return nil
+}
+
+func (m *SQLManager) AddRoleMembers(group string, subjects []string) error {
+ tx, err := m.DB.Beginx()
+ if err != nil {
+ return errors.Wrap(err, "Could not begin transaction")
+ }
+
+ query := fmt.Sprintf("INSERT INTO %s (role_id, member) VALUES (?, ?)", m.TableMember)
+ for _, subject := range subjects {
+ if _, err := tx.Exec(m.DB.Rebind(query), group, subject); err != nil {
+ if err := tx.Rollback(); err != nil {
+ return errors.WithStack(err)
+ }
+ return errors.WithStack(err)
+ }
+ }
+
+ if err := tx.Commit(); err != nil {
+ if err := tx.Rollback(); err != nil {
+ return errors.WithStack(err)
+ }
+ return errors.Wrap(err, "Could not commit transaction")
+ }
+ return nil
+}
+
+func (m *SQLManager) RemoveRoleMembers(group string, subjects []string) error {
+ tx, err := m.DB.Beginx()
+ if err != nil {
+ return errors.Wrap(err, "Could not begin transaction")
+ }
+ for _, subject := range subjects {
+ if _, err := m.DB.Exec(m.DB.Rebind(fmt.Sprintf("DELETE FROM %s WHERE member=? AND role_id=?", m.TableMember)), subject, group); err != nil {
+ if err := tx.Rollback(); err != nil {
+ return errors.WithStack(err)
+ }
+ return errors.WithStack(err)
+ }
+ }
+
+ if err := tx.Commit(); err != nil {
+ if err := tx.Rollback(); err != nil {
+ return errors.WithStack(err)
+ }
+ return errors.Wrap(err, "Could not commit transaction")
+ }
+ return nil
+}
+
+func (m *SQLManager) FindRolesByMember(member string, limit, offset int) ([]Role, error) {
+ var ids []string
+ if err := m.DB.Select(&ids, m.DB.Rebind(fmt.Sprintf("SELECT role_id from %s WHERE member = ? GROUP BY role_id ORDER BY role_id LIMIT ? OFFSET ?", m.TableMember)), member, limit, offset); err == sql.ErrNoRows {
+ return nil, errors.WithStack(pkg.ErrNotFound)
+ } else if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ var groups = make([]Role, len(ids))
+ for k, id := range ids {
+ group, err := m.GetRole(id)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ groups[k] = *group
+ }
+
+ return groups, nil
+}
+
+func (m *SQLManager) ListRoles(limit, offset int) ([]Role, error) {
+ var ids []string
+ if err := m.DB.Select(&ids, m.DB.Rebind(fmt.Sprintf("SELECT role_id from %s GROUP BY role_id ORDER BY role_id LIMIT ? OFFSET ?", m.TableMember)), limit, offset); err == sql.ErrNoRows {
+ return nil, errors.WithStack(pkg.ErrNotFound)
+ } else if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ var groups = make([]Role, len(ids))
+ for k, id := range ids {
+ group, err := m.GetRole(id)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ groups[k] = *group
+ }
+
+ return groups, nil
+}
diff --git a/role/manager_test.go b/role/manager_test.go
new file mode 100644
index 000000000..dc47e9c30
--- /dev/null
+++ b/role/manager_test.go
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2015-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 role_test
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "testing"
+
+ _ "github.com/go-sql-driver/mysql"
+ _ "github.com/lib/pq"
+ . "github.com/ory/keto/role"
+ "github.com/ory/sqlcon/dockertest"
+)
+
+var clientManagers = map[string]Manager{
+ "memory": &MemoryManager{
+ Roles: map[string]Role{},
+ },
+}
+
+func TestMain(m *testing.M) {
+ runner := dockertest.Register()
+
+ flag.Parse()
+ if !testing.Short() {
+ connectToPostgres()
+ connectToMySQL()
+ }
+
+ runner.Exit(m.Run())
+}
+
+func connectToMySQL() {
+ db, err := dockertest.ConnectToTestMySQL()
+ if err != nil {
+ panic(err)
+ }
+
+ s := NewSQLManager(db)
+ if _, err := s.CreateSchemas(); err != nil {
+ log.Fatalf("Could not create mysql schema: %v", err)
+ }
+
+ clientManagers["mysql"] = s
+}
+
+func connectToPostgres() {
+ db, err := dockertest.ConnectToTestMySQL()
+ if err != nil {
+ panic(err)
+ }
+
+ s := NewSQLManager(db)
+ if _, err := s.CreateSchemas(); err != nil {
+ log.Fatalf("Could not create postgres schema: %v", err)
+ }
+
+ clientManagers["postgres"] = s
+}
+
+func TestManagers(t *testing.T) {
+ for k, m := range clientManagers {
+ t.Run(fmt.Sprintf("case=%s", k), TestHelperManagers(m))
+ }
+}
diff --git a/role/manager_test_helper.go b/role/manager_test_helper.go
new file mode 100644
index 000000000..d4be5bf94
--- /dev/null
+++ b/role/manager_test_helper.go
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2015-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 role
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestHelperManagers(m Manager) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Parallel()
+
+ _, err := m.GetRole("4321")
+ assert.NotNil(t, err)
+
+ c := &Role{
+ ID: "1",
+ Members: []string{"bar", "foo"},
+ }
+ assert.NoError(t, m.CreateRole(c))
+ assert.NoError(t, m.CreateRole(&Role{
+ ID: "2",
+ Members: []string{"foo"},
+ }))
+
+ d, err := m.GetRole("1")
+ require.NoError(t, err)
+ assert.EqualValues(t, c.Members, d.Members)
+ assert.EqualValues(t, c.ID, d.ID)
+
+ ds, err := m.FindRolesByMember("foo", 100, 0)
+ require.NoError(t, err)
+ assert.Len(t, ds, 2)
+
+ ds, err = m.FindRolesByMember("foo", 1, 0)
+ require.NoError(t, err)
+ assert.Len(t, ds, 1)
+
+ ds, err = m.ListRoles(100, 0)
+ require.NoError(t, err)
+ assert.Len(t, ds, 2)
+
+ ds, err = m.ListRoles(1, 0)
+ require.NoError(t, err)
+ assert.Len(t, ds, 1)
+
+ assert.NoError(t, m.AddRoleMembers("1", []string{"baz"}))
+
+ ds, err = m.FindRolesByMember("baz", 100, 0)
+ require.NoError(t, err)
+ assert.Len(t, ds, 1)
+
+ assert.NoError(t, m.RemoveRoleMembers("1", []string{"baz"}))
+ ds, err = m.FindRolesByMember("baz", 100, 0)
+ require.NoError(t, err)
+ assert.Len(t, ds, 0)
+
+ assert.NoError(t, m.DeleteRole("1"))
+ _, err = m.GetRole("1")
+ require.NotNil(t, err)
+ }
+}
diff --git a/role/sdk_test.go b/role/sdk_test.go
new file mode 100644
index 000000000..b2e81f81a
--- /dev/null
+++ b/role/sdk_test.go
@@ -0,0 +1,121 @@
+/*
+ * Copyright © 2015-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 role_test
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/julienschmidt/httprouter"
+ _ "github.com/lib/pq"
+ "github.com/ory/herodot"
+ . "github.com/ory/keto/role"
+ keto "github.com/ory/keto/sdk/go/keto/swagger"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestGroupSDK(t *testing.T) {
+ clientManagers["memory"] = &MemoryManager{
+ Roles: map[string]Role{},
+ }
+
+ handler := &Handler{
+ Manager: &MemoryManager{
+ Roles: map[string]Role{},
+ },
+ H: herodot.NewJSONWriter(nil),
+ }
+
+ router := httprouter.New()
+ handler.SetRoutes(router)
+ server := httptest.NewServer(router)
+
+ client := keto.NewRoleApiWithBasePath(server.URL)
+
+ t.Run("flows", func(*testing.T) {
+ _, response, err := client.GetRole("4321")
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusNotFound, response.StatusCode)
+
+ firstGroup := keto.Role{Id: "1", Members: []string{"bar", "foo"}}
+ result, response, err := client.CreateRole(firstGroup)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusCreated, response.StatusCode)
+ assert.EqualValues(t, firstGroup, *result)
+
+ secondGroup := keto.Role{Id: "2", Members: []string{"foo"}}
+ result, response, err = client.CreateRole(secondGroup)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusCreated, response.StatusCode)
+ assert.EqualValues(t, secondGroup, *result)
+
+ result, response, err = client.GetRole("1")
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.EqualValues(t, firstGroup, *result)
+
+ results, response, err := client.ListRoles("foo", 100, 0)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 2)
+
+ results, response, err = client.ListRoles("", 100, 0)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 2)
+
+ results, response, err = client.ListRoles("", 1, 0)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 1)
+
+ results, response, err = client.ListRoles("foo", 1, 0)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 1)
+
+ client.AddMembersToRole("1", keto.GroupMembers{Members: []string{"baz"}})
+
+ results, response, err = client.ListRoles("baz", 100, 0)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 1)
+
+ response, err = client.RemoveMembersFromRole("1", keto.GroupMembers{Members: []string{"baz"}})
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusNoContent, response.StatusCode)
+
+ results, response, err = client.ListRoles("baz", 100, 0)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 0)
+
+ response, err = client.DeleteRole("1")
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusNoContent, response.StatusCode)
+
+ _, response, err = client.GetRole("4321")
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusNotFound, response.StatusCode)
+ })
+}
diff --git a/roles/doc.go b/roles/doc.go
new file mode 100644
index 000000000..3f10bd100
--- /dev/null
+++ b/roles/doc.go
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2015-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 group offers capabilities for grouping subjects together, making policy management easier.
+//
+// This endpoint is **experimental**, use it at your own risk.
+
+package roles
+
+// A list of groups the member is belonging to
+// swagger:response listGroupsResponse
+type swaggerListGroupsResponse struct {
+ // in: body
+ // type: array
+ Body []Role
+}
+
+// swagger:parameters listGroups
+type swaggerListGroupsParameters struct {
+ // The id of the member to look up.
+ // in: query
+ Member string `json:"member"`
+
+ // The maximum amount of policies returned.
+ // in: query
+ Limit int `json:"limit"`
+
+ // The offset from where to start looking.
+ // in: query
+ Offset int `json:"offset"`
+}
+
+// swagger:parameters createGroup
+type swaggerCreateGroupParameters struct {
+ // in: body
+ Body Role
+}
+
+// swagger:parameters getGroup deleteGroup
+type swaggerGetGroupParameters struct {
+ // The id of the group to look up.
+ // in: path
+ ID string `json:"id"`
+}
+
+// swagger:parameters removeMembersFromGroup addMembersToGroup
+type swaggerModifyMembersParameters struct {
+ // The id of the group to modify.
+ // in: path
+ ID string `json:"id"`
+
+ // in: body
+ Body membersRequest
+}
diff --git a/roles/handler.go b/roles/handler.go
new file mode 100644
index 000000000..b11cb41cf
--- /dev/null
+++ b/roles/handler.go
@@ -0,0 +1,263 @@
+/*
+ * Copyright © 2015-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 roles
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/julienschmidt/httprouter"
+ "github.com/ory/herodot"
+ "github.com/ory/pagination"
+ "github.com/pkg/errors"
+)
+
+// swagger:model groupMembers
+type membersRequest struct {
+ Members []string `json:"members"`
+}
+
+type Handler struct {
+ Manager Manager
+ H herodot.Writer
+}
+
+const (
+ RolesPath = "/roles"
+)
+
+func (h *Handler) SetRoutes(r *httprouter.Router) {
+ r.POST(RolesPath, h.CreateGroup)
+ r.GET(RolesPath, h.ListGroupsHandler)
+ r.GET(RolesPath+"/:id", h.GetGroup)
+ r.DELETE(RolesPath+"/:id", h.DeleteGroup)
+ r.POST(RolesPath+"/:id/members", h.AddGroupMembers)
+ r.DELETE(RolesPath+"/:id/members", h.RemoveGroupMembers)
+}
+
+// swagger:route GET /warden/groups warden listGroups
+//
+// List groups
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Responses:
+// 200: listGroupsResponse
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) ListGroupsHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ limit, offset := pagination.Parse(r, 100, 0, 500)
+ if member := r.URL.Query().Get("member"); member != "" {
+ h.FindGroupNames(w, r, member, limit, offset)
+ return
+ } else {
+ h.ListGroups(w, r, limit, offset)
+ return
+ }
+}
+
+func (h *Handler) ListGroups(w http.ResponseWriter, r *http.Request, limit, offset int) {
+ groups, err := h.Manager.ListRoles(limit, offset)
+ if err != nil {
+ h.H.WriteError(w, r, err)
+ return
+ }
+
+ h.H.Write(w, r, groups)
+}
+
+func (h *Handler) FindGroupNames(w http.ResponseWriter, r *http.Request, member string, limit, offset int) {
+ groups, err := h.Manager.FindRolesByMember(member, limit, offset)
+ if err != nil {
+ h.H.WriteError(w, r, err)
+ return
+ }
+
+ h.H.Write(w, r, groups)
+}
+
+// swagger:route POST /warden/groups warden createGroup
+//
+// Create a group
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Responses:
+// 201: group
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) CreateGroup(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ var g Role
+ var ctx = r.Context()
+
+ if err := json.NewDecoder(r.Body).Decode(&g); err != nil {
+ h.H.WriteError(w, r, errors.WithStack(err))
+ return
+ }
+
+ if err := h.Manager.CreateRole(&g); err != nil {
+ h.H.WriteError(w, r, err)
+ return
+ }
+
+ h.H.WriteCreated(w, r, RolesPath+"/"+g.ID, &g)
+}
+
+// swagger:route GET /warden/groups/{id} warden getGroup
+//
+// Get a group by id
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Responses:
+// 201: group
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) GetGroup(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+ var ctx = r.Context()
+ var id = ps.ByName("id")
+
+ g, err := h.Manager.GetRole(id)
+ if err != nil {
+ h.H.WriteError(w, r, err)
+ return
+ }
+
+ h.H.Write(w, r, g)
+}
+
+// swagger:route DELETE /warden/groups/{id} warden deleteGroup
+//
+// Delete a group by id
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Responses:
+// 204: emptyResponse
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) DeleteGroup(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+ var id = ps.ByName("id")
+
+ if err := h.Manager.DeleteRole(id); err != nil {
+ h.H.WriteError(w, r, err)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
+
+// swagger:route POST /warden/groups/{id}/members warden addMembersToGroup
+//
+// Add members to a group
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Responses:
+// 204: emptyResponse
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) AddGroupMembers(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+ var id = ps.ByName("id")
+
+ var m membersRequest
+ if err := json.NewDecoder(r.Body).Decode(&m); err != nil {
+ h.H.WriteError(w, r, errors.WithStack(err))
+ return
+ }
+
+ if err := h.Manager.AddRoleMembers(id, m.Members); err != nil {
+ h.H.WriteError(w, r, err)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
+
+// swagger:route DELETE /warden/groups/{id}/members warden removeMembersFromGroup
+//
+// Remove members from a group
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - application/json
+//
+// Schemes: http, https
+//
+// Responses:
+// 204: emptyResponse
+// 401: genericError
+// 403: genericError
+// 500: genericError
+func (h *Handler) RemoveGroupMembers(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+ var id = ps.ByName("id")
+
+ var m membersRequest
+ if err := json.NewDecoder(r.Body).Decode(&m); err != nil {
+ h.H.WriteError(w, r, errors.WithStack(err))
+ return
+ }
+
+
+ if err := h.Manager.RemoveRoleMembers(id, m.Members); err != nil {
+ h.H.WriteError(w, r, err)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
diff --git a/roles/manager.go b/roles/manager.go
new file mode 100644
index 000000000..82d64e428
--- /dev/null
+++ b/roles/manager.go
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2015-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 roles
+
+// Role represents a warden group
+//
+// swagger:model group
+type Role struct {
+ // ID is the groups id.
+ ID string `json:"id"`
+
+ // Members is who belongs to the group.
+ Members []string `json:"members"`
+}
+
+type Manager interface {
+ CreateRole(*Role) error
+ GetRole(id string) (*Role, error)
+ DeleteRole(id string) error
+
+ AddRoleMembers(group string, members []string) error
+ RemoveRoleMembers(group string, members []string) error
+
+ FindRolesByMember(member string, limit, offset int) ([]Role, error)
+ ListRoles(limit, offset int) ([]Role, error)
+}
diff --git a/roles/manager_memory.go b/roles/manager_memory.go
new file mode 100644
index 000000000..42ede5d9d
--- /dev/null
+++ b/roles/manager_memory.go
@@ -0,0 +1,134 @@
+/*
+ * Copyright © 2015-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 roles
+
+import (
+ "sync"
+
+ "github.com/ory/hydra/pkg"
+ "github.com/ory/pagination"
+ "github.com/pborman/uuid"
+ "github.com/pkg/errors"
+)
+
+func NewMemoryManager() *MemoryManager {
+ return &MemoryManager{
+ Roles: map[string]Role{},
+ }
+}
+
+type MemoryManager struct {
+ Roles map[string]Role
+ sync.RWMutex
+}
+
+func (m *MemoryManager) CreateRole(g *Role) error {
+ if g.ID == "" {
+ g.ID = uuid.New()
+ }
+ if m.Roles == nil {
+ m.Roles = map[string]Role{}
+ }
+
+ m.Roles[g.ID] = *g
+ return nil
+}
+
+func (m *MemoryManager) GetRole(id string) (*Role, error) {
+ if g, ok := m.Roles[id]; !ok {
+ return nil, errors.WithStack(pkg.ErrNotFound)
+ } else {
+ return &g, nil
+ }
+}
+
+func (m *MemoryManager) DeleteRole(id string) error {
+ delete(m.Roles, id)
+ return nil
+}
+
+func (m *MemoryManager) AddRoleMembers(group string, subjects []string) error {
+ g, err := m.GetRole(group)
+ if err != nil {
+ return err
+ }
+ g.Members = append(g.Members, subjects...)
+ return m.CreateRole(g)
+}
+
+func (m *MemoryManager) RemoveRoleMembers(group string, subjects []string) error {
+ g, err := m.GetRole(group)
+ if err != nil {
+ return err
+ }
+
+ var subs []string
+ for _, s := range g.Members {
+ var remove bool
+ for _, f := range subjects {
+ if f == s {
+ remove = true
+ break
+ }
+ }
+ if !remove {
+ subs = append(subs, s)
+ }
+ }
+
+ g.Members = subs
+ return m.CreateRole(g)
+}
+
+func (m *MemoryManager) FindRolesByMember(member string, limit, offset int) ([]Role, error) {
+ if m.Roles == nil {
+ m.Roles = map[string]Role{}
+ }
+
+ res := make([]Role, 0)
+ for _, g := range m.Roles {
+ for _, s := range g.Members {
+ if s == member {
+ res = append(res, g)
+ break
+ }
+ }
+ }
+
+ start, end := pagination.Index(limit, offset, len(res))
+ return res[start:end], nil
+}
+
+func (m *MemoryManager) ListRoles(limit, offset int) ([]Role, error) {
+ if m.Roles == nil {
+ m.Roles = map[string]Role{}
+ }
+
+ i := 0
+ res := make([]Role, len(m.Roles))
+ for _, g := range m.Roles {
+ res[i] = g
+ i++
+ }
+
+ start, end := pagination.Index(limit, offset, len(res))
+ return res[start:end], nil
+}
diff --git a/roles/manager_sql.go b/roles/manager_sql.go
new file mode 100644
index 000000000..7b8e1b05e
--- /dev/null
+++ b/roles/manager_sql.go
@@ -0,0 +1,209 @@
+/*
+ * Copyright © 2015-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 roles
+
+import (
+ "database/sql"
+
+ "github.com/jmoiron/sqlx"
+ "github.com/ory/hydra/pkg"
+ "github.com/pborman/uuid"
+ "github.com/pkg/errors"
+ "github.com/rubenv/sql-migrate"
+ "fmt"
+)
+
+var migrations = &migrate.MemoryMigrationSource{
+ Migrations: []*migrate.Migration{
+ {
+ Id: "1",
+ Up: []string{`CREATE TABLE IF NOT EXISTS hydra_warden_group (
+ id varchar(255) NOT NULL PRIMARY KEY
+)`, `CREATE TABLE IF NOT EXISTS hydra_warden_group_member (
+ member varchar(255) NOT NULL,
+ group_id varchar(255) NOT NULL,
+ FOREIGN KEY (group_id) REFERENCES hydra_warden_group(id) ON DELETE CASCADE,
+ PRIMARY KEY (member, group_id)
+)`},
+ Down: []string{
+ "DROP TABLE hydra_warden_group",
+ "DROP TABLE hydra_warden_group_member",
+ },
+ },
+ },
+}
+
+type SQLManager struct {
+ DB *sqlx.DB
+
+ TableRole string
+ TableMember string
+ TableMigration string
+}
+
+func NewLegacySQLManager(db *sqlx.DB) *SQLManager {
+ return &SQLManager{
+ DB: db,
+ TableRole: "hydra_warden_group",
+ TableMember: "hydra_warden_group_member",
+ TableMigration: "hydra_groups_migration",
+ }
+}
+
+func NewSQLManager(db *sqlx.DB) *SQLManager {
+
+}
+
+func (m *SQLManager) CreateSchemas() (int, error) {
+ migrate.SetTable(m.TableMigration)
+ n, err := migrate.Exec(m.DB.DB, m.DB.DriverName(), migrations, migrate.Up)
+ if err != nil {
+ return 0, errors.Wrapf(err, "Could not migrate sql schema, applied %d migrations", n)
+ }
+ return n, nil
+}
+
+func (m *SQLManager) CreateRole(g *Role) error {
+ if g.ID == "" {
+ g.ID = uuid.New()
+ }
+
+ if _, err := m.DB.Exec(m.DB.Rebind(fmt.Sprintf("INSERT INTO %s (id) VALUES (?)", m.TableRole)), g.ID); err != nil {
+ return errors.WithStack(err)
+ }
+
+ return m.AddRoleMembers(g.ID, g.Members)
+}
+
+func (m *SQLManager) GetRole(id string) (*Role, error) {
+ var found string
+ if err := m.DB.Get(&found, m.DB.Rebind(fmt.Sprintf("SELECT id from %s WHERE id = ?)", m.TableRole)), id); err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ var q []string
+ if err := m.DB.Select(&q, m.DB.Rebind(fmt.Sprintf("SELECT member from %s WHERE group_id = ?", m.TableMember)), found); err == sql.ErrNoRows {
+ return nil, errors.WithStack(pkg.ErrNotFound)
+ } else if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ return &Role{
+ ID: found,
+ Members: q,
+ }, nil
+}
+
+func (m *SQLManager) DeleteRole(id string) error {
+ if _, err := m.DB.Exec(m.DB.Rebind(fmt.Sprintf("DELETE FROM %s WHERE id=?", m.TableRole)), id); err != nil {
+ return errors.WithStack(err)
+ }
+ return nil
+}
+
+func (m *SQLManager) AddRoleMembers(group string, subjects []string) error {
+ tx, err := m.DB.Beginx()
+ if err != nil {
+ return errors.Wrap(err, "Could not begin transaction")
+ }
+
+ for _, subject := range subjects {
+ if _, err := tx.Exec(m.DB.Rebind(fmt.Sprintf("INSERT INTO %s (group_id, member) VALUES (?, ?)", m.TableMember)), group, subject); err != nil {
+ if err := tx.Rollback(); err != nil {
+ return errors.WithStack(err)
+ }
+ return errors.WithStack(err)
+ }
+ }
+
+ if err := tx.Commit(); err != nil {
+ if err := tx.Rollback(); err != nil {
+ return errors.WithStack(err)
+ }
+ return errors.Wrap(err, "Could not commit transaction")
+ }
+ return nil
+}
+
+func (m *SQLManager) RemoveRoleMembers(group string, subjects []string) error {
+ tx, err := m.DB.Beginx()
+ if err != nil {
+ return errors.Wrap(err, "Could not begin transaction")
+ }
+ for _, subject := range subjects {
+ if _, err := m.DB.Exec(m.DB.Rebind(fmt.Sprintf("DELETE FROM %s WHERE member=? AND group_id=?", m.TableMember)), subject, group); err != nil {
+ if err := tx.Rollback(); err != nil {
+ return errors.WithStack(err)
+ }
+ return errors.WithStack(err)
+ }
+ }
+
+ if err := tx.Commit(); err != nil {
+ if err := tx.Rollback(); err != nil {
+ return errors.WithStack(err)
+ }
+ return errors.Wrap(err, "Could not commit transaction")
+ }
+ return nil
+}
+
+func (m *SQLManager) FindRoleByMember(member string, limit, offset int) ([]Role, error) {
+ var ids []string
+ if err := m.DB.Select(&ids, m.DB.Rebind(fmt.Sprintf("SELECT group_id from %s WHERE member = ? GROUP BY group_id ORDER BY group_id LIMIT ? OFFSET ?", m.TableMember)), member, limit, offset); err == sql.ErrNoRows {
+ return nil, errors.WithStack(pkg.ErrNotFound)
+ } else if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ var groups = make([]Role, len(ids))
+ for k, id := range ids {
+ group, err := m.GetRole(id)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ groups[k] = *group
+ }
+
+ return groups, nil
+}
+
+func (m *SQLManager) ListRoles(limit, offset int) ([]Role, error) {
+ var ids []string
+ if err := m.DB.Select(&ids, m.DB.Rebind(fmt.Sprintf("SELECT group_id from %s GROUP BY group_id ORDER BY group_id LIMIT ? OFFSET ?", m.TableMember)), limit, offset); err == sql.ErrNoRows {
+ return nil, errors.WithStack(pkg.ErrNotFound)
+ } else if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ var groups = make([]Role, len(ids))
+ for k, id := range ids {
+ group, err := m.GetRole(id)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ groups[k] = *group
+ }
+
+ return groups, nil
+}
diff --git a/roles/manager_test.go b/roles/manager_test.go
new file mode 100644
index 000000000..3e008126d
--- /dev/null
+++ b/roles/manager_test.go
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2015-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 roles_test
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "testing"
+
+ _ "github.com/lib/pq"
+ . "github.com/ory/hades/roles"
+ "github.com/jmoiron/sqlx"
+)
+
+var clientManagers = map[string]Manager{
+ "memory": &MemoryManager{
+ Roles: map[string]Role{},
+ },
+}
+
+func TestMain(m *testing.M) {
+ flag.Parse()
+ if !testing.Short() {
+ connectToPG()
+ connectToMySQL()
+ }
+
+ os.Exit(m.Run())
+}
+
+func connectToMySQL() {
+ db, err := sqlx.Open("postgres", os.Getenv("TEST_POSTGRES_URL"))
+ if err != nil {
+ panic(err)
+ }
+ s := &SQLManager{DB: db}
+ if _, err := s.CreateSchemas(); err != nil {
+ log.Fatalf("Could not create postgres schema: %v", err)
+ }
+
+ clientManagers["postgres"] = s
+}
+
+func connectToPG() {
+ db, err := sqlx.Open("mysql", os.Getenv("TEST_MYSQL_URL"))
+ s := &SQLManager{DB: db}
+
+ if _, err := s.CreateSchemas(); err != nil {
+ log.Fatalf("Could not create postgres schema: %v", err)
+ }
+
+ clientManagers["mysql"] = s
+}
+
+func TestManagers(t *testing.T) {
+ for k, m := range clientManagers {
+ t.Run(fmt.Sprintf("case=%s", k), TestHelperManagers(m))
+ }
+}
diff --git a/roles/manager_test_helper.go b/roles/manager_test_helper.go
new file mode 100644
index 000000000..0c920b19c
--- /dev/null
+++ b/roles/manager_test_helper.go
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2015-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 roles
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestHelperManagers(m Manager) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Parallel()
+
+ _, err := m.GetRole("4321")
+ assert.NotNil(t, err)
+
+ c := &Role{
+ ID: "1",
+ Members: []string{"bar", "foo"},
+ }
+ assert.NoError(t, m.CreateRole(c))
+ assert.NoError(t, m.CreateRole(&Role{
+ ID: "2",
+ Members: []string{"foo"},
+ }))
+
+ d, err := m.GetRole("1")
+ require.NoError(t, err)
+ assert.EqualValues(t, c.Members, d.Members)
+ assert.EqualValues(t, c.ID, d.ID)
+
+ ds, err := m.FindRolesByMember("foo", 100, 0)
+ require.NoError(t, err)
+ assert.Len(t, ds, 2)
+
+ ds, err = m.FindRolesByMember("foo", 1, 0)
+ require.NoError(t, err)
+ assert.Len(t, ds, 1)
+
+ ds, err = m.ListRoles(100, 0)
+ require.NoError(t, err)
+ assert.Len(t, ds, 2)
+
+ ds, err = m.ListRoles(1, 0)
+ require.NoError(t, err)
+ assert.Len(t, ds, 1)
+
+ assert.NoError(t, m.AddRoleMembers("1", []string{"baz"}))
+
+ ds, err = m.FindRolesByMember("baz", 100, 0)
+ require.NoError(t, err)
+ assert.Len(t, ds, 1)
+
+ assert.NoError(t, m.RemoveRoleMembers("1", []string{"baz"}))
+ ds, err = m.FindRolesByMember("baz", 100, 0)
+ require.NoError(t, err)
+ assert.Len(t, ds, 0)
+
+ assert.NoError(t, m.DeleteRole("1"))
+ _, err = m.GetRole("1")
+ require.NotNil(t, err)
+ }
+}
diff --git a/roles/sdk_test.go.bak b/roles/sdk_test.go.bak
new file mode 100644
index 000000000..539a6118a
--- /dev/null
+++ b/roles/sdk_test.go.bak
@@ -0,0 +1,135 @@
+/*
+ * Copyright © 2015-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 group_test
+
+import (
+ "net/http/httptest"
+ "testing"
+
+ "net/http"
+
+ "github.com/julienschmidt/httprouter"
+ _ "github.com/lib/pq"
+ "github.com/ory/fosite"
+ "github.com/ory/herodot"
+ "github.com/ory/hydra/compose"
+ hydra "github.com/ory/hydra/sdk/go/hydra/swagger"
+ . "github.com/ory/hydra/warden/group"
+ "github.com/ory/ladon"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestGroupSDK(t *testing.T) {
+ clientManagers["memory"] = &MemoryManager{
+ Groups: map[string]Group{},
+ }
+
+ localWarden, httpClient := compose.NewMockFirewall("foo", "alice", fosite.Arguments{Scope}, &ladon.DefaultPolicy{
+ ID: "1",
+ Subjects: []string{"alice"},
+ Resources: []string{"rn:hydra:warden<.*>"},
+ Actions: []string{"list", "create", "get", "delete", "update", "members.add", "members.remove"},
+ Effect: ladon.AllowAccess,
+ })
+
+ handler := &Handler{
+ Manager: &MemoryManager{
+ Groups: map[string]Group{},
+ },
+ H: herodot.NewJSONWriter(nil),
+ W: localWarden,
+ }
+
+ router := httprouter.New()
+ handler.SetRoutes(router)
+ server := httptest.NewServer(router)
+
+ client := hydra.NewWardenApiWithBasePath(server.URL)
+ client.Configuration.Transport = httpClient.Transport
+
+ t.Run("flows", func(*testing.T) {
+ _, response, err := client.GetGroup("4321")
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusNotFound, response.StatusCode)
+
+ firstGroup := hydra.Group{Id: "1", Members: []string{"bar", "foo"}}
+ result, response, err := client.CreateGroup(firstGroup)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusCreated, response.StatusCode)
+ assert.EqualValues(t, firstGroup, *result)
+
+ secondGroup := hydra.Group{Id: "2", Members: []string{"foo"}}
+ result, response, err = client.CreateGroup(secondGroup)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusCreated, response.StatusCode)
+ assert.EqualValues(t, secondGroup, *result)
+
+ result, response, err = client.GetGroup("1")
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.EqualValues(t, firstGroup, *result)
+
+ results, response, err := client.ListGroups("foo", 100, 0)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 2)
+
+ results, response, err = client.ListGroups("", 100, 0)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 2)
+
+ results, response, err = client.ListGroups("", 1, 0)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 1)
+
+ results, response, err = client.ListGroups("foo", 1, 0)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 1)
+
+ client.AddMembersToGroup("1", hydra.GroupMembers{Members: []string{"baz"}})
+
+ results, response, err = client.ListGroups("baz", 100, 0)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 1)
+
+ response, err = client.RemoveMembersFromGroup("1", hydra.GroupMembers{Members: []string{"baz"}})
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusNoContent, response.StatusCode)
+
+ results, response, err = client.ListGroups("baz", 100, 0)
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ assert.Len(t, results, 0)
+
+ response, err = client.DeleteGroup("1")
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusNoContent, response.StatusCode)
+
+ _, response, err = client.GetGroup("4321")
+ require.NoError(t, err)
+ assert.Equal(t, http.StatusNotFound, response.StatusCode)
+ })
+}
diff --git a/scripts/.gitattributes b/scripts/.gitattributes
new file mode 100644
index 000000000..dfdb8b771
--- /dev/null
+++ b/scripts/.gitattributes
@@ -0,0 +1 @@
+*.sh text eol=lf
diff --git a/scripts/run-deploy.sh b/scripts/run-deploy.sh
new file mode 100644
index 000000000..2691f75a9
--- /dev/null
+++ b/scripts/run-deploy.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+set -euo pipefail
+
+gox -ldflags "-X github.com/ory/keto/cmd.Version=`git describe --tags` -X github.com/ory/keto/cmd.BuildTime=`TZ=UTC date -u '+%Y-%m-%dT%H:%M:%SZ'` -X github.com/ory/keto/cmd.GitHash=`git rev-parse HEAD`" -output "dist/{{.Dir}}-{{.OS}}-{{.Arch}}";
+npm version -f --no-git-tag-version $(git describe --tag);
diff --git a/scripts/run-format.sh b/scripts/run-format.sh
new file mode 100644
index 000000000..fe530bc00
--- /dev/null
+++ b/scripts/run-format.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -euo pipefail
+
+cd "$( dirname "${BASH_SOURCE[0]}" )/.."
+
+goimports -w $(go list -f {{.Dir}} ./... | grep -v vendor | grep -v "ory.keto$")
+goimports -w *.go
+
+git add -A
diff --git a/scripts/run-gensdk.sh b/scripts/run-gensdk.sh
new file mode 100644
index 000000000..ac0ffcc05
--- /dev/null
+++ b/scripts/run-gensdk.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+set -euo pipefail
+
+cd "$( dirname "${BASH_SOURCE[0]}" )/.."
+
+scripts/run-genswag.sh
+
+rm -rf ./sdk/go/keto/swagger
+rm -rf ./sdk/js/swagger
+rm -rf ./sdk/php/swagger
+
+# curl -O scripts/swagger-codegen-cli-2.2.3.jar http://central.maven.org/maven2/io/swagger/swagger-codegen-cli/2.2.3/swagger-codegen-cli-2.2.3.jar
+
+java -jar scripts/swagger-codegen-cli-2.2.3.jar generate -i ./docs/api.swagger.json -l go -o ./sdk/go/keto/swagger
+java -jar scripts/swagger-codegen-cli-2.2.3.jar generate -i ./docs/api.swagger.json -l javascript -o ./sdk/js/swagger
+java -jar scripts/swagger-codegen-cli-2.2.3.jar generate -i ./docs/api.swagger.json -l php -o sdk/php/ \
+ --invoker-package keto\\SDK --git-repo-id swagger --git-user-id ory --additional-properties "packagePath=swagger,description=Client for keto"
+
+scripts/run-format.sh
+
+git checkout HEAD -- sdk/go/keto/swagger/configuration.go
+git checkout HEAD -- sdk/go/keto/swagger/api_client.go
+rm -f ./sdk/js/swagger/package.json
+rm -rf ./sdk/js/swagger/test
+rm -f ./sdk/php/swagger/composer.json ./sdk/php/swagger/phpunit.xml.dist
+rm -rf ./sdk/php/swagger/test
+
+npm run prettier
diff --git a/scripts/run-genswag.sh b/scripts/run-genswag.sh
new file mode 100644
index 000000000..d47f7cfcb
--- /dev/null
+++ b/scripts/run-genswag.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -euo pipefail
+
+cd "$( dirname "${BASH_SOURCE[0]}" )/.."
+
+swagger generate spec -m -o ./docs/api.swagger.json
diff --git a/scripts/swagger-codegen-cli-2.2.3.jar b/scripts/swagger-codegen-cli-2.2.3.jar
new file mode 100644
index 000000000..ef3cedbd3
Binary files /dev/null and b/scripts/swagger-codegen-cli-2.2.3.jar differ
diff --git a/scripts/test-e2e.sh b/scripts/test-e2e.sh
new file mode 100644
index 000000000..5a230e154
--- /dev/null
+++ b/scripts/test-e2e.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+cd "$( dirname "${BASH_SOURCE[0]}" )/.."
+
+DATABASE_URL=memory hades host --dangerous-auto-logon --dangerous-force-http --disable-telemetry &
+while ! echo exit | nc 127.0.0.1 4444; do sleep 1; done
+
+hades clients create --id foobar
+hades clients delete foobar
+curl --header "Authorization: bearer $(hades token client)" http://localhost:4444/clients
+hades token validate $(hades token client)
diff --git a/scripts/test-format.sh b/scripts/test-format.sh
new file mode 100644
index 000000000..9f142b9b7
--- /dev/null
+++ b/scripts/test-format.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+set -euo pipefail
+
+cd "$( dirname "${BASH_SOURCE[0]}" )/.."
+
+toformat=$(goimports -l $(go list -f {{.Dir}} ./... | grep -v vendor | grep -v 'ory.keto$'))
+[ -z "$toformat" ] && echo "All files are formatted correctly"
+[ -n "$toformat" ] && echo "Please use \`goimports\` to format the following files:" && echo $toformat && exit 1
+
+exit 0
\ No newline at end of file
diff --git a/scripts/test-sdk.sh b/scripts/test-sdk.sh
new file mode 100644
index 000000000..4bf29e60f
--- /dev/null
+++ b/scripts/test-sdk.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+set -euo pipefail
+
+cd "$( dirname "${BASH_SOURCE[0]}" )/.."
+
+scripts/run-genswag.sh
+git add -A
+git diff --exit-code
+
+./scripts/run-gensdk.sh
+git add -A
+git diff --exit-code
diff --git a/sdk/go/keto/sdk.go b/sdk/go/keto/sdk.go
new file mode 100644
index 000000000..fc3a6ed50
--- /dev/null
+++ b/sdk/go/keto/sdk.go
@@ -0,0 +1,7 @@
+package keto
+
+type SDK interface {
+ RoleSDK
+ WardenSDK
+ PolicySDK
+}
diff --git a/sdk/go/keto/sdk_policy.go b/sdk/go/keto/sdk_policy.go
new file mode 100644
index 000000000..d7518fc4d
--- /dev/null
+++ b/sdk/go/keto/sdk_policy.go
@@ -0,0 +1,11 @@
+package keto
+
+import "github.com/ory/keto/sdk/go/keto/swagger"
+
+type PolicySDK interface {
+ CreatePolicy(body swagger.Policy) (*swagger.Policy, *swagger.APIResponse, error)
+ DeletePolicy(id string) (*swagger.APIResponse, error)
+ GetPolicy(id string) (*swagger.Policy, *swagger.APIResponse, error)
+ ListPolicies(offset int64, limit int64) ([]swagger.Policy, *swagger.APIResponse, error)
+ UpdatePolicy(id string, body swagger.Policy) (*swagger.Policy, *swagger.APIResponse, error)
+}
diff --git a/sdk/go/keto/sdk_role.go b/sdk/go/keto/sdk_role.go
new file mode 100644
index 000000000..97a709816
--- /dev/null
+++ b/sdk/go/keto/sdk_role.go
@@ -0,0 +1,12 @@
+package keto
+
+import "github.com/ory/keto/sdk/go/keto/swagger"
+
+type RoleSDK interface {
+ AddMembersToRole(id string, body swagger.RoleMembers) (*swagger.APIResponse, error)
+ DeleteRole(id string) (*swagger.APIResponse, error)
+ CreateRole(body swagger.Role) (*swagger.Role, *swagger.APIResponse, error)
+ GetRole(id string) (*swagger.Role, *swagger.APIResponse, error)
+ ListRoles(member string, limit int64, offset int64) ([]swagger.Role, *swagger.APIResponse, error)
+ RemoveMembersFromRole(id string, body swagger.RoleMembers) (*swagger.APIResponse, error)
+}
diff --git a/sdk/go/keto/sdk_warden.go b/sdk/go/keto/sdk_warden.go
new file mode 100644
index 000000000..49f402db9
--- /dev/null
+++ b/sdk/go/keto/sdk_warden.go
@@ -0,0 +1,8 @@
+package keto
+
+import "github.com/ory/keto/sdk/go/keto/swagger"
+
+type WardenSDK interface {
+ IsSubjectAuthorized(body swagger.WardenSubjectAuthorizationRequest) (*swagger.WardenSubjectAuthorizationResponse, *swagger.APIResponse, error)
+ IsOAuth2AccessTokenAuthorized(body swagger.WardenOAuth2AuthorizationRequest) (*swagger.WardenOAuth2AuthorizationResponse, *swagger.APIResponse, error)
+}
diff --git a/sdk/go/keto/swagger/.gitignore b/sdk/go/keto/swagger/.gitignore
new file mode 100644
index 000000000..daf913b1b
--- /dev/null
+++ b/sdk/go/keto/swagger/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/sdk/go/keto/swagger/.swagger-codegen-ignore b/sdk/go/keto/swagger/.swagger-codegen-ignore
new file mode 100644
index 000000000..c5fa491b4
--- /dev/null
+++ b/sdk/go/keto/swagger/.swagger-codegen-ignore
@@ -0,0 +1,23 @@
+# Swagger Codegen Ignore
+# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen
+
+# Use this file to prevent files from being overwritten by the generator.
+# The patterns follow closely to .gitignore or .dockerignore.
+
+# As an example, the C# client generator defines ApiClient.cs.
+# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line:
+#ApiClient.cs
+
+# You can match any string of characters against a directory, file or extension with a single asterisk (*):
+#foo/*/qux
+# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
+
+# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
+#foo/**/qux
+# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
+
+# You can also negate patterns with an exclamation (!).
+# For example, you can ignore all files in a docs folder with the file extension .md:
+#docs/*.md
+# Then explicitly reverse the ignore rule for a single file:
+#!docs/README.md
diff --git a/sdk/go/keto/swagger/.swagger-codegen/VERSION b/sdk/go/keto/swagger/.swagger-codegen/VERSION
new file mode 100644
index 000000000..6b4d15773
--- /dev/null
+++ b/sdk/go/keto/swagger/.swagger-codegen/VERSION
@@ -0,0 +1 @@
+2.2.3
\ No newline at end of file
diff --git a/sdk/go/keto/swagger/.travis.yml b/sdk/go/keto/swagger/.travis.yml
new file mode 100644
index 000000000..f5cb2ce9a
--- /dev/null
+++ b/sdk/go/keto/swagger/.travis.yml
@@ -0,0 +1,8 @@
+language: go
+
+install:
+ - go get -d -v .
+
+script:
+ - go build -v ./
+
diff --git a/sdk/go/keto/swagger/README.md b/sdk/go/keto/swagger/README.md
new file mode 100644
index 000000000..6bbf9076d
--- /dev/null
+++ b/sdk/go/keto/swagger/README.md
@@ -0,0 +1,80 @@
+# Go API client for swagger
+
+Package main ORY Keto
+
+## Overview
+This API client was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the [swagger-spec](https://github.com/swagger-api/swagger-spec) from a remote server, you can easily generate an API client.
+
+- API version: Latest
+- Package version: 1.0.0
+- Build package: io.swagger.codegen.languages.GoClientCodegen
+For more information, please visit [https://www.ory.sh](https://www.ory.sh)
+
+## Installation
+Put the package under your project folder and add the following in import:
+```
+ "./swagger"
+```
+
+## Documentation for API Endpoints
+
+All URIs are relative to *http://localhost*
+
+Class | Method | HTTP request | Description
+------------ | ------------- | ------------- | -------------
+*PolicyApi* | [**CreatePolicy**](docs/PolicyApi.md#createpolicy) | **Post** /policies |
+*PolicyApi* | [**DeletePolicy**](docs/PolicyApi.md#deletepolicy) | **Delete** /policies/{id} |
+*PolicyApi* | [**GetPolicy**](docs/PolicyApi.md#getpolicy) | **Get** /policies/{id} |
+*PolicyApi* | [**ListPolicies**](docs/PolicyApi.md#listpolicies) | **Get** /policies |
+*PolicyApi* | [**UpdatePolicy**](docs/PolicyApi.md#updatepolicy) | **Put** /policies/{id} |
+*RoleApi* | [**AddMembersToRole**](docs/RoleApi.md#addmemberstorole) | **Post** /roles/{id}/members | Add members to a role
+*RoleApi* | [**CreateRole**](docs/RoleApi.md#createrole) | **Post** /roles | Create a role
+*RoleApi* | [**DeleteRole**](docs/RoleApi.md#deleterole) | **Delete** /roles/{id} | Get a role by its ID
+*RoleApi* | [**GetRole**](docs/RoleApi.md#getrole) | **Get** /roles/{id} | Get a role by its ID
+*RoleApi* | [**ListRoles**](docs/RoleApi.md#listroles) | **Get** /roles | List all roles
+*RoleApi* | [**RemoveMembersFromRole**](docs/RoleApi.md#removemembersfromrole) | **Delete** /roles/{id}/members | Remove members from a role
+*WardenApi* | [**IsOAuth2AccessTokenAuthorized**](docs/WardenApi.md#isoauth2accesstokenauthorized) | **Post** /warden/oauth2/authorize | Check if an OAuth 2.0 access token is authorized to access a resource
+*WardenApi* | [**IsSubjectAuthorized**](docs/WardenApi.md#issubjectauthorized) | **Post** /warden/subjects/authorize | Check if a subject is authorized to access a resource
+
+
+## Documentation For Models
+
+ - [AuthenticationDefaultSession](docs/AuthenticationDefaultSession.md)
+ - [AuthenticationOAuth2IntrospectionRequest](docs/AuthenticationOAuth2IntrospectionRequest.md)
+ - [AuthenticationOAuth2Session](docs/AuthenticationOAuth2Session.md)
+ - [Authenticator](docs/Authenticator.md)
+ - [Firewall](docs/Firewall.md)
+ - [Handler](docs/Handler.md)
+ - [InlineResponse401](docs/InlineResponse401.md)
+ - [IntrospectionResponse](docs/IntrospectionResponse.md)
+ - [Manager](docs/Manager.md)
+ - [OAuth2IntrospectionAuthentication](docs/OAuth2IntrospectionAuthentication.md)
+ - [Policy](docs/Policy.md)
+ - [PolicyConditions](docs/PolicyConditions.md)
+ - [Role](docs/Role.md)
+ - [RoleMembers](docs/RoleMembers.md)
+ - [Session](docs/Session.md)
+ - [SwaggerCreatePolicyParameters](docs/SwaggerCreatePolicyParameters.md)
+ - [SwaggerDoesWardenAllowAccessRequestParameters](docs/SwaggerDoesWardenAllowAccessRequestParameters.md)
+ - [SwaggerDoesWardenAllowTokenAccessRqeuestParameters](docs/SwaggerDoesWardenAllowTokenAccessRqeuestParameters.md)
+ - [SwaggerGetPolicyParameters](docs/SwaggerGetPolicyParameters.md)
+ - [SwaggerListPolicyParameters](docs/SwaggerListPolicyParameters.md)
+ - [SwaggerListPolicyResponse](docs/SwaggerListPolicyResponse.md)
+ - [SwaggerUpdatePolicyParameters](docs/SwaggerUpdatePolicyParameters.md)
+ - [SwaggerWardenBaseRequest](docs/SwaggerWardenBaseRequest.md)
+ - [WardenOAuth2AuthorizationRequest](docs/WardenOAuth2AuthorizationRequest.md)
+ - [WardenOAuth2AuthorizationResponse](docs/WardenOAuth2AuthorizationResponse.md)
+ - [WardenSubjectAuthorizationRequest](docs/WardenSubjectAuthorizationRequest.md)
+ - [WardenSubjectAuthorizationResponse](docs/WardenSubjectAuthorizationResponse.md)
+ - [Writer](docs/Writer.md)
+
+
+## Documentation For Authorization
+
+ All endpoints do not require authorization.
+
+
+## Author
+
+hi@ory.am
+
diff --git a/sdk/go/keto/swagger/api_client.go b/sdk/go/keto/swagger/api_client.go
new file mode 100644
index 000000000..52873b0c0
--- /dev/null
+++ b/sdk/go/keto/swagger/api_client.go
@@ -0,0 +1,162 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "net/url"
+ "path/filepath"
+ "reflect"
+ "strings"
+
+ "github.com/go-resty/resty"
+)
+
+type APIClient struct {
+ config *Configuration
+}
+
+func (c *APIClient) SelectHeaderContentType(contentTypes []string) string {
+
+ if len(contentTypes) == 0 {
+ return ""
+ }
+ if contains(contentTypes, "application/json") {
+ return "application/json"
+ }
+ return contentTypes[0] // use the first content type specified in 'consumes'
+}
+
+func (c *APIClient) SelectHeaderAccept(accepts []string) string {
+
+ if len(accepts) == 0 {
+ return ""
+ }
+ if contains(accepts, "application/json") {
+ return "application/json"
+ }
+ return strings.Join(accepts, ",")
+}
+
+func contains(haystack []string, needle string) bool {
+ for _, a := range haystack {
+ if strings.ToLower(a) == strings.ToLower(needle) {
+ return true
+ }
+ }
+ return false
+}
+
+func (c *APIClient) CallAPI(path string, method string,
+ postBody interface{},
+ headerParams map[string]string,
+ queryParams url.Values,
+ formParams map[string]string,
+ fileName string,
+ fileBytes []byte) (*resty.Response, error) {
+
+ rClient := c.prepareClient()
+ request := c.prepareRequest(rClient, postBody, headerParams, queryParams, formParams, fileName, fileBytes)
+
+ switch strings.ToUpper(method) {
+ case "GET":
+ response, err := request.Get(path)
+ return response, err
+ case "POST":
+ response, err := request.Post(path)
+ return response, err
+ case "PUT":
+ response, err := request.Put(path)
+ return response, err
+ case "PATCH":
+ response, err := request.Patch(path)
+ return response, err
+ case "DELETE":
+ response, err := request.Delete(path)
+ return response, err
+ }
+
+ return nil, fmt.Errorf("invalid method %v", method)
+}
+
+func (c *APIClient) ParameterToString(obj interface{}, collectionFormat string) string {
+ delimiter := ""
+ switch collectionFormat {
+ case "pipes":
+ delimiter = "|"
+ case "ssv":
+ delimiter = " "
+ case "tsv":
+ delimiter = "\t"
+ case "csv":
+ delimiter = ","
+ }
+
+ if reflect.TypeOf(obj).Kind() == reflect.Slice {
+ return strings.Trim(strings.Replace(fmt.Sprint(obj), " ", delimiter, -1), "[]")
+ }
+
+ return fmt.Sprintf("%v", obj)
+}
+
+func (c *APIClient) prepareClient() *resty.Client {
+
+ rClient := resty.New()
+
+ rClient.SetDebug(c.config.Debug)
+ if c.config.Transport != nil {
+ rClient.SetTransport(c.config.Transport)
+ }
+
+ if c.config.Timeout != nil {
+ rClient.SetTimeout(*c.config.Timeout)
+ }
+ rClient.SetLogger(ioutil.Discard)
+ return rClient
+}
+
+func (c *APIClient) prepareRequest(
+ rClient *resty.Client,
+ postBody interface{},
+ headerParams map[string]string,
+ queryParams url.Values,
+ formParams map[string]string,
+ fileName string,
+ fileBytes []byte) *resty.Request {
+
+ request := rClient.R()
+ request.SetBody(postBody)
+
+ if c.config.UserAgent != "" {
+ request.SetHeader("User-Agent", c.config.UserAgent)
+ }
+
+ // add header parameter, if any
+ if len(headerParams) > 0 {
+ request.SetHeaders(headerParams)
+ }
+
+ // add query parameter, if any
+ if len(queryParams) > 0 {
+ request.SetMultiValueQueryParams(queryParams)
+ }
+
+ // add form parameter, if any
+ if len(formParams) > 0 {
+ request.SetFormData(formParams)
+ }
+
+ if len(fileBytes) > 0 && fileName != "" {
+ _, fileNm := filepath.Split(fileName)
+ request.SetFileReader("file", fileNm, bytes.NewReader(fileBytes))
+ }
+ return request
+}
diff --git a/sdk/go/keto/swagger/api_response.go b/sdk/go/keto/swagger/api_response.go
new file mode 100644
index 000000000..081af8f2e
--- /dev/null
+++ b/sdk/go/keto/swagger/api_response.go
@@ -0,0 +1,42 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+import (
+ "net/http"
+)
+
+type APIResponse struct {
+ *http.Response `json:"-"`
+ Message string `json:"message,omitempty"`
+ // Operation is the name of the swagger operation.
+ Operation string `json:"operation,omitempty"`
+ // RequestURL is the request URL. This value is always available, even if the
+ // embedded *http.Response is nil.
+ RequestURL string `json:"url,omitempty"`
+ // Method is the HTTP method used for the request. This value is always
+ // available, even if the embedded *http.Response is nil.
+ Method string `json:"method,omitempty"`
+ // Payload holds the contents of the response body (which may be nil or empty).
+ // This is provided here as the raw response.Body() reader will have already
+ // been drained.
+ Payload []byte `json:"-"`
+}
+
+func NewAPIResponse(r *http.Response) *APIResponse {
+
+ response := &APIResponse{Response: r}
+ return response
+}
+
+func NewAPIResponseWithError(errorMessage string) *APIResponse {
+
+ response := &APIResponse{Message: errorMessage}
+ return response
+}
diff --git a/sdk/go/keto/swagger/authentication_default_session.go b/sdk/go/keto/swagger/authentication_default_session.go
new file mode 100644
index 000000000..8dcb42db6
--- /dev/null
+++ b/sdk/go/keto/swagger/authentication_default_session.go
@@ -0,0 +1,18 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type AuthenticationDefaultSession struct {
+
+ // Allowed is true if the request is allowed and false otherwise.
+ Allowed bool `json:"allowed,omitempty"`
+
+ // Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.
+ Subject string `json:"subject,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/authentication_o_auth2_introspection_request.go b/sdk/go/keto/swagger/authentication_o_auth2_introspection_request.go
new file mode 100644
index 000000000..cd933949f
--- /dev/null
+++ b/sdk/go/keto/swagger/authentication_o_auth2_introspection_request.go
@@ -0,0 +1,18 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type AuthenticationOAuth2IntrospectionRequest struct {
+
+ // Scopes is an array of scopes that are required.
+ Scopes []string `json:"scopes,omitempty"`
+
+ // Token is the token to introspect.
+ Token string `json:"token,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/authentication_o_auth2_session.go b/sdk/go/keto/swagger/authentication_o_auth2_session.go
new file mode 100644
index 000000000..dad8c2163
--- /dev/null
+++ b/sdk/go/keto/swagger/authentication_o_auth2_session.go
@@ -0,0 +1,46 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+import (
+ "time"
+)
+
+type AuthenticationOAuth2Session struct {
+
+ // Extra represents arbitrary session data.
+ AccessTokenExtra map[string]interface{} `json:"accessTokenExtra,omitempty"`
+
+ // Allowed is true if the request is allowed and false otherwise.
+ Allowed bool `json:"allowed,omitempty"`
+
+ Audience string `json:"audience,omitempty"`
+
+ // ClientID is the id of the OAuth2 client that requested the token.
+ ClientId string `json:"clientId,omitempty"`
+
+ // ExpiresAt is the expiry timestamp.
+ ExpiresAt time.Time `json:"expiresAt,omitempty"`
+
+ // GrantedScopes is a list of scopes that the subject authorized when asked for consent.
+ GrantedScopes []string `json:"grantedScopes,omitempty"`
+
+ // IssuedAt is the token creation time stamp.
+ IssuedAt time.Time `json:"issuedAt,omitempty"`
+
+ // Issuer is the id of the issuer, typically an hydra instance.
+ Issuer string `json:"issuer,omitempty"`
+
+ NotBefore time.Time `json:"notBefore,omitempty"`
+
+ // Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.
+ Subject string `json:"subject,omitempty"`
+
+ Username string `json:"username,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/authenticator.go b/sdk/go/keto/swagger/authenticator.go
new file mode 100644
index 000000000..238a0a6f1
--- /dev/null
+++ b/sdk/go/keto/swagger/authenticator.go
@@ -0,0 +1,12 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type Authenticator struct {
+}
diff --git a/sdk/go/keto/swagger/configuration.go b/sdk/go/keto/swagger/configuration.go
new file mode 100644
index 000000000..353060fbb
--- /dev/null
+++ b/sdk/go/keto/swagger/configuration.go
@@ -0,0 +1,64 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+import (
+ "encoding/base64"
+ "net/http"
+ "time"
+)
+
+type Configuration struct {
+ Username string `json:"userName,omitempty"`
+ Password string `json:"password,omitempty"`
+ APIKeyPrefix map[string]string `json:"APIKeyPrefix,omitempty"`
+ APIKey map[string]string `json:"APIKey,omitempty"`
+ Debug bool `json:"debug,omitempty"`
+ DebugFile string `json:"debugFile,omitempty"`
+ OAuthToken string `json:"oAuthToken,omitempty"`
+ BasePath string `json:"basePath,omitempty"`
+ Host string `json:"host,omitempty"`
+ Scheme string `json:"scheme,omitempty"`
+ AccessToken string `json:"accessToken,omitempty"`
+ DefaultHeader map[string]string `json:"defaultHeader,omitempty"`
+ UserAgent string `json:"userAgent,omitempty"`
+ APIClient *APIClient
+ Transport http.RoundTripper
+ Timeout *time.Duration `json:"timeout,omitempty"`
+}
+
+func NewConfiguration() *Configuration {
+ cfg := &Configuration{
+ BasePath: "http://localhost",
+ DefaultHeader: make(map[string]string),
+ APIKey: make(map[string]string),
+ APIKeyPrefix: make(map[string]string),
+ UserAgent: "Swagger-Codegen/1.0.0/go",
+ APIClient: &APIClient{},
+ }
+
+ cfg.APIClient.config = cfg
+ return cfg
+}
+
+func (c *Configuration) GetBasicAuthEncodedString() string {
+ return base64.StdEncoding.EncodeToString([]byte(c.Username + ":" + c.Password))
+}
+
+func (c *Configuration) AddDefaultHeader(key string, value string) {
+ c.DefaultHeader[key] = value
+}
+
+func (c *Configuration) GetAPIKeyWithPrefix(APIKeyIdentifier string) string {
+ if c.APIKeyPrefix[APIKeyIdentifier] != "" {
+ return c.APIKeyPrefix[APIKeyIdentifier] + " " + c.APIKey[APIKeyIdentifier]
+ }
+
+ return c.APIKey[APIKeyIdentifier]
+}
diff --git a/sdk/go/keto/swagger/docs/AuthenticationDefaultSession.md b/sdk/go/keto/swagger/docs/AuthenticationDefaultSession.md
new file mode 100644
index 000000000..ec0c0812f
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/AuthenticationDefaultSession.md
@@ -0,0 +1,11 @@
+# AuthenticationDefaultSession
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Allowed** | **bool** | Allowed is true if the request is allowed and false otherwise. | [optional] [default to null]
+**Subject** | **string** | Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too. | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/AuthenticationOAuth2IntrospectionRequest.md b/sdk/go/keto/swagger/docs/AuthenticationOAuth2IntrospectionRequest.md
new file mode 100644
index 000000000..1ca9f35b8
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/AuthenticationOAuth2IntrospectionRequest.md
@@ -0,0 +1,11 @@
+# AuthenticationOAuth2IntrospectionRequest
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Scopes** | **[]string** | Scopes is an array of scopes that are required. | [optional] [default to null]
+**Token** | **string** | Token is the token to introspect. | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/AuthenticationOAuth2Session.md b/sdk/go/keto/swagger/docs/AuthenticationOAuth2Session.md
new file mode 100644
index 000000000..34b4cc44d
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/AuthenticationOAuth2Session.md
@@ -0,0 +1,20 @@
+# AuthenticationOAuth2Session
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**AccessTokenExtra** | [**map[string]interface{}**](interface{}.md) | Extra represents arbitrary session data. | [optional] [default to null]
+**Allowed** | **bool** | Allowed is true if the request is allowed and false otherwise. | [optional] [default to null]
+**Audience** | **string** | | [optional] [default to null]
+**ClientId** | **string** | ClientID is the id of the OAuth2 client that requested the token. | [optional] [default to null]
+**ExpiresAt** | [**time.Time**](time.Time.md) | ExpiresAt is the expiry timestamp. | [optional] [default to null]
+**GrantedScopes** | **[]string** | GrantedScopes is a list of scopes that the subject authorized when asked for consent. | [optional] [default to null]
+**IssuedAt** | [**time.Time**](time.Time.md) | IssuedAt is the token creation time stamp. | [optional] [default to null]
+**Issuer** | **string** | Issuer is the id of the issuer, typically an hydra instance. | [optional] [default to null]
+**NotBefore** | [**time.Time**](time.Time.md) | | [optional] [default to null]
+**Subject** | **string** | Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too. | [optional] [default to null]
+**Username** | **string** | | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/Authenticator.md b/sdk/go/keto/swagger/docs/Authenticator.md
new file mode 100644
index 000000000..841ffbb14
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/Authenticator.md
@@ -0,0 +1,9 @@
+# Authenticator
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/Firewall.md b/sdk/go/keto/swagger/docs/Firewall.md
new file mode 100644
index 000000000..134131c8c
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/Firewall.md
@@ -0,0 +1,9 @@
+# Firewall
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/Handler.md b/sdk/go/keto/swagger/docs/Handler.md
new file mode 100644
index 000000000..dd845173e
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/Handler.md
@@ -0,0 +1,11 @@
+# Handler
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**H** | [**Writer**](Writer.md) | | [optional] [default to null]
+**Manager** | [**Manager**](Manager.md) | | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/InlineResponse401.md b/sdk/go/keto/swagger/docs/InlineResponse401.md
new file mode 100644
index 000000000..0ac1e5d99
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/InlineResponse401.md
@@ -0,0 +1,15 @@
+# InlineResponse401
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Code** | **int64** | | [optional] [default to null]
+**Details** | [**[]map[string]interface{}**](map.md) | | [optional] [default to null]
+**Message** | **string** | | [optional] [default to null]
+**Reason** | **string** | | [optional] [default to null]
+**Request** | **string** | | [optional] [default to null]
+**Status** | **string** | | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/IntrospectionResponse.md b/sdk/go/keto/swagger/docs/IntrospectionResponse.md
new file mode 100644
index 000000000..d9e41b04d
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/IntrospectionResponse.md
@@ -0,0 +1,19 @@
+# IntrospectionResponse
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Active** | **bool** | | [optional] [default to null]
+**Aud** | **string** | | [optional] [default to null]
+**ClientId** | **string** | | [optional] [default to null]
+**Exp** | **int64** | | [optional] [default to null]
+**Iat** | **int64** | | [optional] [default to null]
+**Iss** | **string** | | [optional] [default to null]
+**Nbf** | **int64** | | [optional] [default to null]
+**Scope** | **string** | | [optional] [default to null]
+**Sub** | **string** | Here, it's sub | [optional] [default to null]
+**Username** | **string** | | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/Manager.md b/sdk/go/keto/swagger/docs/Manager.md
new file mode 100644
index 000000000..485b36683
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/Manager.md
@@ -0,0 +1,9 @@
+# Manager
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/OAuth2IntrospectionAuthentication.md b/sdk/go/keto/swagger/docs/OAuth2IntrospectionAuthentication.md
new file mode 100644
index 000000000..aeccd97ff
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/OAuth2IntrospectionAuthentication.md
@@ -0,0 +1,9 @@
+# OAuth2IntrospectionAuthentication
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/Policy.md b/sdk/go/keto/swagger/docs/Policy.md
new file mode 100644
index 000000000..56930955e
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/Policy.md
@@ -0,0 +1,16 @@
+# Policy
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Actions** | **[]string** | Actions impacted by the policy. | [optional] [default to null]
+**Conditions** | [**map[string]PolicyConditions**](policy_conditions.md) | Conditions under which the policy is active. | [optional] [default to null]
+**Description** | **string** | Description of the policy. | [optional] [default to null]
+**Effect** | **string** | Effect of the policy | [optional] [default to null]
+**Id** | **string** | ID of the policy. | [optional] [default to null]
+**Resources** | **[]string** | Resources impacted by the policy. | [optional] [default to null]
+**Subjects** | **[]string** | Subjects impacted by the policy. | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/PolicyApi.md b/sdk/go/keto/swagger/docs/PolicyApi.md
new file mode 100644
index 000000000..35aa7df8f
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/PolicyApi.md
@@ -0,0 +1,160 @@
+# \PolicyApi
+
+All URIs are relative to *http://localhost*
+
+Method | HTTP request | Description
+------------- | ------------- | -------------
+[**CreatePolicy**](PolicyApi.md#CreatePolicy) | **Post** /policies |
+[**DeletePolicy**](PolicyApi.md#DeletePolicy) | **Delete** /policies/{id} |
+[**GetPolicy**](PolicyApi.md#GetPolicy) | **Get** /policies/{id} |
+[**ListPolicies**](PolicyApi.md#ListPolicies) | **Get** /policies |
+[**UpdatePolicy**](PolicyApi.md#UpdatePolicy) | **Put** /policies/{id} |
+
+
+# **CreatePolicy**
+> Policy CreatePolicy($body)
+
+
+
+Create an Access Control Policy
+
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **body** | [**Policy**](Policy.md)| | [optional]
+
+### Return type
+
+[**Policy**](policy.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
+# **DeletePolicy**
+> DeletePolicy($id)
+
+
+
+Delete an Access Control Policy
+
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **id** | **string**| The id of the policy. |
+
+### Return type
+
+void (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
+# **GetPolicy**
+> Policy GetPolicy($id)
+
+
+
+Get an Access Control Policy
+
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **id** | **string**| The id of the policy. |
+
+### Return type
+
+[**Policy**](policy.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
+# **ListPolicies**
+> []Policy ListPolicies($offset, $limit)
+
+
+
+List Access Control Policies
+
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **offset** | **int64**| The offset from where to start looking. | [optional]
+ **limit** | **int64**| The maximum amount of policies returned. | [optional]
+
+### Return type
+
+[**[]Policy**](policy.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
+# **UpdatePolicy**
+> Policy UpdatePolicy($id, $body)
+
+
+
+Update an Access Control Policy
+
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **id** | **string**| The id of the policy. |
+ **body** | [**Policy**](Policy.md)| | [optional]
+
+### Return type
+
+[**Policy**](policy.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
diff --git a/sdk/go/keto/swagger/docs/PolicyConditions.md b/sdk/go/keto/swagger/docs/PolicyConditions.md
new file mode 100644
index 000000000..e38c6e6a1
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/PolicyConditions.md
@@ -0,0 +1,11 @@
+# PolicyConditions
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Options** | [**map[string]interface{}**](interface{}.md) | | [optional] [default to null]
+**Type_** | **string** | | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/Role.md b/sdk/go/keto/swagger/docs/Role.md
new file mode 100644
index 000000000..b8be2d4b4
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/Role.md
@@ -0,0 +1,11 @@
+# Role
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Id** | **string** | ID is the role's unique id. | [optional] [default to null]
+**Members** | **[]string** | Members is who belongs to the role. | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/RoleApi.md b/sdk/go/keto/swagger/docs/RoleApi.md
new file mode 100644
index 000000000..e21d5662f
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/RoleApi.md
@@ -0,0 +1,192 @@
+# \RoleApi
+
+All URIs are relative to *http://localhost*
+
+Method | HTTP request | Description
+------------- | ------------- | -------------
+[**AddMembersToRole**](RoleApi.md#AddMembersToRole) | **Post** /roles/{id}/members | Add members to a role
+[**CreateRole**](RoleApi.md#CreateRole) | **Post** /roles | Create a role
+[**DeleteRole**](RoleApi.md#DeleteRole) | **Delete** /roles/{id} | Get a role by its ID
+[**GetRole**](RoleApi.md#GetRole) | **Get** /roles/{id} | Get a role by its ID
+[**ListRoles**](RoleApi.md#ListRoles) | **Get** /roles | List all roles
+[**RemoveMembersFromRole**](RoleApi.md#RemoveMembersFromRole) | **Delete** /roles/{id}/members | Remove members from a role
+
+
+# **AddMembersToRole**
+> AddMembersToRole($id, $body)
+
+Add members to a role
+
+A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to add members (users, applications, ...) to a specific role. You have to know the role's ID.
+
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **id** | **string**| The id of the role to modify. |
+ **body** | [**RoleMembers**](RoleMembers.md)| | [optional]
+
+### Return type
+
+void (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
+# **CreateRole**
+> Role CreateRole($body)
+
+Create a role
+
+A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to create a new role. You may define members as well but you don't have to.
+
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **body** | [**Role**](Role.md)| | [optional]
+
+### Return type
+
+[**Role**](role.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
+# **DeleteRole**
+> DeleteRole($id)
+
+Get a role by its ID
+
+A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to delete an existing role. You have to know the role's ID.
+
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **id** | **string**| The id of the role to look up. |
+
+### Return type
+
+void (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
+# **GetRole**
+> Role GetRole($id)
+
+Get a role by its ID
+
+A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to retrieve an existing role. You have to know the role's ID.
+
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **id** | **string**| The id of the role to look up. |
+
+### Return type
+
+[**Role**](role.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
+# **ListRoles**
+> []Role ListRoles($member, $limit, $offset)
+
+List all roles
+
+A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to retrieve all roles that are stored in the system.
+
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **member** | **string**| The id of the member to look up. | [optional]
+ **limit** | **int64**| The maximum amount of policies returned. | [optional]
+ **offset** | **int64**| The offset from where to start looking. | [optional]
+
+### Return type
+
+[**[]Role**](role.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
+# **RemoveMembersFromRole**
+> RemoveMembersFromRole($id, $body)
+
+Remove members from a role
+
+A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to remove members (users, applications, ...) from a specific role. You have to know the role's ID.
+
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **id** | **string**| The id of the role to modify. |
+ **body** | [**RoleMembers**](RoleMembers.md)| | [optional]
+
+### Return type
+
+void (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
diff --git a/sdk/go/keto/swagger/docs/RoleMembers.md b/sdk/go/keto/swagger/docs/RoleMembers.md
new file mode 100644
index 000000000..7d96546fb
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/RoleMembers.md
@@ -0,0 +1,10 @@
+# RoleMembers
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Members** | **[]string** | | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/Session.md b/sdk/go/keto/swagger/docs/Session.md
new file mode 100644
index 000000000..9a47b3b1c
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/Session.md
@@ -0,0 +1,10 @@
+# Session
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**GetSubject** | **string** | | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/SwaggerCreatePolicyParameters.md b/sdk/go/keto/swagger/docs/SwaggerCreatePolicyParameters.md
new file mode 100644
index 000000000..c8304b894
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/SwaggerCreatePolicyParameters.md
@@ -0,0 +1,10 @@
+# SwaggerCreatePolicyParameters
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Body** | [**Policy**](policy.md) | | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/SwaggerDoesWardenAllowAccessRequestParameters.md b/sdk/go/keto/swagger/docs/SwaggerDoesWardenAllowAccessRequestParameters.md
new file mode 100644
index 000000000..9f959938b
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/SwaggerDoesWardenAllowAccessRequestParameters.md
@@ -0,0 +1,10 @@
+# SwaggerDoesWardenAllowAccessRequestParameters
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Body** | [**WardenSubjectAuthorizationRequest**](WardenSubjectAuthorizationRequest.md) | | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/SwaggerDoesWardenAllowTokenAccessRqeuestParameters.md b/sdk/go/keto/swagger/docs/SwaggerDoesWardenAllowTokenAccessRqeuestParameters.md
new file mode 100644
index 000000000..6651ad074
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/SwaggerDoesWardenAllowTokenAccessRqeuestParameters.md
@@ -0,0 +1,10 @@
+# SwaggerDoesWardenAllowTokenAccessRqeuestParameters
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Body** | [**WardenOAuth2AuthorizationRequest**](WardenOAuth2AuthorizationRequest.md) | | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/SwaggerGetPolicyParameters.md b/sdk/go/keto/swagger/docs/SwaggerGetPolicyParameters.md
new file mode 100644
index 000000000..875e78471
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/SwaggerGetPolicyParameters.md
@@ -0,0 +1,10 @@
+# SwaggerGetPolicyParameters
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Id** | **string** | The id of the policy. in: path | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/SwaggerListPolicyParameters.md b/sdk/go/keto/swagger/docs/SwaggerListPolicyParameters.md
new file mode 100644
index 000000000..8fc0c8862
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/SwaggerListPolicyParameters.md
@@ -0,0 +1,11 @@
+# SwaggerListPolicyParameters
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Limit** | **int64** | The maximum amount of policies returned. in: query | [optional] [default to null]
+**Offset** | **int64** | The offset from where to start looking. in: query | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/SwaggerListPolicyResponse.md b/sdk/go/keto/swagger/docs/SwaggerListPolicyResponse.md
new file mode 100644
index 000000000..73c549f54
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/SwaggerListPolicyResponse.md
@@ -0,0 +1,10 @@
+# SwaggerListPolicyResponse
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Body** | [**[]Policy**](policy.md) | in: body type: array | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/SwaggerUpdatePolicyParameters.md b/sdk/go/keto/swagger/docs/SwaggerUpdatePolicyParameters.md
new file mode 100644
index 000000000..c78915af3
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/SwaggerUpdatePolicyParameters.md
@@ -0,0 +1,11 @@
+# SwaggerUpdatePolicyParameters
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Body** | [**Policy**](policy.md) | | [optional] [default to null]
+**Id** | **string** | The id of the policy. in: path | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/SwaggerWardenBaseRequest.md b/sdk/go/keto/swagger/docs/SwaggerWardenBaseRequest.md
new file mode 100644
index 000000000..2a43ea249
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/SwaggerWardenBaseRequest.md
@@ -0,0 +1,12 @@
+# SwaggerWardenBaseRequest
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Action** | **string** | Action is the action that is requested on the resource. | [optional] [default to null]
+**Context** | [**map[string]interface{}**](interface{}.md) | Context is the request's environmental context. | [optional] [default to null]
+**Resource** | **string** | Resource is the resource that access is requested to. | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/WardenApi.md b/sdk/go/keto/swagger/docs/WardenApi.md
new file mode 100644
index 000000000..bd8e3c7b2
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/WardenApi.md
@@ -0,0 +1,68 @@
+# \WardenApi
+
+All URIs are relative to *http://localhost*
+
+Method | HTTP request | Description
+------------- | ------------- | -------------
+[**IsOAuth2AccessTokenAuthorized**](WardenApi.md#IsOAuth2AccessTokenAuthorized) | **Post** /warden/oauth2/authorize | Check if an OAuth 2.0 access token is authorized to access a resource
+[**IsSubjectAuthorized**](WardenApi.md#IsSubjectAuthorized) | **Post** /warden/subjects/authorize | Check if a subject is authorized to access a resource
+
+
+# **IsOAuth2AccessTokenAuthorized**
+> IsOAuth2AccessTokenAuthorized($body)
+
+Check if an OAuth 2.0 access token is authorized to access a resource
+
+Checks if a token is valid and if the token subject is allowed to perform an action on a resource. This endpoint requires a token, a scope, a resource name, an action name and a context. If a token is expired/invalid, has not been granted the requested scope or the subject is not allowed to perform the action on the resource, this endpoint returns a 200 response with `{ \"allowed\": false }`. This endpoint passes all data from the upstream OAuth 2.0 token introspection endpoint. If you use ORY Hydra as an upstream OAuth 2.0 provider, data set through the `accessTokenExtra` field in the consent flow will be included in this response as well.
+
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **body** | [**WardenOAuth2AuthorizationRequest**](WardenOAuth2AuthorizationRequest.md)| | [optional]
+
+### Return type
+
+void (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
+# **IsSubjectAuthorized**
+> IsSubjectAuthorized($body)
+
+Check if a subject is authorized to access a resource
+
+Checks if a subject (e.g. user ID, API key, ...) is allowed to perform a certain action on a resource.
+
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **body** | [**WardenSubjectAuthorizationRequest**](WardenSubjectAuthorizationRequest.md)| | [optional]
+
+### Return type
+
+void (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
diff --git a/sdk/go/keto/swagger/docs/WardenOAuth2AuthorizationRequest.md b/sdk/go/keto/swagger/docs/WardenOAuth2AuthorizationRequest.md
new file mode 100644
index 000000000..cda73efb1
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/WardenOAuth2AuthorizationRequest.md
@@ -0,0 +1,14 @@
+# WardenOAuth2AuthorizationRequest
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Action** | **string** | Action is the action that is requested on the resource. | [optional] [default to null]
+**Context** | [**map[string]interface{}**](interface{}.md) | Context is the request's environmental context. | [optional] [default to null]
+**Resource** | **string** | Resource is the resource that access is requested to. | [optional] [default to null]
+**Scopes** | **[]string** | Scopes is an array of scopes that are required. | [optional] [default to null]
+**Token** | **string** | Token is the token to introspect. | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/WardenOAuth2AuthorizationResponse.md b/sdk/go/keto/swagger/docs/WardenOAuth2AuthorizationResponse.md
new file mode 100644
index 000000000..a11fb31f7
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/WardenOAuth2AuthorizationResponse.md
@@ -0,0 +1,20 @@
+# WardenOAuth2AuthorizationResponse
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**AccessTokenExtra** | [**map[string]interface{}**](interface{}.md) | Extra represents arbitrary session data. | [optional] [default to null]
+**Allowed** | **bool** | Allowed is true if the request is allowed and false otherwise. | [optional] [default to null]
+**Audience** | **string** | | [optional] [default to null]
+**ClientId** | **string** | ClientID is the id of the OAuth2 client that requested the token. | [optional] [default to null]
+**ExpiresAt** | [**time.Time**](time.Time.md) | ExpiresAt is the expiry timestamp. | [optional] [default to null]
+**GrantedScopes** | **[]string** | GrantedScopes is a list of scopes that the subject authorized when asked for consent. | [optional] [default to null]
+**IssuedAt** | [**time.Time**](time.Time.md) | IssuedAt is the token creation time stamp. | [optional] [default to null]
+**Issuer** | **string** | Issuer is the id of the issuer, typically an hydra instance. | [optional] [default to null]
+**NotBefore** | [**time.Time**](time.Time.md) | | [optional] [default to null]
+**Subject** | **string** | Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too. | [optional] [default to null]
+**Username** | **string** | | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/WardenSubjectAuthorizationRequest.md b/sdk/go/keto/swagger/docs/WardenSubjectAuthorizationRequest.md
new file mode 100644
index 000000000..189063ace
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/WardenSubjectAuthorizationRequest.md
@@ -0,0 +1,13 @@
+# WardenSubjectAuthorizationRequest
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Action** | **string** | Action is the action that is requested on the resource. | [optional] [default to null]
+**Context** | [**map[string]interface{}**](interface{}.md) | Context is the request's environmental context. | [optional] [default to null]
+**Resource** | **string** | Resource is the resource that access is requested to. | [optional] [default to null]
+**Subject** | **string** | Subejct is the subject that is requesting access. | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/WardenSubjectAuthorizationResponse.md b/sdk/go/keto/swagger/docs/WardenSubjectAuthorizationResponse.md
new file mode 100644
index 000000000..cb59ef027
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/WardenSubjectAuthorizationResponse.md
@@ -0,0 +1,11 @@
+# WardenSubjectAuthorizationResponse
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**Allowed** | **bool** | Allowed is true if the request is allowed and false otherwise. | [optional] [default to null]
+**Subject** | **string** | Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too. | [optional] [default to null]
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/docs/Writer.md b/sdk/go/keto/swagger/docs/Writer.md
new file mode 100644
index 000000000..530fd0f80
--- /dev/null
+++ b/sdk/go/keto/swagger/docs/Writer.md
@@ -0,0 +1,9 @@
+# Writer
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+
+[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+
+
diff --git a/sdk/go/keto/swagger/firewall.go b/sdk/go/keto/swagger/firewall.go
new file mode 100644
index 000000000..bb3c6ff2f
--- /dev/null
+++ b/sdk/go/keto/swagger/firewall.go
@@ -0,0 +1,12 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type Firewall struct {
+}
diff --git a/sdk/go/keto/swagger/git_push.sh b/sdk/go/keto/swagger/git_push.sh
new file mode 100644
index 000000000..ed374619b
--- /dev/null
+++ b/sdk/go/keto/swagger/git_push.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
+#
+# Usage example: /bin/sh ./git_push.sh wing328 swagger-petstore-perl "minor update"
+
+git_user_id=$1
+git_repo_id=$2
+release_note=$3
+
+if [ "$git_user_id" = "" ]; then
+ git_user_id="GIT_USER_ID"
+ echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
+fi
+
+if [ "$git_repo_id" = "" ]; then
+ git_repo_id="GIT_REPO_ID"
+ echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
+fi
+
+if [ "$release_note" = "" ]; then
+ release_note="Minor update"
+ echo "[INFO] No command line input provided. Set \$release_note to $release_note"
+fi
+
+# Initialize the local directory as a Git repository
+git init
+
+# Adds the files in the local repository and stages them for commit.
+git add .
+
+# Commits the tracked changes and prepares them to be pushed to a remote repository.
+git commit -m "$release_note"
+
+# Sets the new remote
+git_remote=`git remote`
+if [ "$git_remote" = "" ]; then # git remote not defined
+
+ if [ "$GIT_TOKEN" = "" ]; then
+ echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment."
+ git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git
+ else
+ git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git
+ fi
+
+fi
+
+git pull origin master
+
+# Pushes (Forces) the changes in the local repository up to the remote repository
+echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git"
+git push origin master 2>&1 | grep -v 'To https'
+
diff --git a/sdk/go/keto/swagger/handler.go b/sdk/go/keto/swagger/handler.go
new file mode 100644
index 000000000..705d004b4
--- /dev/null
+++ b/sdk/go/keto/swagger/handler.go
@@ -0,0 +1,15 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type Handler struct {
+ H Writer `json:"H,omitempty"`
+
+ Manager Manager `json:"Manager,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/inline_response_401.go b/sdk/go/keto/swagger/inline_response_401.go
new file mode 100644
index 000000000..180359d20
--- /dev/null
+++ b/sdk/go/keto/swagger/inline_response_401.go
@@ -0,0 +1,23 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type InlineResponse401 struct {
+ Code int64 `json:"code,omitempty"`
+
+ Details []map[string]interface{} `json:"details,omitempty"`
+
+ Message string `json:"message,omitempty"`
+
+ Reason string `json:"reason,omitempty"`
+
+ Request string `json:"request,omitempty"`
+
+ Status string `json:"status,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/introspection_response.go b/sdk/go/keto/swagger/introspection_response.go
new file mode 100644
index 000000000..b3ef8d2a0
--- /dev/null
+++ b/sdk/go/keto/swagger/introspection_response.go
@@ -0,0 +1,32 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type IntrospectionResponse struct {
+ Active bool `json:"active,omitempty"`
+
+ Aud string `json:"aud,omitempty"`
+
+ ClientId string `json:"client_id,omitempty"`
+
+ Exp int64 `json:"exp,omitempty"`
+
+ Iat int64 `json:"iat,omitempty"`
+
+ Iss string `json:"iss,omitempty"`
+
+ Nbf int64 `json:"nbf,omitempty"`
+
+ Scope string `json:"scope,omitempty"`
+
+ // Here, it's sub
+ Sub string `json:"sub,omitempty"`
+
+ Username string `json:"username,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/manager.go b/sdk/go/keto/swagger/manager.go
new file mode 100644
index 000000000..aaf6f051d
--- /dev/null
+++ b/sdk/go/keto/swagger/manager.go
@@ -0,0 +1,12 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type Manager struct {
+}
diff --git a/sdk/go/keto/swagger/o_auth2_introspection_authentication.go b/sdk/go/keto/swagger/o_auth2_introspection_authentication.go
new file mode 100644
index 000000000..055705f0d
--- /dev/null
+++ b/sdk/go/keto/swagger/o_auth2_introspection_authentication.go
@@ -0,0 +1,12 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type OAuth2IntrospectionAuthentication struct {
+}
diff --git a/sdk/go/keto/swagger/policy.go b/sdk/go/keto/swagger/policy.go
new file mode 100644
index 000000000..c5821aa3b
--- /dev/null
+++ b/sdk/go/keto/swagger/policy.go
@@ -0,0 +1,33 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type Policy struct {
+
+ // Actions impacted by the policy.
+ Actions []string `json:"actions,omitempty"`
+
+ // Conditions under which the policy is active.
+ Conditions map[string]PolicyConditions `json:"conditions,omitempty"`
+
+ // Description of the policy.
+ Description string `json:"description,omitempty"`
+
+ // Effect of the policy
+ Effect string `json:"effect,omitempty"`
+
+ // ID of the policy.
+ Id string `json:"id,omitempty"`
+
+ // Resources impacted by the policy.
+ Resources []string `json:"resources,omitempty"`
+
+ // Subjects impacted by the policy.
+ Subjects []string `json:"subjects,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/policy_api.go b/sdk/go/keto/swagger/policy_api.go
new file mode 100644
index 000000000..9587a9d2e
--- /dev/null
+++ b/sdk/go/keto/swagger/policy_api.go
@@ -0,0 +1,345 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/url"
+ "strings"
+)
+
+type PolicyApi struct {
+ Configuration *Configuration
+}
+
+func NewPolicyApi() *PolicyApi {
+ configuration := NewConfiguration()
+ return &PolicyApi{
+ Configuration: configuration,
+ }
+}
+
+func NewPolicyApiWithBasePath(basePath string) *PolicyApi {
+ configuration := NewConfiguration()
+ configuration.BasePath = basePath
+
+ return &PolicyApi{
+ Configuration: configuration,
+ }
+}
+
+/**
+ *
+ * Create an Access Control Policy
+ *
+ * @param body
+ * @return *Policy
+ */
+func (a PolicyApi) CreatePolicy(body Policy) (*Policy, *APIResponse, error) {
+
+ var localVarHttpMethod = strings.ToUpper("Post")
+ // create path and map variables
+ localVarPath := a.Configuration.BasePath + "/policies"
+
+ localVarHeaderParams := make(map[string]string)
+ localVarQueryParams := url.Values{}
+ localVarFormParams := make(map[string]string)
+ var localVarPostBody interface{}
+ var localVarFileName string
+ var localVarFileBytes []byte
+ // add default headers if any
+ for key := range a.Configuration.DefaultHeader {
+ localVarHeaderParams[key] = a.Configuration.DefaultHeader[key]
+ }
+
+ // to determine the Content-Type header
+ localVarHttpContentTypes := []string{"application/json"}
+
+ // set Content-Type header
+ localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes)
+ if localVarHttpContentType != "" {
+ localVarHeaderParams["Content-Type"] = localVarHttpContentType
+ }
+ // to determine the Accept header
+ localVarHttpHeaderAccepts := []string{
+ "application/json",
+ }
+
+ // set Accept header
+ localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts)
+ if localVarHttpHeaderAccept != "" {
+ localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+ }
+ // body params
+ localVarPostBody = &body
+ var successPayload = new(Policy)
+ localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+
+ var localVarURL, _ = url.Parse(localVarPath)
+ localVarURL.RawQuery = localVarQueryParams.Encode()
+ var localVarAPIResponse = &APIResponse{Operation: "CreatePolicy", Method: localVarHttpMethod, RequestURL: localVarURL.String()}
+ if localVarHttpResponse != nil {
+ localVarAPIResponse.Response = localVarHttpResponse.RawResponse
+ localVarAPIResponse.Payload = localVarHttpResponse.Body()
+ }
+
+ if err != nil {
+ return successPayload, localVarAPIResponse, err
+ }
+ err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload)
+ return successPayload, localVarAPIResponse, err
+}
+
+/**
+ *
+ * Delete an Access Control Policy
+ *
+ * @param id The id of the policy.
+ * @return void
+ */
+func (a PolicyApi) DeletePolicy(id string) (*APIResponse, error) {
+
+ var localVarHttpMethod = strings.ToUpper("Delete")
+ // create path and map variables
+ localVarPath := a.Configuration.BasePath + "/policies/{id}"
+ localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1)
+
+ localVarHeaderParams := make(map[string]string)
+ localVarQueryParams := url.Values{}
+ localVarFormParams := make(map[string]string)
+ var localVarPostBody interface{}
+ var localVarFileName string
+ var localVarFileBytes []byte
+ // add default headers if any
+ for key := range a.Configuration.DefaultHeader {
+ localVarHeaderParams[key] = a.Configuration.DefaultHeader[key]
+ }
+
+ // to determine the Content-Type header
+ localVarHttpContentTypes := []string{"application/json"}
+
+ // set Content-Type header
+ localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes)
+ if localVarHttpContentType != "" {
+ localVarHeaderParams["Content-Type"] = localVarHttpContentType
+ }
+ // to determine the Accept header
+ localVarHttpHeaderAccepts := []string{
+ "application/json",
+ }
+
+ // set Accept header
+ localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts)
+ if localVarHttpHeaderAccept != "" {
+ localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+ }
+ localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+
+ var localVarURL, _ = url.Parse(localVarPath)
+ localVarURL.RawQuery = localVarQueryParams.Encode()
+ var localVarAPIResponse = &APIResponse{Operation: "DeletePolicy", Method: localVarHttpMethod, RequestURL: localVarURL.String()}
+ if localVarHttpResponse != nil {
+ localVarAPIResponse.Response = localVarHttpResponse.RawResponse
+ localVarAPIResponse.Payload = localVarHttpResponse.Body()
+ }
+
+ if err != nil {
+ return localVarAPIResponse, err
+ }
+ return localVarAPIResponse, err
+}
+
+/**
+ *
+ * Get an Access Control Policy
+ *
+ * @param id The id of the policy.
+ * @return *Policy
+ */
+func (a PolicyApi) GetPolicy(id string) (*Policy, *APIResponse, error) {
+
+ var localVarHttpMethod = strings.ToUpper("Get")
+ // create path and map variables
+ localVarPath := a.Configuration.BasePath + "/policies/{id}"
+ localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1)
+
+ localVarHeaderParams := make(map[string]string)
+ localVarQueryParams := url.Values{}
+ localVarFormParams := make(map[string]string)
+ var localVarPostBody interface{}
+ var localVarFileName string
+ var localVarFileBytes []byte
+ // add default headers if any
+ for key := range a.Configuration.DefaultHeader {
+ localVarHeaderParams[key] = a.Configuration.DefaultHeader[key]
+ }
+
+ // to determine the Content-Type header
+ localVarHttpContentTypes := []string{"application/json"}
+
+ // set Content-Type header
+ localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes)
+ if localVarHttpContentType != "" {
+ localVarHeaderParams["Content-Type"] = localVarHttpContentType
+ }
+ // to determine the Accept header
+ localVarHttpHeaderAccepts := []string{
+ "application/json",
+ }
+
+ // set Accept header
+ localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts)
+ if localVarHttpHeaderAccept != "" {
+ localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+ }
+ var successPayload = new(Policy)
+ localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+
+ var localVarURL, _ = url.Parse(localVarPath)
+ localVarURL.RawQuery = localVarQueryParams.Encode()
+ var localVarAPIResponse = &APIResponse{Operation: "GetPolicy", Method: localVarHttpMethod, RequestURL: localVarURL.String()}
+ if localVarHttpResponse != nil {
+ localVarAPIResponse.Response = localVarHttpResponse.RawResponse
+ localVarAPIResponse.Payload = localVarHttpResponse.Body()
+ }
+
+ if err != nil {
+ return successPayload, localVarAPIResponse, err
+ }
+ err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload)
+ return successPayload, localVarAPIResponse, err
+}
+
+/**
+ *
+ * List Access Control Policies
+ *
+ * @param offset The offset from where to start looking.
+ * @param limit The maximum amount of policies returned.
+ * @return []Policy
+ */
+func (a PolicyApi) ListPolicies(offset int64, limit int64) ([]Policy, *APIResponse, error) {
+
+ var localVarHttpMethod = strings.ToUpper("Get")
+ // create path and map variables
+ localVarPath := a.Configuration.BasePath + "/policies"
+
+ localVarHeaderParams := make(map[string]string)
+ localVarQueryParams := url.Values{}
+ localVarFormParams := make(map[string]string)
+ var localVarPostBody interface{}
+ var localVarFileName string
+ var localVarFileBytes []byte
+ // add default headers if any
+ for key := range a.Configuration.DefaultHeader {
+ localVarHeaderParams[key] = a.Configuration.DefaultHeader[key]
+ }
+ localVarQueryParams.Add("offset", a.Configuration.APIClient.ParameterToString(offset, ""))
+ localVarQueryParams.Add("limit", a.Configuration.APIClient.ParameterToString(limit, ""))
+
+ // to determine the Content-Type header
+ localVarHttpContentTypes := []string{"application/json"}
+
+ // set Content-Type header
+ localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes)
+ if localVarHttpContentType != "" {
+ localVarHeaderParams["Content-Type"] = localVarHttpContentType
+ }
+ // to determine the Accept header
+ localVarHttpHeaderAccepts := []string{
+ "application/json",
+ }
+
+ // set Accept header
+ localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts)
+ if localVarHttpHeaderAccept != "" {
+ localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+ }
+ var successPayload = new([]Policy)
+ localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+
+ var localVarURL, _ = url.Parse(localVarPath)
+ localVarURL.RawQuery = localVarQueryParams.Encode()
+ var localVarAPIResponse = &APIResponse{Operation: "ListPolicies", Method: localVarHttpMethod, RequestURL: localVarURL.String()}
+ if localVarHttpResponse != nil {
+ localVarAPIResponse.Response = localVarHttpResponse.RawResponse
+ localVarAPIResponse.Payload = localVarHttpResponse.Body()
+ }
+
+ if err != nil {
+ return *successPayload, localVarAPIResponse, err
+ }
+ err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload)
+ return *successPayload, localVarAPIResponse, err
+}
+
+/**
+ *
+ * Update an Access Control Policy
+ *
+ * @param id The id of the policy.
+ * @param body
+ * @return *Policy
+ */
+func (a PolicyApi) UpdatePolicy(id string, body Policy) (*Policy, *APIResponse, error) {
+
+ var localVarHttpMethod = strings.ToUpper("Put")
+ // create path and map variables
+ localVarPath := a.Configuration.BasePath + "/policies/{id}"
+ localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1)
+
+ localVarHeaderParams := make(map[string]string)
+ localVarQueryParams := url.Values{}
+ localVarFormParams := make(map[string]string)
+ var localVarPostBody interface{}
+ var localVarFileName string
+ var localVarFileBytes []byte
+ // add default headers if any
+ for key := range a.Configuration.DefaultHeader {
+ localVarHeaderParams[key] = a.Configuration.DefaultHeader[key]
+ }
+
+ // to determine the Content-Type header
+ localVarHttpContentTypes := []string{"application/json"}
+
+ // set Content-Type header
+ localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes)
+ if localVarHttpContentType != "" {
+ localVarHeaderParams["Content-Type"] = localVarHttpContentType
+ }
+ // to determine the Accept header
+ localVarHttpHeaderAccepts := []string{
+ "application/json",
+ }
+
+ // set Accept header
+ localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts)
+ if localVarHttpHeaderAccept != "" {
+ localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+ }
+ // body params
+ localVarPostBody = &body
+ var successPayload = new(Policy)
+ localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+
+ var localVarURL, _ = url.Parse(localVarPath)
+ localVarURL.RawQuery = localVarQueryParams.Encode()
+ var localVarAPIResponse = &APIResponse{Operation: "UpdatePolicy", Method: localVarHttpMethod, RequestURL: localVarURL.String()}
+ if localVarHttpResponse != nil {
+ localVarAPIResponse.Response = localVarHttpResponse.RawResponse
+ localVarAPIResponse.Payload = localVarHttpResponse.Body()
+ }
+
+ if err != nil {
+ return successPayload, localVarAPIResponse, err
+ }
+ err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload)
+ return successPayload, localVarAPIResponse, err
+}
diff --git a/sdk/go/keto/swagger/policy_conditions.go b/sdk/go/keto/swagger/policy_conditions.go
new file mode 100644
index 000000000..95b22c0c4
--- /dev/null
+++ b/sdk/go/keto/swagger/policy_conditions.go
@@ -0,0 +1,15 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type PolicyConditions struct {
+ Options map[string]interface{} `json:"options,omitempty"`
+
+ Type_ string `json:"type,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/role.go b/sdk/go/keto/swagger/role.go
new file mode 100644
index 000000000..d6499e555
--- /dev/null
+++ b/sdk/go/keto/swagger/role.go
@@ -0,0 +1,19 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+// Role represents a group of users that share the same role. A role could be an administrator, a moderator, a regular user or some other sort of role.
+type Role struct {
+
+ // ID is the role's unique id.
+ Id string `json:"id,omitempty"`
+
+ // Members is who belongs to the role.
+ Members []string `json:"members,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/role_api.go b/sdk/go/keto/swagger/role_api.go
new file mode 100644
index 000000000..e34960457
--- /dev/null
+++ b/sdk/go/keto/swagger/role_api.go
@@ -0,0 +1,407 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/url"
+ "strings"
+)
+
+type RoleApi struct {
+ Configuration *Configuration
+}
+
+func NewRoleApi() *RoleApi {
+ configuration := NewConfiguration()
+ return &RoleApi{
+ Configuration: configuration,
+ }
+}
+
+func NewRoleApiWithBasePath(basePath string) *RoleApi {
+ configuration := NewConfiguration()
+ configuration.BasePath = basePath
+
+ return &RoleApi{
+ Configuration: configuration,
+ }
+}
+
+/**
+ * Add members to a role
+ * A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to add members (users, applications, ...) to a specific role. You have to know the role's ID.
+ *
+ * @param id The id of the role to modify.
+ * @param body
+ * @return void
+ */
+func (a RoleApi) AddMembersToRole(id string, body RoleMembers) (*APIResponse, error) {
+
+ var localVarHttpMethod = strings.ToUpper("Post")
+ // create path and map variables
+ localVarPath := a.Configuration.BasePath + "/roles/{id}/members"
+ localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1)
+
+ localVarHeaderParams := make(map[string]string)
+ localVarQueryParams := url.Values{}
+ localVarFormParams := make(map[string]string)
+ var localVarPostBody interface{}
+ var localVarFileName string
+ var localVarFileBytes []byte
+ // add default headers if any
+ for key := range a.Configuration.DefaultHeader {
+ localVarHeaderParams[key] = a.Configuration.DefaultHeader[key]
+ }
+
+ // to determine the Content-Type header
+ localVarHttpContentTypes := []string{"application/json"}
+
+ // set Content-Type header
+ localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes)
+ if localVarHttpContentType != "" {
+ localVarHeaderParams["Content-Type"] = localVarHttpContentType
+ }
+ // to determine the Accept header
+ localVarHttpHeaderAccepts := []string{
+ "application/json",
+ }
+
+ // set Accept header
+ localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts)
+ if localVarHttpHeaderAccept != "" {
+ localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+ }
+ // body params
+ localVarPostBody = &body
+ localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+
+ var localVarURL, _ = url.Parse(localVarPath)
+ localVarURL.RawQuery = localVarQueryParams.Encode()
+ var localVarAPIResponse = &APIResponse{Operation: "AddMembersToRole", Method: localVarHttpMethod, RequestURL: localVarURL.String()}
+ if localVarHttpResponse != nil {
+ localVarAPIResponse.Response = localVarHttpResponse.RawResponse
+ localVarAPIResponse.Payload = localVarHttpResponse.Body()
+ }
+
+ if err != nil {
+ return localVarAPIResponse, err
+ }
+ return localVarAPIResponse, err
+}
+
+/**
+ * Create a role
+ * A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to create a new role. You may define members as well but you don't have to.
+ *
+ * @param body
+ * @return *Role
+ */
+func (a RoleApi) CreateRole(body Role) (*Role, *APIResponse, error) {
+
+ var localVarHttpMethod = strings.ToUpper("Post")
+ // create path and map variables
+ localVarPath := a.Configuration.BasePath + "/roles"
+
+ localVarHeaderParams := make(map[string]string)
+ localVarQueryParams := url.Values{}
+ localVarFormParams := make(map[string]string)
+ var localVarPostBody interface{}
+ var localVarFileName string
+ var localVarFileBytes []byte
+ // add default headers if any
+ for key := range a.Configuration.DefaultHeader {
+ localVarHeaderParams[key] = a.Configuration.DefaultHeader[key]
+ }
+
+ // to determine the Content-Type header
+ localVarHttpContentTypes := []string{"application/json"}
+
+ // set Content-Type header
+ localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes)
+ if localVarHttpContentType != "" {
+ localVarHeaderParams["Content-Type"] = localVarHttpContentType
+ }
+ // to determine the Accept header
+ localVarHttpHeaderAccepts := []string{
+ "application/json",
+ }
+
+ // set Accept header
+ localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts)
+ if localVarHttpHeaderAccept != "" {
+ localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+ }
+ // body params
+ localVarPostBody = &body
+ var successPayload = new(Role)
+ localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+
+ var localVarURL, _ = url.Parse(localVarPath)
+ localVarURL.RawQuery = localVarQueryParams.Encode()
+ var localVarAPIResponse = &APIResponse{Operation: "CreateRole", Method: localVarHttpMethod, RequestURL: localVarURL.String()}
+ if localVarHttpResponse != nil {
+ localVarAPIResponse.Response = localVarHttpResponse.RawResponse
+ localVarAPIResponse.Payload = localVarHttpResponse.Body()
+ }
+
+ if err != nil {
+ return successPayload, localVarAPIResponse, err
+ }
+ err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload)
+ return successPayload, localVarAPIResponse, err
+}
+
+/**
+ * Get a role by its ID
+ * A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to delete an existing role. You have to know the role's ID.
+ *
+ * @param id The id of the role to look up.
+ * @return void
+ */
+func (a RoleApi) DeleteRole(id string) (*APIResponse, error) {
+
+ var localVarHttpMethod = strings.ToUpper("Delete")
+ // create path and map variables
+ localVarPath := a.Configuration.BasePath + "/roles/{id}"
+ localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1)
+
+ localVarHeaderParams := make(map[string]string)
+ localVarQueryParams := url.Values{}
+ localVarFormParams := make(map[string]string)
+ var localVarPostBody interface{}
+ var localVarFileName string
+ var localVarFileBytes []byte
+ // add default headers if any
+ for key := range a.Configuration.DefaultHeader {
+ localVarHeaderParams[key] = a.Configuration.DefaultHeader[key]
+ }
+
+ // to determine the Content-Type header
+ localVarHttpContentTypes := []string{"application/json"}
+
+ // set Content-Type header
+ localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes)
+ if localVarHttpContentType != "" {
+ localVarHeaderParams["Content-Type"] = localVarHttpContentType
+ }
+ // to determine the Accept header
+ localVarHttpHeaderAccepts := []string{
+ "application/json",
+ }
+
+ // set Accept header
+ localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts)
+ if localVarHttpHeaderAccept != "" {
+ localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+ }
+ localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+
+ var localVarURL, _ = url.Parse(localVarPath)
+ localVarURL.RawQuery = localVarQueryParams.Encode()
+ var localVarAPIResponse = &APIResponse{Operation: "DeleteRole", Method: localVarHttpMethod, RequestURL: localVarURL.String()}
+ if localVarHttpResponse != nil {
+ localVarAPIResponse.Response = localVarHttpResponse.RawResponse
+ localVarAPIResponse.Payload = localVarHttpResponse.Body()
+ }
+
+ if err != nil {
+ return localVarAPIResponse, err
+ }
+ return localVarAPIResponse, err
+}
+
+/**
+ * Get a role by its ID
+ * A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to retrieve an existing role. You have to know the role's ID.
+ *
+ * @param id The id of the role to look up.
+ * @return *Role
+ */
+func (a RoleApi) GetRole(id string) (*Role, *APIResponse, error) {
+
+ var localVarHttpMethod = strings.ToUpper("Get")
+ // create path and map variables
+ localVarPath := a.Configuration.BasePath + "/roles/{id}"
+ localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1)
+
+ localVarHeaderParams := make(map[string]string)
+ localVarQueryParams := url.Values{}
+ localVarFormParams := make(map[string]string)
+ var localVarPostBody interface{}
+ var localVarFileName string
+ var localVarFileBytes []byte
+ // add default headers if any
+ for key := range a.Configuration.DefaultHeader {
+ localVarHeaderParams[key] = a.Configuration.DefaultHeader[key]
+ }
+
+ // to determine the Content-Type header
+ localVarHttpContentTypes := []string{"application/json"}
+
+ // set Content-Type header
+ localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes)
+ if localVarHttpContentType != "" {
+ localVarHeaderParams["Content-Type"] = localVarHttpContentType
+ }
+ // to determine the Accept header
+ localVarHttpHeaderAccepts := []string{
+ "application/json",
+ }
+
+ // set Accept header
+ localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts)
+ if localVarHttpHeaderAccept != "" {
+ localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+ }
+ var successPayload = new(Role)
+ localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+
+ var localVarURL, _ = url.Parse(localVarPath)
+ localVarURL.RawQuery = localVarQueryParams.Encode()
+ var localVarAPIResponse = &APIResponse{Operation: "GetRole", Method: localVarHttpMethod, RequestURL: localVarURL.String()}
+ if localVarHttpResponse != nil {
+ localVarAPIResponse.Response = localVarHttpResponse.RawResponse
+ localVarAPIResponse.Payload = localVarHttpResponse.Body()
+ }
+
+ if err != nil {
+ return successPayload, localVarAPIResponse, err
+ }
+ err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload)
+ return successPayload, localVarAPIResponse, err
+}
+
+/**
+ * List all roles
+ * A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to retrieve all roles that are stored in the system.
+ *
+ * @param member The id of the member to look up.
+ * @param limit The maximum amount of policies returned.
+ * @param offset The offset from where to start looking.
+ * @return []Role
+ */
+func (a RoleApi) ListRoles(member string, limit int64, offset int64) ([]Role, *APIResponse, error) {
+
+ var localVarHttpMethod = strings.ToUpper("Get")
+ // create path and map variables
+ localVarPath := a.Configuration.BasePath + "/roles"
+
+ localVarHeaderParams := make(map[string]string)
+ localVarQueryParams := url.Values{}
+ localVarFormParams := make(map[string]string)
+ var localVarPostBody interface{}
+ var localVarFileName string
+ var localVarFileBytes []byte
+ // add default headers if any
+ for key := range a.Configuration.DefaultHeader {
+ localVarHeaderParams[key] = a.Configuration.DefaultHeader[key]
+ }
+ localVarQueryParams.Add("member", a.Configuration.APIClient.ParameterToString(member, ""))
+ localVarQueryParams.Add("limit", a.Configuration.APIClient.ParameterToString(limit, ""))
+ localVarQueryParams.Add("offset", a.Configuration.APIClient.ParameterToString(offset, ""))
+
+ // to determine the Content-Type header
+ localVarHttpContentTypes := []string{"application/json"}
+
+ // set Content-Type header
+ localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes)
+ if localVarHttpContentType != "" {
+ localVarHeaderParams["Content-Type"] = localVarHttpContentType
+ }
+ // to determine the Accept header
+ localVarHttpHeaderAccepts := []string{
+ "application/json",
+ }
+
+ // set Accept header
+ localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts)
+ if localVarHttpHeaderAccept != "" {
+ localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+ }
+ var successPayload = new([]Role)
+ localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+
+ var localVarURL, _ = url.Parse(localVarPath)
+ localVarURL.RawQuery = localVarQueryParams.Encode()
+ var localVarAPIResponse = &APIResponse{Operation: "ListRoles", Method: localVarHttpMethod, RequestURL: localVarURL.String()}
+ if localVarHttpResponse != nil {
+ localVarAPIResponse.Response = localVarHttpResponse.RawResponse
+ localVarAPIResponse.Payload = localVarHttpResponse.Body()
+ }
+
+ if err != nil {
+ return *successPayload, localVarAPIResponse, err
+ }
+ err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload)
+ return *successPayload, localVarAPIResponse, err
+}
+
+/**
+ * Remove members from a role
+ * A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to remove members (users, applications, ...) from a specific role. You have to know the role's ID.
+ *
+ * @param id The id of the role to modify.
+ * @param body
+ * @return void
+ */
+func (a RoleApi) RemoveMembersFromRole(id string, body RoleMembers) (*APIResponse, error) {
+
+ var localVarHttpMethod = strings.ToUpper("Delete")
+ // create path and map variables
+ localVarPath := a.Configuration.BasePath + "/roles/{id}/members"
+ localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1)
+
+ localVarHeaderParams := make(map[string]string)
+ localVarQueryParams := url.Values{}
+ localVarFormParams := make(map[string]string)
+ var localVarPostBody interface{}
+ var localVarFileName string
+ var localVarFileBytes []byte
+ // add default headers if any
+ for key := range a.Configuration.DefaultHeader {
+ localVarHeaderParams[key] = a.Configuration.DefaultHeader[key]
+ }
+
+ // to determine the Content-Type header
+ localVarHttpContentTypes := []string{"application/json"}
+
+ // set Content-Type header
+ localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes)
+ if localVarHttpContentType != "" {
+ localVarHeaderParams["Content-Type"] = localVarHttpContentType
+ }
+ // to determine the Accept header
+ localVarHttpHeaderAccepts := []string{
+ "application/json",
+ }
+
+ // set Accept header
+ localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts)
+ if localVarHttpHeaderAccept != "" {
+ localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+ }
+ // body params
+ localVarPostBody = &body
+ localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+
+ var localVarURL, _ = url.Parse(localVarPath)
+ localVarURL.RawQuery = localVarQueryParams.Encode()
+ var localVarAPIResponse = &APIResponse{Operation: "RemoveMembersFromRole", Method: localVarHttpMethod, RequestURL: localVarURL.String()}
+ if localVarHttpResponse != nil {
+ localVarAPIResponse.Response = localVarHttpResponse.RawResponse
+ localVarAPIResponse.Payload = localVarHttpResponse.Body()
+ }
+
+ if err != nil {
+ return localVarAPIResponse, err
+ }
+ return localVarAPIResponse, err
+}
diff --git a/sdk/go/keto/swagger/role_members.go b/sdk/go/keto/swagger/role_members.go
new file mode 100644
index 000000000..166f6c7cc
--- /dev/null
+++ b/sdk/go/keto/swagger/role_members.go
@@ -0,0 +1,13 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type RoleMembers struct {
+ Members []string `json:"members,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/session.go b/sdk/go/keto/swagger/session.go
new file mode 100644
index 000000000..555a5897e
--- /dev/null
+++ b/sdk/go/keto/swagger/session.go
@@ -0,0 +1,13 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type Session struct {
+ GetSubject string `json:"GetSubject,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/swagger_create_policy_parameters.go b/sdk/go/keto/swagger/swagger_create_policy_parameters.go
new file mode 100644
index 000000000..2e041d642
--- /dev/null
+++ b/sdk/go/keto/swagger/swagger_create_policy_parameters.go
@@ -0,0 +1,13 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type SwaggerCreatePolicyParameters struct {
+ Body Policy `json:"Body,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/swagger_does_warden_allow_access_request_parameters.go b/sdk/go/keto/swagger/swagger_does_warden_allow_access_request_parameters.go
new file mode 100644
index 000000000..8486b2872
--- /dev/null
+++ b/sdk/go/keto/swagger/swagger_does_warden_allow_access_request_parameters.go
@@ -0,0 +1,13 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type SwaggerDoesWardenAllowAccessRequestParameters struct {
+ Body WardenSubjectAuthorizationRequest `json:"Body,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/swagger_does_warden_allow_token_access_rqeuest_parameters.go b/sdk/go/keto/swagger/swagger_does_warden_allow_token_access_rqeuest_parameters.go
new file mode 100644
index 000000000..b9402dfee
--- /dev/null
+++ b/sdk/go/keto/swagger/swagger_does_warden_allow_token_access_rqeuest_parameters.go
@@ -0,0 +1,13 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type SwaggerDoesWardenAllowTokenAccessRqeuestParameters struct {
+ Body WardenOAuth2AuthorizationRequest `json:"Body,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/swagger_get_policy_parameters.go b/sdk/go/keto/swagger/swagger_get_policy_parameters.go
new file mode 100644
index 000000000..f4dbc2e6e
--- /dev/null
+++ b/sdk/go/keto/swagger/swagger_get_policy_parameters.go
@@ -0,0 +1,15 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type SwaggerGetPolicyParameters struct {
+
+ // The id of the policy. in: path
+ Id string `json:"id,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/swagger_list_policy_parameters.go b/sdk/go/keto/swagger/swagger_list_policy_parameters.go
new file mode 100644
index 000000000..2f5c98b77
--- /dev/null
+++ b/sdk/go/keto/swagger/swagger_list_policy_parameters.go
@@ -0,0 +1,18 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type SwaggerListPolicyParameters struct {
+
+ // The maximum amount of policies returned. in: query
+ Limit int64 `json:"limit,omitempty"`
+
+ // The offset from where to start looking. in: query
+ Offset int64 `json:"offset,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/swagger_list_policy_response.go b/sdk/go/keto/swagger/swagger_list_policy_response.go
new file mode 100644
index 000000000..883cb42f7
--- /dev/null
+++ b/sdk/go/keto/swagger/swagger_list_policy_response.go
@@ -0,0 +1,16 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+// A policy
+type SwaggerListPolicyResponse struct {
+
+ // in: body type: array
+ Body []Policy `json:"Body,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/swagger_update_policy_parameters.go b/sdk/go/keto/swagger/swagger_update_policy_parameters.go
new file mode 100644
index 000000000..dce4ea6ec
--- /dev/null
+++ b/sdk/go/keto/swagger/swagger_update_policy_parameters.go
@@ -0,0 +1,16 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type SwaggerUpdatePolicyParameters struct {
+ Body Policy `json:"Body,omitempty"`
+
+ // The id of the policy. in: path
+ Id string `json:"id,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/swagger_warden_base_request.go b/sdk/go/keto/swagger/swagger_warden_base_request.go
new file mode 100644
index 000000000..2ba953473
--- /dev/null
+++ b/sdk/go/keto/swagger/swagger_warden_base_request.go
@@ -0,0 +1,22 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+// swager:model authorizedBaseRequest
+type SwaggerWardenBaseRequest struct {
+
+ // Action is the action that is requested on the resource.
+ Action string `json:"action,omitempty"`
+
+ // Context is the request's environmental context.
+ Context map[string]interface{} `json:"context,omitempty"`
+
+ // Resource is the resource that access is requested to.
+ Resource string `json:"resource,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/warden_api.go b/sdk/go/keto/swagger/warden_api.go
new file mode 100644
index 000000000..9c96659de
--- /dev/null
+++ b/sdk/go/keto/swagger/warden_api.go
@@ -0,0 +1,154 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+import (
+ "net/url"
+ "strings"
+)
+
+type WardenApi struct {
+ Configuration *Configuration
+}
+
+func NewWardenApi() *WardenApi {
+ configuration := NewConfiguration()
+ return &WardenApi{
+ Configuration: configuration,
+ }
+}
+
+func NewWardenApiWithBasePath(basePath string) *WardenApi {
+ configuration := NewConfiguration()
+ configuration.BasePath = basePath
+
+ return &WardenApi{
+ Configuration: configuration,
+ }
+}
+
+/**
+ * Check if an OAuth 2.0 access token is authorized to access a resource
+ * Checks if a token is valid and if the token subject is allowed to perform an action on a resource. This endpoint requires a token, a scope, a resource name, an action name and a context. If a token is expired/invalid, has not been granted the requested scope or the subject is not allowed to perform the action on the resource, this endpoint returns a 200 response with `{ \"allowed\": false }`. This endpoint passes all data from the upstream OAuth 2.0 token introspection endpoint. If you use ORY Hydra as an upstream OAuth 2.0 provider, data set through the `accessTokenExtra` field in the consent flow will be included in this response as well.
+ *
+ * @param body
+ * @return void
+ */
+func (a WardenApi) IsOAuth2AccessTokenAuthorized(body WardenOAuth2AuthorizationRequest) (*APIResponse, error) {
+
+ var localVarHttpMethod = strings.ToUpper("Post")
+ // create path and map variables
+ localVarPath := a.Configuration.BasePath + "/warden/oauth2/authorize"
+
+ localVarHeaderParams := make(map[string]string)
+ localVarQueryParams := url.Values{}
+ localVarFormParams := make(map[string]string)
+ var localVarPostBody interface{}
+ var localVarFileName string
+ var localVarFileBytes []byte
+ // add default headers if any
+ for key := range a.Configuration.DefaultHeader {
+ localVarHeaderParams[key] = a.Configuration.DefaultHeader[key]
+ }
+
+ // to determine the Content-Type header
+ localVarHttpContentTypes := []string{"application/json"}
+
+ // set Content-Type header
+ localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes)
+ if localVarHttpContentType != "" {
+ localVarHeaderParams["Content-Type"] = localVarHttpContentType
+ }
+ // to determine the Accept header
+ localVarHttpHeaderAccepts := []string{
+ "application/json",
+ }
+
+ // set Accept header
+ localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts)
+ if localVarHttpHeaderAccept != "" {
+ localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+ }
+ // body params
+ localVarPostBody = &body
+ localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+
+ var localVarURL, _ = url.Parse(localVarPath)
+ localVarURL.RawQuery = localVarQueryParams.Encode()
+ var localVarAPIResponse = &APIResponse{Operation: "IsOAuth2AccessTokenAuthorized", Method: localVarHttpMethod, RequestURL: localVarURL.String()}
+ if localVarHttpResponse != nil {
+ localVarAPIResponse.Response = localVarHttpResponse.RawResponse
+ localVarAPIResponse.Payload = localVarHttpResponse.Body()
+ }
+
+ if err != nil {
+ return localVarAPIResponse, err
+ }
+ return localVarAPIResponse, err
+}
+
+/**
+ * Check if a subject is authorized to access a resource
+ * Checks if a subject (e.g. user ID, API key, ...) is allowed to perform a certain action on a resource.
+ *
+ * @param body
+ * @return void
+ */
+func (a WardenApi) IsSubjectAuthorized(body WardenSubjectAuthorizationRequest) (*APIResponse, error) {
+
+ var localVarHttpMethod = strings.ToUpper("Post")
+ // create path and map variables
+ localVarPath := a.Configuration.BasePath + "/warden/subjects/authorize"
+
+ localVarHeaderParams := make(map[string]string)
+ localVarQueryParams := url.Values{}
+ localVarFormParams := make(map[string]string)
+ var localVarPostBody interface{}
+ var localVarFileName string
+ var localVarFileBytes []byte
+ // add default headers if any
+ for key := range a.Configuration.DefaultHeader {
+ localVarHeaderParams[key] = a.Configuration.DefaultHeader[key]
+ }
+
+ // to determine the Content-Type header
+ localVarHttpContentTypes := []string{"application/json"}
+
+ // set Content-Type header
+ localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes)
+ if localVarHttpContentType != "" {
+ localVarHeaderParams["Content-Type"] = localVarHttpContentType
+ }
+ // to determine the Accept header
+ localVarHttpHeaderAccepts := []string{
+ "application/json",
+ }
+
+ // set Accept header
+ localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts)
+ if localVarHttpHeaderAccept != "" {
+ localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+ }
+ // body params
+ localVarPostBody = &body
+ localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+
+ var localVarURL, _ = url.Parse(localVarPath)
+ localVarURL.RawQuery = localVarQueryParams.Encode()
+ var localVarAPIResponse = &APIResponse{Operation: "IsSubjectAuthorized", Method: localVarHttpMethod, RequestURL: localVarURL.String()}
+ if localVarHttpResponse != nil {
+ localVarAPIResponse.Response = localVarHttpResponse.RawResponse
+ localVarAPIResponse.Payload = localVarHttpResponse.Body()
+ }
+
+ if err != nil {
+ return localVarAPIResponse, err
+ }
+ return localVarAPIResponse, err
+}
diff --git a/sdk/go/keto/swagger/warden_o_auth2_authorization_request.go b/sdk/go/keto/swagger/warden_o_auth2_authorization_request.go
new file mode 100644
index 000000000..16243d3a3
--- /dev/null
+++ b/sdk/go/keto/swagger/warden_o_auth2_authorization_request.go
@@ -0,0 +1,27 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type WardenOAuth2AuthorizationRequest struct {
+
+ // Action is the action that is requested on the resource.
+ Action string `json:"action,omitempty"`
+
+ // Context is the request's environmental context.
+ Context map[string]interface{} `json:"context,omitempty"`
+
+ // Resource is the resource that access is requested to.
+ Resource string `json:"resource,omitempty"`
+
+ // Scopes is an array of scopes that are required.
+ Scopes []string `json:"scopes,omitempty"`
+
+ // Token is the token to introspect.
+ Token string `json:"token,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/warden_o_auth2_authorization_response.go b/sdk/go/keto/swagger/warden_o_auth2_authorization_response.go
new file mode 100644
index 000000000..0d1fd52be
--- /dev/null
+++ b/sdk/go/keto/swagger/warden_o_auth2_authorization_response.go
@@ -0,0 +1,46 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+import (
+ "time"
+)
+
+type WardenOAuth2AuthorizationResponse struct {
+
+ // Extra represents arbitrary session data.
+ AccessTokenExtra map[string]interface{} `json:"accessTokenExtra,omitempty"`
+
+ // Allowed is true if the request is allowed and false otherwise.
+ Allowed bool `json:"allowed,omitempty"`
+
+ Audience string `json:"audience,omitempty"`
+
+ // ClientID is the id of the OAuth2 client that requested the token.
+ ClientId string `json:"clientId,omitempty"`
+
+ // ExpiresAt is the expiry timestamp.
+ ExpiresAt time.Time `json:"expiresAt,omitempty"`
+
+ // GrantedScopes is a list of scopes that the subject authorized when asked for consent.
+ GrantedScopes []string `json:"grantedScopes,omitempty"`
+
+ // IssuedAt is the token creation time stamp.
+ IssuedAt time.Time `json:"issuedAt,omitempty"`
+
+ // Issuer is the id of the issuer, typically an hydra instance.
+ Issuer string `json:"issuer,omitempty"`
+
+ NotBefore time.Time `json:"notBefore,omitempty"`
+
+ // Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.
+ Subject string `json:"subject,omitempty"`
+
+ Username string `json:"username,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/warden_subject_authorization_request.go b/sdk/go/keto/swagger/warden_subject_authorization_request.go
new file mode 100644
index 000000000..5fa024caf
--- /dev/null
+++ b/sdk/go/keto/swagger/warden_subject_authorization_request.go
@@ -0,0 +1,24 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type WardenSubjectAuthorizationRequest struct {
+
+ // Action is the action that is requested on the resource.
+ Action string `json:"action,omitempty"`
+
+ // Context is the request's environmental context.
+ Context map[string]interface{} `json:"context,omitempty"`
+
+ // Resource is the resource that access is requested to.
+ Resource string `json:"resource,omitempty"`
+
+ // Subejct is the subject that is requesting access.
+ Subject string `json:"subject,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/warden_subject_authorization_response.go b/sdk/go/keto/swagger/warden_subject_authorization_response.go
new file mode 100644
index 000000000..9183e624a
--- /dev/null
+++ b/sdk/go/keto/swagger/warden_subject_authorization_response.go
@@ -0,0 +1,18 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+type WardenSubjectAuthorizationResponse struct {
+
+ // Allowed is true if the request is allowed and false otherwise.
+ Allowed bool `json:"allowed,omitempty"`
+
+ // Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.
+ Subject string `json:"subject,omitempty"`
+}
diff --git a/sdk/go/keto/swagger/writer.go b/sdk/go/keto/swagger/writer.go
new file mode 100644
index 000000000..53a7e4b33
--- /dev/null
+++ b/sdk/go/keto/swagger/writer.go
@@ -0,0 +1,13 @@
+/*
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+
+package swagger
+
+// Writer is a helper to write arbitrary data to a ResponseWriter
+type Writer struct {
+}
diff --git a/sdk/js/swagger/.swagger-codegen-ignore b/sdk/js/swagger/.swagger-codegen-ignore
new file mode 100644
index 000000000..c5fa491b4
--- /dev/null
+++ b/sdk/js/swagger/.swagger-codegen-ignore
@@ -0,0 +1,23 @@
+# Swagger Codegen Ignore
+# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen
+
+# Use this file to prevent files from being overwritten by the generator.
+# The patterns follow closely to .gitignore or .dockerignore.
+
+# As an example, the C# client generator defines ApiClient.cs.
+# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line:
+#ApiClient.cs
+
+# You can match any string of characters against a directory, file or extension with a single asterisk (*):
+#foo/*/qux
+# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
+
+# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
+#foo/**/qux
+# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
+
+# You can also negate patterns with an exclamation (!).
+# For example, you can ignore all files in a docs folder with the file extension .md:
+#docs/*.md
+# Then explicitly reverse the ignore rule for a single file:
+#!docs/README.md
diff --git a/sdk/js/swagger/.swagger-codegen/VERSION b/sdk/js/swagger/.swagger-codegen/VERSION
new file mode 100644
index 000000000..6b4d15773
--- /dev/null
+++ b/sdk/js/swagger/.swagger-codegen/VERSION
@@ -0,0 +1 @@
+2.2.3
\ No newline at end of file
diff --git a/sdk/js/swagger/.travis.yml b/sdk/js/swagger/.travis.yml
new file mode 100644
index 000000000..e49f4692f
--- /dev/null
+++ b/sdk/js/swagger/.travis.yml
@@ -0,0 +1,7 @@
+language: node_js
+node_js:
+ - "6"
+ - "6.1"
+ - "5"
+ - "5.11"
+
diff --git a/sdk/js/swagger/README.md b/sdk/js/swagger/README.md
new file mode 100644
index 000000000..5b5cf33a7
--- /dev/null
+++ b/sdk/js/swagger/README.md
@@ -0,0 +1,172 @@
+# swagger-js-client
+
+SwaggerJsClient - JavaScript client for swagger-js-client
+Package main ORY Keto
+This SDK is automatically generated by the [Swagger Codegen](https://github.com/swagger-api/swagger-codegen) project:
+
+- API version: Latest
+- Package version: Latest
+- Build package: io.swagger.codegen.languages.JavascriptClientCodegen
+For more information, please visit [https://www.ory.sh](https://www.ory.sh)
+
+## Installation
+
+### For [Node.js](https://nodejs.org/)
+
+#### npm
+
+To publish the library as a [npm](https://www.npmjs.com/),
+please follow the procedure in ["Publishing npm packages"](https://docs.npmjs.com/getting-started/publishing-npm-packages).
+
+Then install it via:
+
+```shell
+npm install swagger-js-client --save
+```
+
+##### Local development
+
+To use the library locally without publishing to a remote npm registry, first install the dependencies by changing
+into the directory containing `package.json` (and this README). Let's call this `JAVASCRIPT_CLIENT_DIR`. Then run:
+
+```shell
+npm install
+```
+
+Next, [link](https://docs.npmjs.com/cli/link) it globally in npm with the following, also from `JAVASCRIPT_CLIENT_DIR`:
+
+```shell
+npm link
+```
+
+Finally, switch to the directory you want to use your swagger-js-client from, and run:
+
+```shell
+npm link /path/to/
+```
+
+You should now be able to `require('swagger-js-client')` in javascript files from the directory you ran the last
+command above from.
+
+#### git
+#
+If the library is hosted at a git repository, e.g.
+https://github.com/GIT_USER_ID/GIT_REPO_ID
+then install it via:
+
+```shell
+ npm install GIT_USER_ID/GIT_REPO_ID --save
+```
+
+### For browser
+
+The library also works in the browser environment via npm and [browserify](http://browserify.org/). After following
+the above steps with Node.js and installing browserify with `npm install -g browserify`,
+perform the following (assuming *main.js* is your entry file, that's to say your javascript file where you actually
+use this library):
+
+```shell
+browserify main.js > bundle.js
+```
+
+Then include *bundle.js* in the HTML pages.
+
+### Webpack Configuration
+
+Using Webpack you may encounter the following error: "Module not found: Error:
+Cannot resolve module", most certainly you should disable AMD loader. Add/merge
+the following section to your webpack config:
+
+```javascript
+module: {
+ rules: [
+ {
+ parser: {
+ amd: false
+ }
+ }
+ ]
+}
+```
+
+## Getting Started
+
+Please follow the [installation](#installation) instruction and execute the following JS code:
+
+```javascript
+var SwaggerJsClient = require('swagger-js-client');
+
+var api = new SwaggerJsClient.PolicyApi()
+
+var opts = {
+ 'body': new SwaggerJsClient.Policy() // {Policy}
+};
+
+var callback = function(error, data, response) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log('API called successfully. Returned data: ' + data);
+ }
+};
+api.createPolicy(opts, callback);
+
+```
+
+## Documentation for API Endpoints
+
+All URIs are relative to *http://localhost*
+
+Class | Method | HTTP request | Description
+------------ | ------------- | ------------- | -------------
+*SwaggerJsClient.PolicyApi* | [**createPolicy**](docs/PolicyApi.md#createPolicy) | **POST** /policies |
+*SwaggerJsClient.PolicyApi* | [**deletePolicy**](docs/PolicyApi.md#deletePolicy) | **DELETE** /policies/{id} |
+*SwaggerJsClient.PolicyApi* | [**getPolicy**](docs/PolicyApi.md#getPolicy) | **GET** /policies/{id} |
+*SwaggerJsClient.PolicyApi* | [**listPolicies**](docs/PolicyApi.md#listPolicies) | **GET** /policies |
+*SwaggerJsClient.PolicyApi* | [**updatePolicy**](docs/PolicyApi.md#updatePolicy) | **PUT** /policies/{id} |
+*SwaggerJsClient.RoleApi* | [**addMembersToRole**](docs/RoleApi.md#addMembersToRole) | **POST** /roles/{id}/members | Add members to a role
+*SwaggerJsClient.RoleApi* | [**createRole**](docs/RoleApi.md#createRole) | **POST** /roles | Create a role
+*SwaggerJsClient.RoleApi* | [**deleteRole**](docs/RoleApi.md#deleteRole) | **DELETE** /roles/{id} | Get a role by its ID
+*SwaggerJsClient.RoleApi* | [**getRole**](docs/RoleApi.md#getRole) | **GET** /roles/{id} | Get a role by its ID
+*SwaggerJsClient.RoleApi* | [**listRoles**](docs/RoleApi.md#listRoles) | **GET** /roles | List all roles
+*SwaggerJsClient.RoleApi* | [**removeMembersFromRole**](docs/RoleApi.md#removeMembersFromRole) | **DELETE** /roles/{id}/members | Remove members from a role
+*SwaggerJsClient.WardenApi* | [**isOAuth2AccessTokenAuthorized**](docs/WardenApi.md#isOAuth2AccessTokenAuthorized) | **POST** /warden/oauth2/authorize | Check if an OAuth 2.0 access token is authorized to access a resource
+*SwaggerJsClient.WardenApi* | [**isSubjectAuthorized**](docs/WardenApi.md#isSubjectAuthorized) | **POST** /warden/subjects/authorize | Check if a subject is authorized to access a resource
+
+
+## Documentation for Models
+
+ - [SwaggerJsClient.AuthenticationDefaultSession](docs/AuthenticationDefaultSession.md)
+ - [SwaggerJsClient.AuthenticationOAuth2IntrospectionRequest](docs/AuthenticationOAuth2IntrospectionRequest.md)
+ - [SwaggerJsClient.AuthenticationOAuth2Session](docs/AuthenticationOAuth2Session.md)
+ - [SwaggerJsClient.Authenticator](docs/Authenticator.md)
+ - [SwaggerJsClient.Firewall](docs/Firewall.md)
+ - [SwaggerJsClient.Handler](docs/Handler.md)
+ - [SwaggerJsClient.InlineResponse401](docs/InlineResponse401.md)
+ - [SwaggerJsClient.IntrospectionResponse](docs/IntrospectionResponse.md)
+ - [SwaggerJsClient.Manager](docs/Manager.md)
+ - [SwaggerJsClient.OAuth2IntrospectionAuthentication](docs/OAuth2IntrospectionAuthentication.md)
+ - [SwaggerJsClient.Policy](docs/Policy.md)
+ - [SwaggerJsClient.PolicyConditions](docs/PolicyConditions.md)
+ - [SwaggerJsClient.Role](docs/Role.md)
+ - [SwaggerJsClient.RoleMembers](docs/RoleMembers.md)
+ - [SwaggerJsClient.Session](docs/Session.md)
+ - [SwaggerJsClient.SwaggerCreatePolicyParameters](docs/SwaggerCreatePolicyParameters.md)
+ - [SwaggerJsClient.SwaggerDoesWardenAllowAccessRequestParameters](docs/SwaggerDoesWardenAllowAccessRequestParameters.md)
+ - [SwaggerJsClient.SwaggerDoesWardenAllowTokenAccessRqeuestParameters](docs/SwaggerDoesWardenAllowTokenAccessRqeuestParameters.md)
+ - [SwaggerJsClient.SwaggerGetPolicyParameters](docs/SwaggerGetPolicyParameters.md)
+ - [SwaggerJsClient.SwaggerListPolicyParameters](docs/SwaggerListPolicyParameters.md)
+ - [SwaggerJsClient.SwaggerListPolicyResponse](docs/SwaggerListPolicyResponse.md)
+ - [SwaggerJsClient.SwaggerUpdatePolicyParameters](docs/SwaggerUpdatePolicyParameters.md)
+ - [SwaggerJsClient.SwaggerWardenBaseRequest](docs/SwaggerWardenBaseRequest.md)
+ - [SwaggerJsClient.WardenOAuth2AuthorizationRequest](docs/WardenOAuth2AuthorizationRequest.md)
+ - [SwaggerJsClient.WardenOAuth2AuthorizationResponse](docs/WardenOAuth2AuthorizationResponse.md)
+ - [SwaggerJsClient.WardenSubjectAuthorizationRequest](docs/WardenSubjectAuthorizationRequest.md)
+ - [SwaggerJsClient.WardenSubjectAuthorizationResponse](docs/WardenSubjectAuthorizationResponse.md)
+ - [SwaggerJsClient.Writer](docs/Writer.md)
+
+
+## Documentation for Authorization
+
+ All endpoints do not require authorization.
+
diff --git a/sdk/js/swagger/docs/AuthenticationDefaultSession.md b/sdk/js/swagger/docs/AuthenticationDefaultSession.md
new file mode 100644
index 000000000..7076fb63f
--- /dev/null
+++ b/sdk/js/swagger/docs/AuthenticationDefaultSession.md
@@ -0,0 +1,9 @@
+# SwaggerJsClient.AuthenticationDefaultSession
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**allowed** | **Boolean** | Allowed is true if the request is allowed and false otherwise. | [optional]
+**subject** | **String** | Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too. | [optional]
+
+
diff --git a/sdk/js/swagger/docs/AuthenticationOAuth2IntrospectionRequest.md b/sdk/js/swagger/docs/AuthenticationOAuth2IntrospectionRequest.md
new file mode 100644
index 000000000..d604adb2e
--- /dev/null
+++ b/sdk/js/swagger/docs/AuthenticationOAuth2IntrospectionRequest.md
@@ -0,0 +1,9 @@
+# SwaggerJsClient.AuthenticationOAuth2IntrospectionRequest
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**scopes** | **[String]** | Scopes is an array of scopes that are required. | [optional]
+**token** | **String** | Token is the token to introspect. | [optional]
+
+
diff --git a/sdk/js/swagger/docs/AuthenticationOAuth2Session.md b/sdk/js/swagger/docs/AuthenticationOAuth2Session.md
new file mode 100644
index 000000000..2e7dd137a
--- /dev/null
+++ b/sdk/js/swagger/docs/AuthenticationOAuth2Session.md
@@ -0,0 +1,18 @@
+# SwaggerJsClient.AuthenticationOAuth2Session
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**accessTokenExtra** | **{String: Object}** | Extra represents arbitrary session data. | [optional]
+**allowed** | **Boolean** | Allowed is true if the request is allowed and false otherwise. | [optional]
+**audience** | **String** | | [optional]
+**clientId** | **String** | ClientID is the id of the OAuth2 client that requested the token. | [optional]
+**expiresAt** | **Date** | ExpiresAt is the expiry timestamp. | [optional]
+**grantedScopes** | **[String]** | GrantedScopes is a list of scopes that the subject authorized when asked for consent. | [optional]
+**issuedAt** | **Date** | IssuedAt is the token creation time stamp. | [optional]
+**issuer** | **String** | Issuer is the id of the issuer, typically an hydra instance. | [optional]
+**notBefore** | **Date** | | [optional]
+**subject** | **String** | Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too. | [optional]
+**username** | **String** | | [optional]
+
+
diff --git a/sdk/js/swagger/docs/Authenticator.md b/sdk/js/swagger/docs/Authenticator.md
new file mode 100644
index 000000000..8a65169ed
--- /dev/null
+++ b/sdk/js/swagger/docs/Authenticator.md
@@ -0,0 +1,7 @@
+# SwaggerJsClient.Authenticator
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+
+
diff --git a/sdk/js/swagger/docs/Firewall.md b/sdk/js/swagger/docs/Firewall.md
new file mode 100644
index 000000000..3f32c61ac
--- /dev/null
+++ b/sdk/js/swagger/docs/Firewall.md
@@ -0,0 +1,7 @@
+# SwaggerJsClient.Firewall
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+
+
diff --git a/sdk/js/swagger/docs/Handler.md b/sdk/js/swagger/docs/Handler.md
new file mode 100644
index 000000000..6eb3d40be
--- /dev/null
+++ b/sdk/js/swagger/docs/Handler.md
@@ -0,0 +1,9 @@
+# SwaggerJsClient.Handler
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**H** | [**Writer**](Writer.md) | | [optional]
+**manager** | [**Manager**](Manager.md) | | [optional]
+
+
diff --git a/sdk/js/swagger/docs/InlineResponse401.md b/sdk/js/swagger/docs/InlineResponse401.md
new file mode 100644
index 000000000..d4a3c3129
--- /dev/null
+++ b/sdk/js/swagger/docs/InlineResponse401.md
@@ -0,0 +1,13 @@
+# SwaggerJsClient.InlineResponse401
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**code** | **Number** | | [optional]
+**details** | **[{String: Object}]** | | [optional]
+**message** | **String** | | [optional]
+**reason** | **String** | | [optional]
+**request** | **String** | | [optional]
+**status** | **String** | | [optional]
+
+
diff --git a/sdk/js/swagger/docs/IntrospectionResponse.md b/sdk/js/swagger/docs/IntrospectionResponse.md
new file mode 100644
index 000000000..f25ff2f60
--- /dev/null
+++ b/sdk/js/swagger/docs/IntrospectionResponse.md
@@ -0,0 +1,17 @@
+# SwaggerJsClient.IntrospectionResponse
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**active** | **Boolean** | | [optional]
+**aud** | **String** | | [optional]
+**clientId** | **String** | | [optional]
+**exp** | **Number** | | [optional]
+**iat** | **Number** | | [optional]
+**iss** | **String** | | [optional]
+**nbf** | **Number** | | [optional]
+**scope** | **String** | | [optional]
+**sub** | **String** | Here, it's sub | [optional]
+**username** | **String** | | [optional]
+
+
diff --git a/sdk/js/swagger/docs/Manager.md b/sdk/js/swagger/docs/Manager.md
new file mode 100644
index 000000000..8a7d574ab
--- /dev/null
+++ b/sdk/js/swagger/docs/Manager.md
@@ -0,0 +1,7 @@
+# SwaggerJsClient.Manager
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+
+
diff --git a/sdk/js/swagger/docs/OAuth2IntrospectionAuthentication.md b/sdk/js/swagger/docs/OAuth2IntrospectionAuthentication.md
new file mode 100644
index 000000000..d5d492bbc
--- /dev/null
+++ b/sdk/js/swagger/docs/OAuth2IntrospectionAuthentication.md
@@ -0,0 +1,7 @@
+# SwaggerJsClient.OAuth2IntrospectionAuthentication
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+
+
diff --git a/sdk/js/swagger/docs/Policy.md b/sdk/js/swagger/docs/Policy.md
new file mode 100644
index 000000000..9253ce82d
--- /dev/null
+++ b/sdk/js/swagger/docs/Policy.md
@@ -0,0 +1,14 @@
+# SwaggerJsClient.Policy
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**actions** | **[String]** | Actions impacted by the policy. | [optional]
+**conditions** | [**{String: PolicyConditions}**](PolicyConditions.md) | Conditions under which the policy is active. | [optional]
+**description** | **String** | Description of the policy. | [optional]
+**effect** | **String** | Effect of the policy | [optional]
+**id** | **String** | ID of the policy. | [optional]
+**resources** | **[String]** | Resources impacted by the policy. | [optional]
+**subjects** | **[String]** | Subjects impacted by the policy. | [optional]
+
+
diff --git a/sdk/js/swagger/docs/PolicyApi.md b/sdk/js/swagger/docs/PolicyApi.md
new file mode 100644
index 000000000..d83a26d5b
--- /dev/null
+++ b/sdk/js/swagger/docs/PolicyApi.md
@@ -0,0 +1,251 @@
+# SwaggerJsClient.PolicyApi
+
+All URIs are relative to *http://localhost*
+
+Method | HTTP request | Description
+------------- | ------------- | -------------
+[**createPolicy**](PolicyApi.md#createPolicy) | **POST** /policies |
+[**deletePolicy**](PolicyApi.md#deletePolicy) | **DELETE** /policies/{id} |
+[**getPolicy**](PolicyApi.md#getPolicy) | **GET** /policies/{id} |
+[**listPolicies**](PolicyApi.md#listPolicies) | **GET** /policies |
+[**updatePolicy**](PolicyApi.md#updatePolicy) | **PUT** /policies/{id} |
+
+
+
+# **createPolicy**
+> Policy createPolicy(opts)
+
+
+
+Create an Access Control Policy
+
+### Example
+```javascript
+var SwaggerJsClient = require('swagger-js-client');
+
+var apiInstance = new SwaggerJsClient.PolicyApi();
+
+var opts = {
+ 'body': new SwaggerJsClient.Policy() // Policy |
+};
+
+var callback = function(error, data, response) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log('API called successfully. Returned data: ' + data);
+ }
+};
+apiInstance.createPolicy(opts, callback);
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **body** | [**Policy**](Policy.md)| | [optional]
+
+### Return type
+
+[**Policy**](Policy.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+
+# **deletePolicy**
+> deletePolicy(id)
+
+
+
+Delete an Access Control Policy
+
+### Example
+```javascript
+var SwaggerJsClient = require('swagger-js-client');
+
+var apiInstance = new SwaggerJsClient.PolicyApi();
+
+var id = "id_example"; // String | The id of the policy.
+
+
+var callback = function(error, data, response) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log('API called successfully.');
+ }
+};
+apiInstance.deletePolicy(id, callback);
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **id** | **String**| The id of the policy. |
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+
+# **getPolicy**
+> Policy getPolicy(id)
+
+
+
+Get an Access Control Policy
+
+### Example
+```javascript
+var SwaggerJsClient = require('swagger-js-client');
+
+var apiInstance = new SwaggerJsClient.PolicyApi();
+
+var id = "id_example"; // String | The id of the policy.
+
+
+var callback = function(error, data, response) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log('API called successfully. Returned data: ' + data);
+ }
+};
+apiInstance.getPolicy(id, callback);
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **id** | **String**| The id of the policy. |
+
+### Return type
+
+[**Policy**](Policy.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+
+# **listPolicies**
+> [Policy] listPolicies(opts)
+
+
+
+List Access Control Policies
+
+### Example
+```javascript
+var SwaggerJsClient = require('swagger-js-client');
+
+var apiInstance = new SwaggerJsClient.PolicyApi();
+
+var opts = {
+ 'offset': 789, // Number | The offset from where to start looking.
+ 'limit': 789 // Number | The maximum amount of policies returned.
+};
+
+var callback = function(error, data, response) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log('API called successfully. Returned data: ' + data);
+ }
+};
+apiInstance.listPolicies(opts, callback);
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **offset** | **Number**| The offset from where to start looking. | [optional]
+ **limit** | **Number**| The maximum amount of policies returned. | [optional]
+
+### Return type
+
+[**[Policy]**](Policy.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+
+# **updatePolicy**
+> Policy updatePolicy(id, opts)
+
+
+
+Update an Access Control Policy
+
+### Example
+```javascript
+var SwaggerJsClient = require('swagger-js-client');
+
+var apiInstance = new SwaggerJsClient.PolicyApi();
+
+var id = "id_example"; // String | The id of the policy.
+
+var opts = {
+ 'body': new SwaggerJsClient.Policy() // Policy |
+};
+
+var callback = function(error, data, response) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log('API called successfully. Returned data: ' + data);
+ }
+};
+apiInstance.updatePolicy(id, opts, callback);
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **id** | **String**| The id of the policy. |
+ **body** | [**Policy**](Policy.md)| | [optional]
+
+### Return type
+
+[**Policy**](Policy.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
diff --git a/sdk/js/swagger/docs/PolicyConditions.md b/sdk/js/swagger/docs/PolicyConditions.md
new file mode 100644
index 000000000..780683cda
--- /dev/null
+++ b/sdk/js/swagger/docs/PolicyConditions.md
@@ -0,0 +1,9 @@
+# SwaggerJsClient.PolicyConditions
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**options** | **{String: Object}** | | [optional]
+**type** | **String** | | [optional]
+
+
diff --git a/sdk/js/swagger/docs/Role.md b/sdk/js/swagger/docs/Role.md
new file mode 100644
index 000000000..ed2add88c
--- /dev/null
+++ b/sdk/js/swagger/docs/Role.md
@@ -0,0 +1,9 @@
+# SwaggerJsClient.Role
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**id** | **String** | ID is the role's unique id. | [optional]
+**members** | **[String]** | Members is who belongs to the role. | [optional]
+
+
diff --git a/sdk/js/swagger/docs/RoleApi.md b/sdk/js/swagger/docs/RoleApi.md
new file mode 100644
index 000000000..04ee37004
--- /dev/null
+++ b/sdk/js/swagger/docs/RoleApi.md
@@ -0,0 +1,304 @@
+# SwaggerJsClient.RoleApi
+
+All URIs are relative to *http://localhost*
+
+Method | HTTP request | Description
+------------- | ------------- | -------------
+[**addMembersToRole**](RoleApi.md#addMembersToRole) | **POST** /roles/{id}/members | Add members to a role
+[**createRole**](RoleApi.md#createRole) | **POST** /roles | Create a role
+[**deleteRole**](RoleApi.md#deleteRole) | **DELETE** /roles/{id} | Get a role by its ID
+[**getRole**](RoleApi.md#getRole) | **GET** /roles/{id} | Get a role by its ID
+[**listRoles**](RoleApi.md#listRoles) | **GET** /roles | List all roles
+[**removeMembersFromRole**](RoleApi.md#removeMembersFromRole) | **DELETE** /roles/{id}/members | Remove members from a role
+
+
+
+# **addMembersToRole**
+> addMembersToRole(id, opts)
+
+Add members to a role
+
+A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to add members (users, applications, ...) to a specific role. You have to know the role's ID.
+
+### Example
+```javascript
+var SwaggerJsClient = require('swagger-js-client');
+
+var apiInstance = new SwaggerJsClient.RoleApi();
+
+var id = "id_example"; // String | The id of the role to modify.
+
+var opts = {
+ 'body': new SwaggerJsClient.RoleMembers() // RoleMembers |
+};
+
+var callback = function(error, data, response) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log('API called successfully.');
+ }
+};
+apiInstance.addMembersToRole(id, opts, callback);
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **id** | **String**| The id of the role to modify. |
+ **body** | [**RoleMembers**](RoleMembers.md)| | [optional]
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+
+# **createRole**
+> Role createRole(opts)
+
+Create a role
+
+A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to create a new role. You may define members as well but you don't have to.
+
+### Example
+```javascript
+var SwaggerJsClient = require('swagger-js-client');
+
+var apiInstance = new SwaggerJsClient.RoleApi();
+
+var opts = {
+ 'body': new SwaggerJsClient.Role() // Role |
+};
+
+var callback = function(error, data, response) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log('API called successfully. Returned data: ' + data);
+ }
+};
+apiInstance.createRole(opts, callback);
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **body** | [**Role**](Role.md)| | [optional]
+
+### Return type
+
+[**Role**](Role.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+
+# **deleteRole**
+> deleteRole(id)
+
+Get a role by its ID
+
+A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to delete an existing role. You have to know the role's ID.
+
+### Example
+```javascript
+var SwaggerJsClient = require('swagger-js-client');
+
+var apiInstance = new SwaggerJsClient.RoleApi();
+
+var id = "id_example"; // String | The id of the role to look up.
+
+
+var callback = function(error, data, response) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log('API called successfully.');
+ }
+};
+apiInstance.deleteRole(id, callback);
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **id** | **String**| The id of the role to look up. |
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+
+# **getRole**
+> Role getRole(id)
+
+Get a role by its ID
+
+A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to retrieve an existing role. You have to know the role's ID.
+
+### Example
+```javascript
+var SwaggerJsClient = require('swagger-js-client');
+
+var apiInstance = new SwaggerJsClient.RoleApi();
+
+var id = "id_example"; // String | The id of the role to look up.
+
+
+var callback = function(error, data, response) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log('API called successfully. Returned data: ' + data);
+ }
+};
+apiInstance.getRole(id, callback);
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **id** | **String**| The id of the role to look up. |
+
+### Return type
+
+[**Role**](Role.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+
+# **listRoles**
+> [Role] listRoles(opts)
+
+List all roles
+
+A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to retrieve all roles that are stored in the system.
+
+### Example
+```javascript
+var SwaggerJsClient = require('swagger-js-client');
+
+var apiInstance = new SwaggerJsClient.RoleApi();
+
+var opts = {
+ 'member': "member_example", // String | The id of the member to look up.
+ 'limit': 789, // Number | The maximum amount of policies returned.
+ 'offset': 789 // Number | The offset from where to start looking.
+};
+
+var callback = function(error, data, response) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log('API called successfully. Returned data: ' + data);
+ }
+};
+apiInstance.listRoles(opts, callback);
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **member** | **String**| The id of the member to look up. | [optional]
+ **limit** | **Number**| The maximum amount of policies returned. | [optional]
+ **offset** | **Number**| The offset from where to start looking. | [optional]
+
+### Return type
+
+[**[Role]**](Role.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+
+# **removeMembersFromRole**
+> removeMembersFromRole(id, opts)
+
+Remove members from a role
+
+A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular user or some other sort of role. This endpoint allows you to remove members (users, applications, ...) from a specific role. You have to know the role's ID.
+
+### Example
+```javascript
+var SwaggerJsClient = require('swagger-js-client');
+
+var apiInstance = new SwaggerJsClient.RoleApi();
+
+var id = "id_example"; // String | The id of the role to modify.
+
+var opts = {
+ 'body': new SwaggerJsClient.RoleMembers() // RoleMembers |
+};
+
+var callback = function(error, data, response) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log('API called successfully.');
+ }
+};
+apiInstance.removeMembersFromRole(id, opts, callback);
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **id** | **String**| The id of the role to modify. |
+ **body** | [**RoleMembers**](RoleMembers.md)| | [optional]
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
diff --git a/sdk/js/swagger/docs/RoleMembers.md b/sdk/js/swagger/docs/RoleMembers.md
new file mode 100644
index 000000000..61dd66888
--- /dev/null
+++ b/sdk/js/swagger/docs/RoleMembers.md
@@ -0,0 +1,8 @@
+# SwaggerJsClient.RoleMembers
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**members** | **[String]** | | [optional]
+
+
diff --git a/sdk/js/swagger/docs/Session.md b/sdk/js/swagger/docs/Session.md
new file mode 100644
index 000000000..8d19b3fb9
--- /dev/null
+++ b/sdk/js/swagger/docs/Session.md
@@ -0,0 +1,8 @@
+# SwaggerJsClient.Session
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**getSubject** | **String** | | [optional]
+
+
diff --git a/sdk/js/swagger/docs/SwaggerCreatePolicyParameters.md b/sdk/js/swagger/docs/SwaggerCreatePolicyParameters.md
new file mode 100644
index 000000000..f92acefb6
--- /dev/null
+++ b/sdk/js/swagger/docs/SwaggerCreatePolicyParameters.md
@@ -0,0 +1,8 @@
+# SwaggerJsClient.SwaggerCreatePolicyParameters
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**body** | [**Policy**](Policy.md) | | [optional]
+
+
diff --git a/sdk/js/swagger/docs/SwaggerDoesWardenAllowAccessRequestParameters.md b/sdk/js/swagger/docs/SwaggerDoesWardenAllowAccessRequestParameters.md
new file mode 100644
index 000000000..f97362c13
--- /dev/null
+++ b/sdk/js/swagger/docs/SwaggerDoesWardenAllowAccessRequestParameters.md
@@ -0,0 +1,8 @@
+# SwaggerJsClient.SwaggerDoesWardenAllowAccessRequestParameters
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**body** | [**WardenSubjectAuthorizationRequest**](WardenSubjectAuthorizationRequest.md) | | [optional]
+
+
diff --git a/sdk/js/swagger/docs/SwaggerDoesWardenAllowTokenAccessRqeuestParameters.md b/sdk/js/swagger/docs/SwaggerDoesWardenAllowTokenAccessRqeuestParameters.md
new file mode 100644
index 000000000..9f77525da
--- /dev/null
+++ b/sdk/js/swagger/docs/SwaggerDoesWardenAllowTokenAccessRqeuestParameters.md
@@ -0,0 +1,8 @@
+# SwaggerJsClient.SwaggerDoesWardenAllowTokenAccessRqeuestParameters
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**body** | [**WardenOAuth2AuthorizationRequest**](WardenOAuth2AuthorizationRequest.md) | | [optional]
+
+
diff --git a/sdk/js/swagger/docs/SwaggerGetPolicyParameters.md b/sdk/js/swagger/docs/SwaggerGetPolicyParameters.md
new file mode 100644
index 000000000..70b170938
--- /dev/null
+++ b/sdk/js/swagger/docs/SwaggerGetPolicyParameters.md
@@ -0,0 +1,8 @@
+# SwaggerJsClient.SwaggerGetPolicyParameters
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**id** | **String** | The id of the policy. in: path | [optional]
+
+
diff --git a/sdk/js/swagger/docs/SwaggerListPolicyParameters.md b/sdk/js/swagger/docs/SwaggerListPolicyParameters.md
new file mode 100644
index 000000000..d0274f435
--- /dev/null
+++ b/sdk/js/swagger/docs/SwaggerListPolicyParameters.md
@@ -0,0 +1,9 @@
+# SwaggerJsClient.SwaggerListPolicyParameters
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**limit** | **Number** | The maximum amount of policies returned. in: query | [optional]
+**offset** | **Number** | The offset from where to start looking. in: query | [optional]
+
+
diff --git a/sdk/js/swagger/docs/SwaggerListPolicyResponse.md b/sdk/js/swagger/docs/SwaggerListPolicyResponse.md
new file mode 100644
index 000000000..09c30856c
--- /dev/null
+++ b/sdk/js/swagger/docs/SwaggerListPolicyResponse.md
@@ -0,0 +1,8 @@
+# SwaggerJsClient.SwaggerListPolicyResponse
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**body** | [**[Policy]**](Policy.md) | in: body type: array | [optional]
+
+
diff --git a/sdk/js/swagger/docs/SwaggerUpdatePolicyParameters.md b/sdk/js/swagger/docs/SwaggerUpdatePolicyParameters.md
new file mode 100644
index 000000000..6b6319c96
--- /dev/null
+++ b/sdk/js/swagger/docs/SwaggerUpdatePolicyParameters.md
@@ -0,0 +1,9 @@
+# SwaggerJsClient.SwaggerUpdatePolicyParameters
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**body** | [**Policy**](Policy.md) | | [optional]
+**id** | **String** | The id of the policy. in: path | [optional]
+
+
diff --git a/sdk/js/swagger/docs/SwaggerWardenBaseRequest.md b/sdk/js/swagger/docs/SwaggerWardenBaseRequest.md
new file mode 100644
index 000000000..dbe949d71
--- /dev/null
+++ b/sdk/js/swagger/docs/SwaggerWardenBaseRequest.md
@@ -0,0 +1,10 @@
+# SwaggerJsClient.SwaggerWardenBaseRequest
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**action** | **String** | Action is the action that is requested on the resource. | [optional]
+**context** | **{String: Object}** | Context is the request's environmental context. | [optional]
+**resource** | **String** | Resource is the resource that access is requested to. | [optional]
+
+
diff --git a/sdk/js/swagger/docs/WardenApi.md b/sdk/js/swagger/docs/WardenApi.md
new file mode 100644
index 000000000..156eaab67
--- /dev/null
+++ b/sdk/js/swagger/docs/WardenApi.md
@@ -0,0 +1,104 @@
+# SwaggerJsClient.WardenApi
+
+All URIs are relative to *http://localhost*
+
+Method | HTTP request | Description
+------------- | ------------- | -------------
+[**isOAuth2AccessTokenAuthorized**](WardenApi.md#isOAuth2AccessTokenAuthorized) | **POST** /warden/oauth2/authorize | Check if an OAuth 2.0 access token is authorized to access a resource
+[**isSubjectAuthorized**](WardenApi.md#isSubjectAuthorized) | **POST** /warden/subjects/authorize | Check if a subject is authorized to access a resource
+
+
+
+# **isOAuth2AccessTokenAuthorized**
+> isOAuth2AccessTokenAuthorized(opts)
+
+Check if an OAuth 2.0 access token is authorized to access a resource
+
+Checks if a token is valid and if the token subject is allowed to perform an action on a resource. This endpoint requires a token, a scope, a resource name, an action name and a context. If a token is expired/invalid, has not been granted the requested scope or the subject is not allowed to perform the action on the resource, this endpoint returns a 200 response with `{ \"allowed\": false }`. This endpoint passes all data from the upstream OAuth 2.0 token introspection endpoint. If you use ORY Hydra as an upstream OAuth 2.0 provider, data set through the `accessTokenExtra` field in the consent flow will be included in this response as well.
+
+### Example
+```javascript
+var SwaggerJsClient = require('swagger-js-client');
+
+var apiInstance = new SwaggerJsClient.WardenApi();
+
+var opts = {
+ 'body': new SwaggerJsClient.WardenOAuth2AuthorizationRequest() // WardenOAuth2AuthorizationRequest |
+};
+
+var callback = function(error, data, response) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log('API called successfully.');
+ }
+};
+apiInstance.isOAuth2AccessTokenAuthorized(opts, callback);
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **body** | [**WardenOAuth2AuthorizationRequest**](WardenOAuth2AuthorizationRequest.md)| | [optional]
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
+
+# **isSubjectAuthorized**
+> isSubjectAuthorized(opts)
+
+Check if a subject is authorized to access a resource
+
+Checks if a subject (e.g. user ID, API key, ...) is allowed to perform a certain action on a resource.
+
+### Example
+```javascript
+var SwaggerJsClient = require('swagger-js-client');
+
+var apiInstance = new SwaggerJsClient.WardenApi();
+
+var opts = {
+ 'body': new SwaggerJsClient.WardenSubjectAuthorizationRequest() // WardenSubjectAuthorizationRequest |
+};
+
+var callback = function(error, data, response) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log('API called successfully.');
+ }
+};
+apiInstance.isSubjectAuthorized(opts, callback);
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **body** | [**WardenSubjectAuthorizationRequest**](WardenSubjectAuthorizationRequest.md)| | [optional]
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: application/json
+ - **Accept**: application/json
+
diff --git a/sdk/js/swagger/docs/WardenOAuth2AuthorizationRequest.md b/sdk/js/swagger/docs/WardenOAuth2AuthorizationRequest.md
new file mode 100644
index 000000000..8c22fa13a
--- /dev/null
+++ b/sdk/js/swagger/docs/WardenOAuth2AuthorizationRequest.md
@@ -0,0 +1,12 @@
+# SwaggerJsClient.WardenOAuth2AuthorizationRequest
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**action** | **String** | Action is the action that is requested on the resource. | [optional]
+**context** | **{String: Object}** | Context is the request's environmental context. | [optional]
+**resource** | **String** | Resource is the resource that access is requested to. | [optional]
+**scopes** | **[String]** | Scopes is an array of scopes that are required. | [optional]
+**token** | **String** | Token is the token to introspect. | [optional]
+
+
diff --git a/sdk/js/swagger/docs/WardenOAuth2AuthorizationResponse.md b/sdk/js/swagger/docs/WardenOAuth2AuthorizationResponse.md
new file mode 100644
index 000000000..1a578eaa0
--- /dev/null
+++ b/sdk/js/swagger/docs/WardenOAuth2AuthorizationResponse.md
@@ -0,0 +1,18 @@
+# SwaggerJsClient.WardenOAuth2AuthorizationResponse
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**accessTokenExtra** | **{String: Object}** | Extra represents arbitrary session data. | [optional]
+**allowed** | **Boolean** | Allowed is true if the request is allowed and false otherwise. | [optional]
+**audience** | **String** | | [optional]
+**clientId** | **String** | ClientID is the id of the OAuth2 client that requested the token. | [optional]
+**expiresAt** | **Date** | ExpiresAt is the expiry timestamp. | [optional]
+**grantedScopes** | **[String]** | GrantedScopes is a list of scopes that the subject authorized when asked for consent. | [optional]
+**issuedAt** | **Date** | IssuedAt is the token creation time stamp. | [optional]
+**issuer** | **String** | Issuer is the id of the issuer, typically an hydra instance. | [optional]
+**notBefore** | **Date** | | [optional]
+**subject** | **String** | Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too. | [optional]
+**username** | **String** | | [optional]
+
+
diff --git a/sdk/js/swagger/docs/WardenSubjectAuthorizationRequest.md b/sdk/js/swagger/docs/WardenSubjectAuthorizationRequest.md
new file mode 100644
index 000000000..33e316ed0
--- /dev/null
+++ b/sdk/js/swagger/docs/WardenSubjectAuthorizationRequest.md
@@ -0,0 +1,11 @@
+# SwaggerJsClient.WardenSubjectAuthorizationRequest
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**action** | **String** | Action is the action that is requested on the resource. | [optional]
+**context** | **{String: Object}** | Context is the request's environmental context. | [optional]
+**resource** | **String** | Resource is the resource that access is requested to. | [optional]
+**subject** | **String** | Subejct is the subject that is requesting access. | [optional]
+
+
diff --git a/sdk/js/swagger/docs/WardenSubjectAuthorizationResponse.md b/sdk/js/swagger/docs/WardenSubjectAuthorizationResponse.md
new file mode 100644
index 000000000..71292620e
--- /dev/null
+++ b/sdk/js/swagger/docs/WardenSubjectAuthorizationResponse.md
@@ -0,0 +1,9 @@
+# SwaggerJsClient.WardenSubjectAuthorizationResponse
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**allowed** | **Boolean** | Allowed is true if the request is allowed and false otherwise. | [optional]
+**subject** | **String** | Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too. | [optional]
+
+
diff --git a/sdk/js/swagger/docs/Writer.md b/sdk/js/swagger/docs/Writer.md
new file mode 100644
index 000000000..deda4e557
--- /dev/null
+++ b/sdk/js/swagger/docs/Writer.md
@@ -0,0 +1,7 @@
+# SwaggerJsClient.Writer
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+
+
diff --git a/sdk/js/swagger/git_push.sh b/sdk/js/swagger/git_push.sh
new file mode 100644
index 000000000..0d041ad0b
--- /dev/null
+++ b/sdk/js/swagger/git_push.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
+#
+# Usage example: /bin/sh ./git_push.sh wing328 swagger-petstore-perl "minor update"
+
+git_user_id=$1
+git_repo_id=$2
+release_note=$3
+
+if [ "$git_user_id" = "" ]; then
+ git_user_id="GIT_USER_ID"
+ echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
+fi
+
+if [ "$git_repo_id" = "" ]; then
+ git_repo_id="GIT_REPO_ID"
+ echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
+fi
+
+if [ "$release_note" = "" ]; then
+ release_note="Minor update"
+ echo "[INFO] No command line input provided. Set \$release_note to $release_note"
+fi
+
+# Initialize the local directory as a Git repository
+git init
+
+# Adds the files in the local repository and stages them for commit.
+git add .
+
+# Commits the tracked changes and prepares them to be pushed to a remote repository.
+git commit -m "$release_note"
+
+# Sets the new remote
+git_remote=`git remote`
+if [ "$git_remote" = "" ]; then # git remote not defined
+
+ if [ "$GIT_TOKEN" = "" ]; then
+ echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the Git credential in your environment."
+ git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git
+ else
+ git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git
+ fi
+
+fi
+
+git pull origin master
+
+# Pushes (Forces) the changes in the local repository up to the remote repository
+echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git"
+git push origin master 2>&1 | grep -v 'To https'
+
diff --git a/sdk/js/swagger/mocha.opts b/sdk/js/swagger/mocha.opts
new file mode 100644
index 000000000..907011807
--- /dev/null
+++ b/sdk/js/swagger/mocha.opts
@@ -0,0 +1 @@
+--timeout 10000
diff --git a/sdk/js/swagger/src/ApiClient.js b/sdk/js/swagger/src/ApiClient.js
new file mode 100644
index 000000000..106697af9
--- /dev/null
+++ b/sdk/js/swagger/src/ApiClient.js
@@ -0,0 +1,586 @@
+/**
+ *
+ * Package main ORY Keto
+ *
+ * OpenAPI spec version: Latest
+ * Contact: hi@ory.am
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ *
+ * Swagger Codegen version: 2.2.3
+ *
+ * Do not edit the class manually.
+ *
+ */
+
+;(function(root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['superagent', 'querystring'], factory)
+ } else if (typeof module === 'object' && module.exports) {
+ // CommonJS-like environments that support module.exports, like Node.
+ module.exports = factory(require('superagent'), require('querystring'))
+ } else {
+ // Browser globals (root is window)
+ if (!root.SwaggerJsClient) {
+ root.SwaggerJsClient = {}
+ }
+ root.SwaggerJsClient.ApiClient = factory(root.superagent, root.querystring)
+ }
+})(this, function(superagent, querystring) {
+ 'use strict'
+
+ /**
+ * @module ApiClient
+ * @version Latest
+ */
+
+ /**
+ * Manages low level client-server communications, parameter marshalling, etc. There should not be any need for an
+ * application to use this class directly - the *Api and model classes provide the public API for the service. The
+ * contents of this file should be regarded as internal but are documented for completeness.
+ * @alias module:ApiClient
+ * @class
+ */
+ var exports = function() {
+ /**
+ * The base URL against which to resolve every API call's (relative) path.
+ * @type {String}
+ * @default http://localhost
+ */
+ this.basePath = 'http://localhost'.replace(/\/+$/, '')
+
+ /**
+ * The authentication methods to be included for all API calls.
+ * @type {Array.}
+ */
+ this.authentications = {}
+ /**
+ * The default HTTP headers to be included for all API calls.
+ * @type {Array.}
+ * @default {}
+ */
+ this.defaultHeaders = {}
+
+ /**
+ * The default HTTP timeout for all API calls.
+ * @type {Number}
+ * @default 60000
+ */
+ this.timeout = 60000
+
+ /**
+ * If set to false an additional timestamp parameter is added to all API GET calls to
+ * prevent browser caching
+ * @type {Boolean}
+ * @default true
+ */
+ this.cache = true
+
+ /**
+ * If set to true, the client will save the cookies from each server
+ * response, and return them in the next request.
+ * @default false
+ */
+ this.enableCookies = false
+
+ /*
+ * Used to save and return cookies in a node.js (non-browser) setting,
+ * if this.enableCookies is set to true.
+ */
+ if (typeof window === 'undefined') {
+ this.agent = new superagent.agent()
+ }
+ }
+
+ /**
+ * Returns a string representation for an actual parameter.
+ * @param param The actual parameter.
+ * @returns {String} The string representation of param
.
+ */
+ exports.prototype.paramToString = function(param) {
+ if (param == undefined || param == null) {
+ return ''
+ }
+ if (param instanceof Date) {
+ return param.toJSON()
+ }
+ return param.toString()
+ }
+
+ /**
+ * Builds full URL by appending the given path to the base URL and replacing path parameter place-holders with parameter values.
+ * NOTE: query parameters are not handled here.
+ * @param {String} path The path to append to the base URL.
+ * @param {Object} pathParams The parameter values to append.
+ * @returns {String} The encoded path with parameter values substituted.
+ */
+ exports.prototype.buildUrl = function(path, pathParams) {
+ if (!path.match(/^\//)) {
+ path = '/' + path
+ }
+ var url = this.basePath + path
+ var _this = this
+ url = url.replace(/\{([\w-]+)\}/g, function(fullMatch, key) {
+ var value
+ if (pathParams.hasOwnProperty(key)) {
+ value = _this.paramToString(pathParams[key])
+ } else {
+ value = fullMatch
+ }
+ return encodeURIComponent(value)
+ })
+ return url
+ }
+
+ /**
+ * Checks whether the given content type represents JSON.
+ * JSON content type examples:
+ *
+ * - application/json
+ * - application/json; charset=UTF8
+ * - APPLICATION/JSON
+ *
+ * @param {String} contentType The MIME content type to check.
+ * @returns {Boolean} true
if contentType
represents JSON, otherwise false
.
+ */
+ exports.prototype.isJsonMime = function(contentType) {
+ return Boolean(
+ contentType != null && contentType.match(/^application\/json(;.*)?$/i)
+ )
+ }
+
+ /**
+ * Chooses a content type from the given array, with JSON preferred; i.e. return JSON if included, otherwise return the first.
+ * @param {Array.} contentTypes
+ * @returns {String} The chosen content type, preferring JSON.
+ */
+ exports.prototype.jsonPreferredMime = function(contentTypes) {
+ for (var i = 0; i < contentTypes.length; i++) {
+ if (this.isJsonMime(contentTypes[i])) {
+ return contentTypes[i]
+ }
+ }
+ return contentTypes[0]
+ }
+
+ /**
+ * Checks whether the given parameter value represents file-like content.
+ * @param param The parameter to check.
+ * @returns {Boolean} true
if param
represents a file.
+ */
+ exports.prototype.isFileParam = function(param) {
+ // fs.ReadStream in Node.js and Electron (but not in runtime like browserify)
+ if (typeof require === 'function') {
+ var fs
+ try {
+ fs = require('fs')
+ } catch (err) {}
+ if (fs && fs.ReadStream && param instanceof fs.ReadStream) {
+ return true
+ }
+ }
+ // Buffer in Node.js
+ if (typeof Buffer === 'function' && param instanceof Buffer) {
+ return true
+ }
+ // Blob in browser
+ if (typeof Blob === 'function' && param instanceof Blob) {
+ return true
+ }
+ // File in browser (it seems File object is also instance of Blob, but keep this for safe)
+ if (typeof File === 'function' && param instanceof File) {
+ return true
+ }
+ return false
+ }
+
+ /**
+ * Normalizes parameter values:
+ *
+ * - remove nils
+ * - keep files and arrays
+ * - format to string with `paramToString` for other cases
+ *
+ * @param {Object.} params The parameters as object properties.
+ * @returns {Object.} normalized parameters.
+ */
+ exports.prototype.normalizeParams = function(params) {
+ var newParams = {}
+ for (var key in params) {
+ if (
+ params.hasOwnProperty(key) &&
+ params[key] != undefined &&
+ params[key] != null
+ ) {
+ var value = params[key]
+ if (this.isFileParam(value) || Array.isArray(value)) {
+ newParams[key] = value
+ } else {
+ newParams[key] = this.paramToString(value)
+ }
+ }
+ }
+ return newParams
+ }
+
+ /**
+ * Enumeration of collection format separator strategies.
+ * @enum {String}
+ * @readonly
+ */
+ exports.CollectionFormatEnum = {
+ /**
+ * Comma-separated values. Value: csv
+ * @const
+ */
+ CSV: ',',
+ /**
+ * Space-separated values. Value: ssv
+ * @const
+ */
+ SSV: ' ',
+ /**
+ * Tab-separated values. Value: tsv
+ * @const
+ */
+ TSV: '\t',
+ /**
+ * Pipe(|)-separated values. Value: pipes
+ * @const
+ */
+ PIPES: '|',
+ /**
+ * Native array. Value: multi
+ * @const
+ */
+ MULTI: 'multi'
+ }
+
+ /**
+ * Builds a string representation of an array-type actual parameter, according to the given collection format.
+ * @param {Array} param An array parameter.
+ * @param {module:ApiClient.CollectionFormatEnum} collectionFormat The array element separator strategy.
+ * @returns {String|Array} A string representation of the supplied collection, using the specified delimiter. Returns
+ * param
as is if collectionFormat
is multi
.
+ */
+ exports.prototype.buildCollectionParam = function buildCollectionParam(
+ param,
+ collectionFormat
+ ) {
+ if (param == null) {
+ return null
+ }
+ switch (collectionFormat) {
+ case 'csv':
+ return param.map(this.paramToString).join(',')
+ case 'ssv':
+ return param.map(this.paramToString).join(' ')
+ case 'tsv':
+ return param.map(this.paramToString).join('\t')
+ case 'pipes':
+ return param.map(this.paramToString).join('|')
+ case 'multi':
+ // return the array directly as SuperAgent will handle it as expected
+ return param.map(this.paramToString)
+ default:
+ throw new Error('Unknown collection format: ' + collectionFormat)
+ }
+ }
+
+ /**
+ * Applies authentication headers to the request.
+ * @param {Object} request The request object created by a superagent()
call.
+ * @param {Array.} authNames An array of authentication method names.
+ */
+ exports.prototype.applyAuthToRequest = function(request, authNames) {
+ var _this = this
+ authNames.forEach(function(authName) {
+ var auth = _this.authentications[authName]
+ switch (auth.type) {
+ case 'basic':
+ if (auth.username || auth.password) {
+ request.auth(auth.username || '', auth.password || '')
+ }
+ break
+ case 'apiKey':
+ if (auth.apiKey) {
+ var data = {}
+ if (auth.apiKeyPrefix) {
+ data[auth.name] = auth.apiKeyPrefix + ' ' + auth.apiKey
+ } else {
+ data[auth.name] = auth.apiKey
+ }
+ if (auth['in'] === 'header') {
+ request.set(data)
+ } else {
+ request.query(data)
+ }
+ }
+ break
+ case 'oauth2':
+ if (auth.accessToken) {
+ request.set({ Authorization: 'Bearer ' + auth.accessToken })
+ }
+ break
+ default:
+ throw new Error('Unknown authentication type: ' + auth.type)
+ }
+ })
+ }
+
+ /**
+ * Deserializes an HTTP response body into a value of the specified type.
+ * @param {Object} response A SuperAgent response object.
+ * @param {(String|Array.|Object.|Function)} returnType The type to return. Pass a string for simple types
+ * or the constructor function for a complex type. Pass an array containing the type name to return an array of that type. To
+ * return an object, pass an object with one property whose name is the key type and whose value is the corresponding value type:
+ * all properties on data will be converted to this type.
+ * @returns A value of the specified type.
+ */
+ exports.prototype.deserialize = function deserialize(response, returnType) {
+ if (response == null || returnType == null || response.status == 204) {
+ return null
+ }
+ // Rely on SuperAgent for parsing response body.
+ // See http://visionmedia.github.io/superagent/#parsing-response-bodies
+ var data = response.body
+ if (
+ data == null ||
+ (typeof data === 'object' &&
+ typeof data.length === 'undefined' &&
+ !Object.keys(data).length)
+ ) {
+ // SuperAgent does not always produce a body; use the unparsed response as a fallback
+ data = response.text
+ }
+ return exports.convertToType(data, returnType)
+ }
+
+ /**
+ * Callback function to receive the result of the operation.
+ * @callback module:ApiClient~callApiCallback
+ * @param {String} error Error message, if any.
+ * @param data The data returned by the service call.
+ * @param {String} response The complete HTTP response.
+ */
+
+ /**
+ * Invokes the REST service using the supplied settings and parameters.
+ * @param {String} path The base URL to invoke.
+ * @param {String} httpMethod The HTTP method to use.
+ * @param {Object.} pathParams A map of path parameters and their values.
+ * @param {Object.} queryParams A map of query parameters and their values.
+ * @param {Object.