From e6df527afa02258b650a852bb1aa3f6c36fe4d65 Mon Sep 17 00:00:00 2001 From: zepatrik Date: Thu, 16 Sep 2021 17:29:32 +0200 Subject: [PATCH 1/9] u --- .schema/relation_tuple.schema.json | 26 +- Makefile | 16 +- internal/expand/tree.go | 8 +- internal/relationtuple/definitions.go | 45 +- internal/relationtuple/definitions_test.go | 70 +- .../relationtuple/transact_server_test.go | 12 +- spec/api.json | 8 +- spec/patches/subjects.yml | 4 + spec/swagger.json | 707 ++++++++++++++++++ 9 files changed, 852 insertions(+), 44 deletions(-) create mode 100644 spec/patches/subjects.yml create mode 100755 spec/swagger.json diff --git a/.schema/relation_tuple.schema.json b/.schema/relation_tuple.schema.json index 203b4108d..db3fe5006 100644 --- a/.schema/relation_tuple.schema.json +++ b/.schema/relation_tuple.schema.json @@ -24,16 +24,28 @@ "subject": { "oneOf": [ { - "type": "string", - "pattern": "^.*:.*#.*$", - "description": "The subject set affected by this relation. Uses the encoding of \":#\"." + "type": "object", + "description": "The subject set affected by this relation.", + "properties": { + "namespace": { + "type": "string", + "description": "The namespace of the object and relation in this subject set." + }, + "relation": { + "type": "string", + "description": "The relation of this subject set." + }, + "object": { + "type": "string", + "description": "The object referenced in this subject set." + } + }, + "additionalProperties": false, + "required": ["namespace", "relation", "object"] }, { "type": "string", - "description": "The subject affected by this relation. Use \":#\" to describe a subject set.", - "not": { - "pattern": "^.*:.*#.*$" - } + "description": "The subject ID affected by this relation." } ] } diff --git a/Makefile b/Makefile index 78020820f..81988bf92 100644 --- a/Makefile +++ b/Makefile @@ -50,10 +50,18 @@ docker: # Generates the SDKs .PHONY: sdk sdk: .bin/swagger .bin/ory - swagger generate spec -m -o ./spec/api.json -x internal/httpclient -x proto/ory/keto -x docker - ory dev swagger sanitize ./spec/api.json - swagger flatten --with-flatten=remove-unused -o ./spec/api.json ./spec/api.json - swagger validate ./spec/api.json + swagger generate spec -m -o ./spec/swagger.json -x internal/httpclient -x proto/ory/keto -x docker + ory dev swagger sanitize ./spec/swagger.json + swagger flatten --with-flatten=remove-unused -o ./spec/swagger.json ./spec/swagger.json + swagger validate ./spec/swagger.json + + CIRCLE_PROJECT_USERNAME=ory CIRCLE_PROJECT_REPONAME=kratos \ + ory dev openapi migrate \ + --health-path-tags metadata \ + -p https://raw.githubusercontent.com/ory/x/master/healthx/openapi/patch.yaml \ + -p file://spec/patches/subjects.yml \ + spec/swagger.json spec/api.json + rm -rf internal/httpclient mkdir -p internal/httpclient swagger generate client -f ./spec/api.json -t internal/httpclient -A Ory_Keto diff --git a/internal/expand/tree.go b/internal/expand/tree.go index 63375705b..87af39057 100644 --- a/internal/expand/tree.go +++ b/internal/expand/tree.go @@ -85,9 +85,9 @@ func NodeTypeFromProto(t acl.NodeType) NodeType { func (t *Tree) UnmarshalJSON(v []byte) error { type node struct { - Type NodeType `json:"type"` - Children []*Tree `json:"children,omitempty"` - Subject string `json:"subject"` + Type NodeType `json:"type"` + Children []*Tree `json:"children,omitempty"` + Subject json.RawMessage `json:"subject"` } n := &node{} @@ -96,7 +96,7 @@ func (t *Tree) UnmarshalJSON(v []byte) error { } var err error - t.Subject, err = relationtuple.SubjectFromString(n.Subject) + t.Subject, err = relationtuple.SubjectFromJSON(n.Subject) if err != nil { return err } diff --git a/internal/relationtuple/definitions.go b/internal/relationtuple/definitions.go index b0cffbd93..841836a90 100644 --- a/internal/relationtuple/definitions.go +++ b/internal/relationtuple/definitions.go @@ -14,8 +14,6 @@ import ( acl "github.com/ory/keto/proto/ory/keto/acl/v1alpha1" - "github.com/tidwall/sjson" - "github.com/pkg/errors" "github.com/ory/keto/internal/x" @@ -62,9 +60,6 @@ type TupleData interface { // swagger:model subject type Subject interface { - // swagger:ignore - json.Marshaler - // swagger:ignore String() string // swagger:ignore @@ -107,6 +102,7 @@ type InternalRelationTuple struct { // swagger:model subject // nolint:deadcode,unused +// this is overwritten with the right definition by /spec/patches/subjects.yml type stringEncodedSubject string // swagger:parameters getExpand @@ -133,8 +129,9 @@ type SubjectSet struct { var ( _, _ Subject = &SubjectID{}, &SubjectSet{} - ErrMalformedInput = herodot.ErrBadRequest.WithError("malformed string input") - ErrNilSubject = herodot.ErrBadRequest.WithError("subject is not allowed to be nil") + ErrMalformedInput = herodot.ErrBadRequest.WithError("malformed string input") + ErrNilSubject = herodot.ErrBadRequest.WithError("subject is not allowed to be nil") + ErrNonParsableSubject = herodot.ErrBadRequest.WithError("subject is not parsable") ) // swagger:enum patchAction @@ -166,6 +163,22 @@ func SubjectFromString(s string) (Subject, error) { return (&SubjectID{}).FromString(s) } +func SubjectFromJSON(raw []byte) (subject Subject, err error) { + s := gjson.ParseBytes(raw) + if s.IsObject() { + subject = &SubjectSet{} + if err := json.Unmarshal([]byte(s.Raw), subject); err != nil { + return nil, errors.WithStack(err) + } + } else if s.Type == gjson.String { + subject = &SubjectID{ID: s.Str} + } else { + return nil, errors.Wrapf(ErrNonParsableSubject, "expected subject to be of type string or object, but got %s", s.Type) + } + + return +} + // swagger:ignore func SubjectFromProto(gs *acl.Subject) (Subject, error) { switch s := gs.GetRef().(type) { @@ -275,11 +288,7 @@ func (s *SubjectSet) Equals(v interface{}) bool { } func (s SubjectID) MarshalJSON() ([]byte, error) { - return []byte(`"` + s.String() + `"`), nil -} - -func (s SubjectSet) MarshalJSON() ([]byte, error) { - return []byte(`"` + s.String() + `"`), nil + return json.Marshal(s.ID) } func (r *InternalRelationTuple) String() string { @@ -326,17 +335,17 @@ func (r *InternalRelationTuple) DeriveSubject() *SubjectSet { } func (r *InternalRelationTuple) UnmarshalJSON(raw []byte) error { - subject := gjson.GetBytes(raw, "subject").Str + parsed := gjson.ParseBytes(raw) var err error - r.Subject, err = SubjectFromString(subject) + r.Subject, err = SubjectFromJSON([]byte(parsed.Get("subject").Raw)) if err != nil { return err } - r.Namespace = gjson.GetBytes(raw, "namespace").Str - r.Object = gjson.GetBytes(raw, "object").Str - r.Relation = gjson.GetBytes(raw, "relation").Str + r.Namespace = parsed.Get("namespace").Str + r.Object = parsed.Get("object").Str + r.Relation = parsed.Get("relation").Str return nil } @@ -349,7 +358,7 @@ func (r *InternalRelationTuple) MarshalJSON() ([]byte, error) { return nil, errors.WithStack(err) } - return sjson.SetBytes(enc, "subject", r.Subject.String()) + return enc, nil } func (r *InternalRelationTuple) FromDataProvider(d TupleData) (*InternalRelationTuple, error) { diff --git a/internal/relationtuple/definitions_test.go b/internal/relationtuple/definitions_test.go index 2e39a5917..21ebf8be0 100644 --- a/internal/relationtuple/definitions_test.go +++ b/internal/relationtuple/definitions_test.go @@ -236,7 +236,12 @@ func TestSubject(t *testing.T) { Object: "o", Relation: "r", }, - json: "\"n:o#r\"", + json: ` +{ + "namespace": "n", + "object": "o", + "relation": "r" +}`, }, { sub: &SubjectID{ID: "foo"}, @@ -246,7 +251,7 @@ func TestSubject(t *testing.T) { t.Run(fmt.Sprintf("case=%d", i), func(t *testing.T) { enc, err := json.Marshal(tc.sub) require.NoError(t, err) - assert.Equal(t, string(enc), tc.json) + assert.JSONEq(t, tc.json, string(enc)) }) } }) @@ -457,6 +462,67 @@ func TestInternalRelationTuple(t *testing.T) { }) } }) + + t.Run("format=JSON", func(t *testing.T) { + t.Run("direction=encoding-decoding", func(t *testing.T) { + for _, tc := range []struct { + name string + rt *InternalRelationTuple + expected string + }{ + { + name: "with subject ID", + rt: &InternalRelationTuple{ + Namespace: "n", + Object: "o", + Relation: "r", + Subject: &SubjectID{ID: "s"}, + }, + expected: ` +{ + "namespace": "n", + "object": "o", + "relation": "r", + "subject": "s" +}`, + }, + { + name: "with subject set", + rt: &InternalRelationTuple{ + Namespace: "n", + Object: "o", + Relation: "r", + Subject: &SubjectSet{ + Namespace: "sn", + Object: "so", + Relation: "sr", + }, + }, + expected: ` +{ + "namespace": "n", + "object": "o", + "relation": "r", + "subject": { + "namespace": "sn", + "object": "so", + "relation": "sr" + } +}`, + }, + } { + t.Run("case="+tc.name, func(t *testing.T) { + raw, err := json.Marshal(tc.rt) + require.NoError(t, err) + assert.JSONEq(t, tc.expected, string(raw)) + + var dec InternalRelationTuple + require.NoError(t, json.Unmarshal(raw, &dec)) + assert.Equal(t, tc.rt, &dec) + }) + } + }) + }) } func TestRelationQuery(t *testing.T) { diff --git a/internal/relationtuple/transact_server_test.go b/internal/relationtuple/transact_server_test.go index 113e6cfb5..5a7b4b7b9 100644 --- a/internal/relationtuple/transact_server_test.go +++ b/internal/relationtuple/transact_server_test.go @@ -101,7 +101,7 @@ func TestWriteHandlers(t *testing.T) { assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) - t.Run("case=special chars error on creation already", func(t *testing.T) { + t.Run("case=special chars", func(t *testing.T) { nspace := addNamespace(t) rts := []*relationtuple.InternalRelationTuple{ @@ -119,7 +119,7 @@ func TestWriteHandlers(t *testing.T) { Namespace: nspace.Name, Object: "@all", Relation: "member", - Subject: &relationtuple.SubjectID{ID: "this:will#be interpreted:as a@subject set"}, + Subject: &relationtuple.SubjectID{ID: "this:could#be interpreted:as a@subject set"}, }, } @@ -128,8 +128,7 @@ func TestWriteHandlers(t *testing.T) { require.NoError(t, err) resp := doCreate(payload) - assert.GreaterOrEqual(t, resp.StatusCode, http.StatusBadRequest) - assert.Less(t, resp.StatusCode, http.StatusInternalServerError) + assert.Equal(t, http.StatusCreated, resp.StatusCode) } actual, next, err := reg.RelationTupleManager().GetRelationTuples(context.Background(), &relationtuple.RelationQuery{ @@ -137,7 +136,10 @@ func TestWriteHandlers(t *testing.T) { }) require.NoError(t, err) assert.Equal(t, "", next) - assert.Len(t, actual, 0) + assert.Len(t, actual, 2) + for _, rt := range rts { + assert.Contains(t, actual, rt) + } }) }) diff --git a/spec/api.json b/spec/api.json index b47be3bd9..4a3205a0a 100755 --- a/spec/api.json +++ b/spec/api.json @@ -67,7 +67,7 @@ "required": true }, { - "type": "string", + "type": "object", "description": "Subject of the Relation Tuple\n\nThe subject follows the subject string encoding format.", "name": "subject", "in": "query" @@ -519,7 +519,7 @@ "in": "query" }, { - "type": "string", + "type": "object", "name": "subject", "in": "query" }, @@ -743,7 +743,7 @@ "required": true }, { - "type": "string", + "type": "object", "description": "Subject of the Relation Tuple\n\nThe subject follows the subject string encoding format.", "name": "subject", "in": "query" @@ -1088,7 +1088,7 @@ } }, "subject": { - "type": "string" + "type": "object" }, "version": { "type": "object", diff --git a/spec/patches/subjects.yml b/spec/patches/subjects.yml new file mode 100644 index 000000000..73947c52d --- /dev/null +++ b/spec/patches/subjects.yml @@ -0,0 +1,4 @@ +- op: replace + path: /definitions/subject + value: + oneOf: diff --git a/spec/swagger.json b/spec/swagger.json new file mode 100755 index 000000000..04044fc34 --- /dev/null +++ b/spec/swagger.json @@ -0,0 +1,707 @@ +{ + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "swagger": "2.0", + "info": { + "description": "Ory Keto is a cloud native access control server providing best-practice patterns (RBAC, ABAC, ACL, AWS IAM Policies, Kubernetes Roles, ...) via REST APIs.", + "title": "ORY Keto", + "contact": { + "name": "ORY", + "url": "https://www.ory.sh", + "email": "hi@ory.sh" + }, + "license": { + "name": "Apache 2.0", + "url": "https://github.com/ory/keto/blob/master/LICENSE" + }, + "version": "Latest" + }, + "basePath": "/", + "paths": { + "/check": { + "get": { + "description": "To learn how relation tuples and the check works, head over to [the documentation](../concepts/relation-tuples.mdx).", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "read" + ], + "summary": "Check a relation tuple", + "operationId": "getCheck", + "parameters": [ + { + "type": "string", + "description": "Namespace of the Relation Tuple", + "name": "namespace", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Object of the Relation Tuple", + "name": "object", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Relation of the Relation Tuple", + "name": "relation", + "in": "query", + "required": true + }, + { + "$ref": "#/definitions/subject", + "description": "Subject of the Relation Tuple\n\nThe subject follows the subject string encoding format.", + "name": "subject", + "in": "query" + } + ], + "responses": { + "200": { + "description": "getCheckResponse", + "schema": { + "$ref": "#/definitions/getCheckResponse" + } + }, + "400": { + "$ref": "#/responses/genericError" + }, + "403": { + "description": "getCheckResponse", + "schema": { + "$ref": "#/definitions/getCheckResponse" + } + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "post": { + "description": "To learn how relation tuples and the check works, head over to [the documentation](../concepts/relation-tuples.mdx).", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "read" + ], + "summary": "Check a relation tuple", + "operationId": "postCheck", + "parameters": [ + { + "name": "Payload", + "in": "body", + "schema": { + "$ref": "#/definitions/InternalRelationTuple" + } + } + ], + "responses": { + "200": { + "description": "getCheckResponse", + "schema": { + "$ref": "#/definitions/getCheckResponse" + } + }, + "400": { + "$ref": "#/responses/genericError" + }, + "403": { + "description": "getCheckResponse", + "schema": { + "$ref": "#/definitions/getCheckResponse" + } + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/expand": { + "get": { + "description": "Use this endpoint to expand a relation tuple.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "read" + ], + "summary": "Expand a Relation Tuple", + "operationId": "getExpand", + "parameters": [ + { + "type": "string", + "description": "Namespace of the Relation Tuple", + "name": "namespace", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Object of the Relation Tuple", + "name": "object", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Relation of the Relation Tuple", + "name": "relation", + "in": "query", + "required": true + }, + { + "type": "integer", + "format": "int64", + "name": "max-depth", + "in": "query" + } + ], + "responses": { + "200": { + "description": "expandTree", + "schema": { + "$ref": "#/definitions/expandTree" + } + }, + "400": { + "$ref": "#/responses/genericError" + }, + "404": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/health/alive": { + "get": { + "description": "This endpoint returns a 200 status code when the HTTP server is up running.\nThis status does currently not include checks whether the database connection is working.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the health status will never\nrefer to the cluster state, only to a single instance.", + "produces": [ + "application/json" + ], + "tags": [ + "health" + ], + "summary": "Check alive status", + "operationId": "isInstanceAlive", + "responses": { + "200": { + "description": "healthStatus", + "schema": { + "$ref": "#/definitions/healthStatus" + } + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/health/ready": { + "get": { + "description": "This endpoint returns a 200 status code when the HTTP server is up running and the environment dependencies (e.g.\nthe database) are responsive as well.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the health status will never\nrefer to the cluster state, only to a single instance.", + "produces": [ + "application/json" + ], + "tags": [ + "health" + ], + "summary": "Check readiness status", + "operationId": "isInstanceReady", + "responses": { + "200": { + "description": "healthStatus", + "schema": { + "$ref": "#/definitions/healthStatus" + } + }, + "503": { + "description": "healthNotReadyStatus", + "schema": { + "$ref": "#/definitions/healthNotReadyStatus" + } + } + } + } + }, + "/relation-tuples": { + "get": { + "description": "Get all relation tuples that match the query. Only the namespace field is required.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "read" + ], + "summary": "Query relation tuples", + "operationId": "getRelationTuples", + "parameters": [ + { + "type": "string", + "name": "namespace", + "in": "query", + "required": true + }, + { + "type": "string", + "name": "object", + "in": "query" + }, + { + "type": "string", + "name": "relation", + "in": "query" + }, + { + "$ref": "#/definitions/subject", + "name": "subject", + "in": "query" + }, + { + "type": "string", + "name": "page_token", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "name": "page_size", + "in": "query" + } + ], + "responses": { + "200": { + "description": "getRelationTuplesResponse", + "schema": { + "$ref": "#/definitions/getRelationTuplesResponse" + } + }, + "404": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "put": { + "description": "Use this endpoint to create a relation tuple.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "write" + ], + "summary": "Create a Relation Tuple", + "operationId": "createRelationTuple", + "parameters": [ + { + "name": "Payload", + "in": "body", + "schema": { + "$ref": "#/definitions/InternalRelationTuple" + } + } + ], + "responses": { + "201": { + "description": "InternalRelationTuple", + "schema": { + "$ref": "#/definitions/InternalRelationTuple" + } + }, + "400": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "delete": { + "description": "Use this endpoint to delete a relation tuple.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "write" + ], + "summary": "Delete a Relation Tuple", + "operationId": "deleteRelationTuple", + "parameters": [ + { + "type": "string", + "description": "Namespace of the Relation Tuple", + "name": "namespace", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Object of the Relation Tuple", + "name": "object", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Relation of the Relation Tuple", + "name": "relation", + "in": "query", + "required": true + }, + { + "$ref": "#/definitions/subject", + "description": "Subject of the Relation Tuple\n\nThe subject follows the subject string encoding format.", + "name": "subject", + "in": "query" + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "400": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "patch": { + "description": "Use this endpoint to patch one or more relation tuples.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "write" + ], + "summary": "Patch Multiple Relation Tuples", + "operationId": "patchRelationTuples", + "parameters": [ + { + "name": "Payload", + "in": "body", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/PatchDelta" + } + } + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "400": { + "$ref": "#/responses/genericError" + }, + "404": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/version": { + "get": { + "description": "This endpoint returns the service version typically notated using semantic versioning.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the health status will never\nrefer to the cluster state, only to a single instance.", + "produces": [ + "application/json" + ], + "tags": [ + "version" + ], + "summary": "Get service version", + "operationId": "getVersion", + "responses": { + "200": { + "description": "version", + "schema": { + "$ref": "#/definitions/version" + } + } + } + } + } + }, + "definitions": { + "InternalRelationTuple": { + "type": "object", + "required": [ + "namespace", + "object", + "relation", + "subject" + ], + "properties": { + "namespace": { + "description": "Namespace of the Relation Tuple\n\nin: query", + "type": "string" + }, + "object": { + "description": "Object of the Relation Tuple\n\nin: query", + "type": "string" + }, + "relation": { + "description": "Relation of the Relation Tuple\n\nin: query", + "type": "string" + }, + "subject": { + "$ref": "#/definitions/subject" + } + } + }, + "PatchDelta": { + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": [ + "insert", + "delete" + ] + }, + "relation_tuple": { + "$ref": "#/definitions/InternalRelationTuple" + } + } + }, + "expandTree": { + "type": "object", + "required": [ + "type", + "subject" + ], + "properties": { + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/expandTree" + } + }, + "subject": { + "$ref": "#/definitions/subject" + }, + "type": { + "type": "string", + "enum": [ + "union", + "exclusion", + "intersection", + "leaf" + ] + } + } + }, + "genericError": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "code": { + "description": "The status code", + "type": "integer", + "format": "int64", + "example": 404 + }, + "debug": { + "description": "Debug information\n\nThis field is often not exposed to protect against leaking\nsensitive information.", + "type": "string", + "example": "SQL field \"foo\" is not a bool." + }, + "details": { + "description": "Further error details", + "type": "object", + "additionalProperties": true + }, + "message": { + "description": "Error message\n\nThe error's message.", + "type": "string", + "example": "The resource could not be found" + }, + "reason": { + "description": "A human-readable reason for the error", + "type": "string", + "example": "User with ID 1234 does not exist." + }, + "request": { + "description": "The request ID\n\nThe request ID is often exposed internally in order to trace\nerrors across service architectures. This is often a UUID.", + "type": "string", + "example": "d7ef54b1-ec15-46e6-bccb-524b82c035e6" + }, + "status": { + "description": "The status description", + "type": "string", + "example": "Not Found" + } + } + }, + "getCheckResponse": { + "description": "The content of the allowed field is mirrored in the HTTP status code.", + "type": "object", + "title": "Represents the response for a check request.", + "required": [ + "allowed" + ], + "properties": { + "allowed": { + "description": "whether the relation tuple is allowed", + "type": "boolean" + } + } + }, + "getRelationTuplesResponse": { + "type": "object", + "properties": { + "next_page_token": { + "description": "The opaque token to provide in a subsequent request\nto get the next page. It is the empty string iff this is\nthe last page.", + "type": "string" + }, + "relation_tuples": { + "type": "array", + "items": { + "$ref": "#/definitions/InternalRelationTuple" + } + } + } + }, + "healthNotReadyStatus": { + "type": "object", + "properties": { + "errors": { + "description": "Errors contains a list of errors that caused the not ready status.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "healthStatus": { + "type": "object", + "properties": { + "status": { + "description": "Status always contains \"ok\".", + "type": "string" + } + } + }, + "subject": { + "type": "object" + }, + "version": { + "type": "object", + "properties": { + "version": { + "description": "Version is the service's version.", + "type": "string" + } + } + } + ,"UUID":{"type": "string", "format": "uuid4"}}, + "responses": { + "emptyResponse": { + "description": "Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is typically 201." + }, + "genericError": { + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file From b9d5ada934ffde35a7f47a6bd638454dd2efacaa Mon Sep 17 00:00:00 2001 From: zepatrik Date: Wed, 22 Sep 2021 18:01:09 +0200 Subject: [PATCH 2/9] refactor: make subject sets and subject IDs unambiguous --- .schema/relation_tuple.schema.json | 100 ++- Makefile | 16 +- cmd/relationtuple/get.go | 32 +- .../relation-tuples/cats1_owner.json | 6 +- .../relation-tuples/cats1_view_owner.json | 6 +- .../relation-tuples/cats1_view_public.json | 2 +- .../relation-tuples/cats2_owner.json | 6 +- .../relation-tuples/cats2_view.json | 6 +- .../relation-tuples/cats_owner.json | 2 +- .../relation-tuples/cats_view.json | 6 +- docs/docs/cli/keto-check.md | 9 +- docs/docs/cli/keto-expand.md | 6 +- docs/docs/cli/keto-migrate-down.md | 12 +- docs/docs/cli/keto-migrate-status.md | 10 +- docs/docs/cli/keto-migrate-up.md | 10 +- docs/docs/cli/keto-migrate.md | 16 +- docs/docs/cli/keto-namespace-validate.md | 13 +- docs/docs/cli/keto-namespace.md | 9 +- docs/docs/cli/keto-relation-tuple-create.md | 13 +- docs/docs/cli/keto-relation-tuple-delete.md | 16 +- docs/docs/cli/keto-relation-tuple-get.md | 13 +- docs/docs/cli/keto-relation-tuple-parse.md | 13 +- docs/docs/cli/keto-relation-tuple.md | 17 +- docs/docs/cli/keto-serve.md | 11 +- docs/docs/cli/keto-status.md | 9 +- docs/docs/cli/keto-version.md | 6 +- docs/docs/cli/keto.md | 23 +- internal/check/handler.go | 11 +- internal/check/handler_test.go | 20 +- internal/e2e/cases_test.go | 13 +- internal/e2e/cli_client_test.go | 7 +- internal/e2e/grpc_client_test.go | 8 +- internal/e2e/rest_client_test.go | 8 +- ...http_client_test.go => sdk_client_test.go} | 107 ++- internal/expand/handler.go | 3 +- internal/expand/tree.go | 86 +- .../client/read/get_check_parameters.go | 136 +++- .../client/read/get_expand_parameters.go | 6 +- .../read/get_relation_tuples_parameters.go | 192 +++-- .../client/read/post_check_parameters.go | 6 +- .../write/create_relation_tuple_parameters.go | 6 +- .../write/create_relation_tuple_responses.go | 8 +- .../write/delete_relation_tuple_parameters.go | 136 +++- internal/httpclient/models/expand_tree.go | 37 +- .../models/internal_relation_tuple.go | 45 +- internal/httpclient/models/relation_query.go | 129 +++ internal/httpclient/models/subject.go | 27 - internal/httpclient/models/subject_set.go | 105 +++ internal/persistence/sql/persister.go | 3 - internal/persistence/sql/relationtuples.go | 4 +- internal/relationtuple/definitions.go | 310 +++++--- internal/relationtuple/definitions_test.go | 58 +- .../relationtuple/manager_requirements.go | 16 +- internal/relationtuple/read_server.go | 15 +- internal/relationtuple/transact_server.go | 60 +- .../relationtuple/transact_server_test.go | 12 +- spec/api.json | 171 +++- spec/swagger.json | 752 +++++++++++++++--- 58 files changed, 2149 insertions(+), 736 deletions(-) rename internal/e2e/{http_client_test.go => sdk_client_test.go} (67%) create mode 100644 internal/httpclient/models/relation_query.go delete mode 100644 internal/httpclient/models/subject.go create mode 100644 internal/httpclient/models/subject_set.go diff --git a/.schema/relation_tuple.schema.json b/.schema/relation_tuple.schema.json index db3fe5006..80d796d04 100644 --- a/.schema/relation_tuple.schema.json +++ b/.schema/relation_tuple.schema.json @@ -2,52 +2,78 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, - "required": ["namespace", "relation", "object", "subject"], - "properties": { - "$schema": { - "type": "string", - "format": "uri-reference", - "description": "Add this to allow defining the schema, useful for IDE integration" - }, - "namespace": { - "type": "string", - "description": "The namespace of the object and relation in this tuple." - }, - "relation": { - "type": "string", - "description": "The relation of the object and subject." - }, - "object": { - "type": "string", - "description": "The object affected by this relation." + "allOf": [ + { + "required": [ + "namespace", + "relation", + "object" + ], + "properties": { + "$schema": { + "type": "string", + "format": "uri-reference", + "description": "Add this to allow defining the schema, useful for IDE integration" + }, + "namespace": { + "type": "string", + "description": "The namespace of the object and relation in this tuple." + }, + "object": { + "type": "string", + "description": "The object affected by this relation." + }, + "relation": { + "type": "string", + "description": "The relation of the object and subject." + } + } }, - "subject": { + { "oneOf": [ { - "type": "object", - "description": "The subject set affected by this relation.", + "required": [ + "subject_id" + ], "properties": { - "namespace": { - "type": "string", - "description": "The namespace of the object and relation in this subject set." - }, - "relation": { + "subject_id": { "type": "string", - "description": "The relation of this subject set." - }, - "object": { - "type": "string", - "description": "The object referenced in this subject set." + "description": "The subject ID affected by this relation." } - }, - "additionalProperties": false, - "required": ["namespace", "relation", "object"] + } }, { - "type": "string", - "description": "The subject ID affected by this relation." + "required": [ + "subject_set" + ], + "properties": { + "subject_set": { + "type": "object", + "description": "The subject set affected by this relation.", + "properties": { + "namespace": { + "type": "string", + "description": "The namespace of the object and relation in this subject set." + }, + "object": { + "type": "string", + "description": "The object referenced in this subject set." + }, + "relation": { + "type": "string", + "description": "The relation of this subject set." + } + }, + "additionalProperties": false, + "required": [ + "namespace", + "relation", + "object" + ] + } + } } ] } - } + ] } diff --git a/Makefile b/Makefile index 81988bf92..78020820f 100644 --- a/Makefile +++ b/Makefile @@ -50,18 +50,10 @@ docker: # Generates the SDKs .PHONY: sdk sdk: .bin/swagger .bin/ory - swagger generate spec -m -o ./spec/swagger.json -x internal/httpclient -x proto/ory/keto -x docker - ory dev swagger sanitize ./spec/swagger.json - swagger flatten --with-flatten=remove-unused -o ./spec/swagger.json ./spec/swagger.json - swagger validate ./spec/swagger.json - - CIRCLE_PROJECT_USERNAME=ory CIRCLE_PROJECT_REPONAME=kratos \ - ory dev openapi migrate \ - --health-path-tags metadata \ - -p https://raw.githubusercontent.com/ory/x/master/healthx/openapi/patch.yaml \ - -p file://spec/patches/subjects.yml \ - spec/swagger.json spec/api.json - + swagger generate spec -m -o ./spec/api.json -x internal/httpclient -x proto/ory/keto -x docker + ory dev swagger sanitize ./spec/api.json + swagger flatten --with-flatten=remove-unused -o ./spec/api.json ./spec/api.json + swagger validate ./spec/api.json rm -rf internal/httpclient mkdir -p internal/httpclient swagger generate client -f ./spec/api.json -t internal/httpclient -A Ory_Keto diff --git a/cmd/relationtuple/get.go b/cmd/relationtuple/get.go index 2a2932149..045e3df7b 100644 --- a/cmd/relationtuple/get.go +++ b/cmd/relationtuple/get.go @@ -20,36 +20,38 @@ import ( ) const ( - FlagSubject = "subject" - FlagRelation = "relation" - FlagObject = "object" - FlagPageSize = "page-size" - FlagPageToken = "page-token" + FlagSubjectID = "subject-id" + FlagSubjectSet = "subject-set" + FlagRelation = "relation" + FlagObject = "object" + FlagPageSize = "page-size" + FlagPageToken = "page-token" ) func registerRelationTupleFlags(flags *pflag.FlagSet) { - flags.String(FlagSubject, "", "Set the requested subject") + flags.String(FlagSubjectID, "", "Set the requested subject ID") + flags.String(FlagSubjectSet, "", `Set the requested subject set; format: "namespace:object#relation"`) flags.String(FlagRelation, "", "Set the requested relation") flags.String(FlagObject, "", "Set the requested object") } func readQueryFromFlags(cmd *cobra.Command, namespace string) (*acl.ListRelationTuplesRequest_Query, error) { - subject := flagx.MustGetString(cmd, FlagSubject) - relation := flagx.MustGetString(cmd, FlagRelation) - object := flagx.MustGetString(cmd, FlagObject) - query := &acl.ListRelationTuplesRequest_Query{ - Relation: relation, - Object: object, Namespace: namespace, + Object: flagx.MustGetString(cmd, FlagObject), + Relation: flagx.MustGetString(cmd, FlagRelation), } - if subject != "" { - s, err := relationtuple.SubjectFromString(subject) + switch flags := cmd.Flags(); { + case flags.Changed(FlagSubjectID) && flags.Changed(FlagSubjectSet): + return nil, relationtuple.ErrDuplicateSubject + case flags.Changed(FlagSubjectID): + query.Subject = (&relationtuple.SubjectID{ID: flagx.MustGetString(cmd, FlagSubjectID)}).ToProto() + case flags.Changed(FlagSubjectSet): + s, err := (&relationtuple.SubjectSet{}).FromString(flagx.MustGetString(cmd, FlagSubjectSet)) if err != nil { return nil, err } - query.Subject = s.ToProto() } diff --git a/contrib/cat-videos-example/relation-tuples/cats1_owner.json b/contrib/cat-videos-example/relation-tuples/cats1_owner.json index 042d3191a..515bc1665 100644 --- a/contrib/cat-videos-example/relation-tuples/cats1_owner.json +++ b/contrib/cat-videos-example/relation-tuples/cats1_owner.json @@ -3,5 +3,9 @@ "namespace": "videos", "object": "/cats/1.mp4", "relation": "owner", - "subject": "videos:/cats#owner" + "subject_set": { + "namespace": "videos", + "object": "/cats", + "relation": "owner" + } } diff --git a/contrib/cat-videos-example/relation-tuples/cats1_view_owner.json b/contrib/cat-videos-example/relation-tuples/cats1_view_owner.json index eb8c604d3..d48d624b3 100644 --- a/contrib/cat-videos-example/relation-tuples/cats1_view_owner.json +++ b/contrib/cat-videos-example/relation-tuples/cats1_view_owner.json @@ -3,5 +3,9 @@ "namespace": "videos", "object": "/cats/1.mp4", "relation": "view", - "subject": "videos:/cats/1.mp4#owner" + "subject_set": { + "namespace": "videos", + "object": "/cats/1.mp4", + "relation": "owner" + } } diff --git a/contrib/cat-videos-example/relation-tuples/cats1_view_public.json b/contrib/cat-videos-example/relation-tuples/cats1_view_public.json index 008ab1ad3..dfb2b1ee4 100644 --- a/contrib/cat-videos-example/relation-tuples/cats1_view_public.json +++ b/contrib/cat-videos-example/relation-tuples/cats1_view_public.json @@ -3,5 +3,5 @@ "namespace": "videos", "object": "/cats/1.mp4", "relation": "view", - "subject": "*" + "subject_id": "*" } diff --git a/contrib/cat-videos-example/relation-tuples/cats2_owner.json b/contrib/cat-videos-example/relation-tuples/cats2_owner.json index a3ad91ec6..3702371e9 100644 --- a/contrib/cat-videos-example/relation-tuples/cats2_owner.json +++ b/contrib/cat-videos-example/relation-tuples/cats2_owner.json @@ -3,5 +3,9 @@ "namespace": "videos", "object": "/cats/2.mp4", "relation": "owner", - "subject": "videos:/cats#owner" + "subject_set": { + "namespace": "videos", + "object": "/cats", + "relation": "owner" + } } diff --git a/contrib/cat-videos-example/relation-tuples/cats2_view.json b/contrib/cat-videos-example/relation-tuples/cats2_view.json index 8531f904e..e72f20e1e 100644 --- a/contrib/cat-videos-example/relation-tuples/cats2_view.json +++ b/contrib/cat-videos-example/relation-tuples/cats2_view.json @@ -3,5 +3,9 @@ "namespace": "videos", "object": "/cats/2.mp4", "relation": "view", - "subject": "videos:/cats/2.mp4#owner" + "subject_set": { + "namespace": "videos", + "object": "/cats/2.mp4", + "relation": "owner" + } } diff --git a/contrib/cat-videos-example/relation-tuples/cats_owner.json b/contrib/cat-videos-example/relation-tuples/cats_owner.json index 5859a71d5..4486fb822 100644 --- a/contrib/cat-videos-example/relation-tuples/cats_owner.json +++ b/contrib/cat-videos-example/relation-tuples/cats_owner.json @@ -3,5 +3,5 @@ "namespace": "videos", "object": "/cats", "relation": "owner", - "subject": "cat lady" + "subject_id": "cat lady" } diff --git a/contrib/cat-videos-example/relation-tuples/cats_view.json b/contrib/cat-videos-example/relation-tuples/cats_view.json index d07257e90..6dfbd07c4 100644 --- a/contrib/cat-videos-example/relation-tuples/cats_view.json +++ b/contrib/cat-videos-example/relation-tuples/cats_view.json @@ -3,5 +3,9 @@ "namespace": "videos", "object": "/cats", "relation": "view", - "subject": "videos:/cats#owner" + "subject_set": { + "namespace": "videos", + "object": "/cats", + "relation": "owner" + } } diff --git a/docs/docs/cli/keto-check.md b/docs/docs/cli/keto-check.md index 01eaf616e..610b734aa 100644 --- a/docs/docs/cli/keto-check.md +++ b/docs/docs/cli/keto-check.md @@ -9,15 +9,13 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto check Check whether a subject has a relation on an object ### Synopsis -Check whether a subject has a relation on an object. This method resolves -subject sets and subject set rewrites. +Check whether a subject has a relation on an object. This method resolves subject sets and subject set rewrites. ``` keto check <subject> <relation> <namespace> <object> [flags] @@ -36,9 +34,10 @@ keto check <subject> <relation> <namespace> <object> [fl ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto](keto) - Global and consistent permission and authorization server +* [keto](keto) - Global and consistent permission and authorization server + diff --git a/docs/docs/cli/keto-expand.md b/docs/docs/cli/keto-expand.md index 8f9ac06c1..bcd2c6ffe 100644 --- a/docs/docs/cli/keto-expand.md +++ b/docs/docs/cli/keto-expand.md @@ -9,7 +9,6 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto expand Expand a subject set @@ -36,9 +35,10 @@ keto expand <relation> <namespace> <object> [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto](keto) - Global and consistent permission and authorization server +* [keto](keto) - Global and consistent permission and authorization server + diff --git a/docs/docs/cli/keto-migrate-down.md b/docs/docs/cli/keto-migrate-down.md index 466ca27fd..d75edc5bf 100644 --- a/docs/docs/cli/keto-migrate-down.md +++ b/docs/docs/cli/keto-migrate-down.md @@ -9,16 +9,15 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto migrate down Migrate the database down ### Synopsis -Migrate the database down a specific amount of steps. Pass 0 steps to fully -migrate down. This does not affect namespaces. Use `keto namespace migrate down` -for migrating namespaces. +Migrate the database down a specific amount of steps. +Pass 0 steps to fully migrate down. +This does not affect namespaces. Use `keto namespace migrate down` for migrating namespaces. ``` keto migrate down <steps> [flags] @@ -36,9 +35,10 @@ keto migrate down <steps> [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto migrate](keto-migrate) - Commands to migrate the database +* [keto migrate](keto-migrate) - Commands to migrate the database + diff --git a/docs/docs/cli/keto-migrate-status.md b/docs/docs/cli/keto-migrate-status.md index 154640dd1..d5581b667 100644 --- a/docs/docs/cli/keto-migrate-status.md +++ b/docs/docs/cli/keto-migrate-status.md @@ -9,15 +9,14 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto migrate status Get the current migration status ### Synopsis -Get the current migration status. This does not affect namespaces. Use -`keto namespace migrate status` for migrating namespaces. +Get the current migration status. +This does not affect namespaces. Use `keto namespace migrate status` for migrating namespaces. ``` keto migrate status [flags] @@ -34,9 +33,10 @@ keto migrate status [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto migrate](keto-migrate) - Commands to migrate the database +* [keto migrate](keto-migrate) - Commands to migrate the database + diff --git a/docs/docs/cli/keto-migrate-up.md b/docs/docs/cli/keto-migrate-up.md index 0e90f2c4c..082a7f611 100644 --- a/docs/docs/cli/keto-migrate-up.md +++ b/docs/docs/cli/keto-migrate-up.md @@ -9,15 +9,14 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto migrate up Migrate the database up ### Synopsis -Migrate the database up. This does not affect namespaces. Use -`keto namespace migrate up` for migrating namespaces. +Migrate the database up. +This does not affect namespaces. Use `keto namespace migrate up` for migrating namespaces. ``` keto migrate up [flags] @@ -36,9 +35,10 @@ keto migrate up [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto migrate](keto-migrate) - Commands to migrate the database +* [keto migrate](keto-migrate) - Commands to migrate the database + diff --git a/docs/docs/cli/keto-migrate.md b/docs/docs/cli/keto-migrate.md index a06fac2de..6889bf96b 100644 --- a/docs/docs/cli/keto-migrate.md +++ b/docs/docs/cli/keto-migrate.md @@ -9,15 +9,14 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto migrate Commands to migrate the database ### Synopsis -Commands to migrate the database. This does not affect namespaces. Use -`keto namespace migrate` for migrating namespaces. +Commands to migrate the database. +This does not affect namespaces. Use `keto namespace migrate` for migrating namespaces. ### Options @@ -28,12 +27,13 @@ Commands to migrate the database. This does not affect namespaces. Use ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto](keto) - Global and consistent permission and authorization server -- [keto migrate down](keto-migrate-down) - Migrate the database down -- [keto migrate status](keto-migrate-status) - Get the current migration status -- [keto migrate up](keto-migrate-up) - Migrate the database up +* [keto](keto) - Global and consistent permission and authorization server +* [keto migrate down](keto-migrate-down) - Migrate the database down +* [keto migrate status](keto-migrate-status) - Get the current migration status +* [keto migrate up](keto-migrate-up) - Migrate the database up + diff --git a/docs/docs/cli/keto-namespace-validate.md b/docs/docs/cli/keto-namespace-validate.md index d98d246c2..52548ade3 100644 --- a/docs/docs/cli/keto-namespace-validate.md +++ b/docs/docs/cli/keto-namespace-validate.md @@ -9,16 +9,16 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto namespace validate Validate namespace definitions ### Synopsis -validate Validates namespace definitions. Parses namespace yaml files or -configuration files passed via the configuration flag. Returns human readable -errors. Useful for debugging. +validate +Validates namespace definitions. Parses namespace yaml files or configuration +files passed via the configuration flag. Returns human readable errors. Useful for +debugging. ``` keto namespace validate <namespace.yml> [<namespace2.yml> ...] | validate -c <config.yaml> [flags] @@ -33,9 +33,10 @@ keto namespace validate <namespace.yml> [<namespace2.yml> ...] | val ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto namespace](keto-namespace) - Read and manipulate namespaces +* [keto namespace](keto-namespace) - Read and manipulate namespaces + diff --git a/docs/docs/cli/keto-namespace.md b/docs/docs/cli/keto-namespace.md index 5f7c6b20b..b2d85140e 100644 --- a/docs/docs/cli/keto-namespace.md +++ b/docs/docs/cli/keto-namespace.md @@ -9,7 +9,6 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto namespace Read and manipulate namespaces @@ -23,11 +22,11 @@ Read and manipulate namespaces ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto](keto) - Global and consistent permission and authorization server -- [keto namespace validate](keto-namespace-validate) - Validate namespace - definitions +* [keto](keto) - Global and consistent permission and authorization server +* [keto namespace validate](keto-namespace-validate) - Validate namespace definitions + diff --git a/docs/docs/cli/keto-relation-tuple-create.md b/docs/docs/cli/keto-relation-tuple-create.md index 4c67d1754..c288e7e01 100644 --- a/docs/docs/cli/keto-relation-tuple-create.md +++ b/docs/docs/cli/keto-relation-tuple-create.md @@ -9,16 +9,15 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto relation-tuple create Create relation tuples from JSON files ### Synopsis -Create relation tuples from JSON files. A directory will be traversed and all -relation tuples will be created. Pass the special filename `-` to read from -STD_IN. +Create relation tuples from JSON files. +A directory will be traversed and all relation tuples will be created. +Pass the special filename `-` to read from STD_IN. ``` keto relation-tuple create <relation-tuple.json> [<relation-tuple-dir>] [flags] @@ -37,10 +36,10 @@ keto relation-tuple create <relation-tuple.json> [<relation-tuple-dir&g ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation - tuples +* [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation tuples + diff --git a/docs/docs/cli/keto-relation-tuple-delete.md b/docs/docs/cli/keto-relation-tuple-delete.md index ac9c88677..3af8bf726 100644 --- a/docs/docs/cli/keto-relation-tuple-delete.md +++ b/docs/docs/cli/keto-relation-tuple-delete.md @@ -1,8 +1,7 @@ --- id: keto-relation-tuple-delete title: keto relation-tuple delete -description: - keto relation-tuple delete Delete relation tuples defined in JSON files +description: keto relation-tuple delete Delete relation tuples defined in JSON files --- - ## keto relation-tuple delete Delete relation tuples defined in JSON files ### Synopsis -Delete relation tuples defined in the given JSON files. A directory will be -traversed and all relation tuples will be deleted. Pass the special filename `-` -to read from STD_IN. +Delete relation tuples defined in the given JSON files. +A directory will be traversed and all relation tuples will be deleted. +Pass the special filename `-` to read from STD_IN. ``` keto relation-tuple delete <relation-tuple.json> [<relation-tuple-dir>] [flags] @@ -38,10 +36,10 @@ keto relation-tuple delete <relation-tuple.json> [<relation-tuple-dir&g ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation - tuples +* [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation tuples + diff --git a/docs/docs/cli/keto-relation-tuple-get.md b/docs/docs/cli/keto-relation-tuple-get.md index bd031bec3..78c83202f 100644 --- a/docs/docs/cli/keto-relation-tuple-get.md +++ b/docs/docs/cli/keto-relation-tuple-get.md @@ -9,14 +9,14 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto relation-tuple get Get relation tuples ### Synopsis -Get relation tuples matching the given partial tuple. Returns paginated results. +Get relation tuples matching the given partial tuple. +Returns paginated results. ``` keto relation-tuple get <namespace> [flags] @@ -33,17 +33,18 @@ keto relation-tuple get <namespace> [flags] -q, --quiet Be quiet with output printing. --read-remote string Remote URL of the read API endpoint. (default "127.0.0.1:4466") --relation string Set the requested relation - --subject string Set the requested subject + --subject-id string Set the requested subject ID + --subject-set string Set the requested subject set; format: "namespace:object#relation" --write-remote string Remote URL of the write API endpoint. (default "127.0.0.1:4467") ``` ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation - tuples +* [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation tuples + diff --git a/docs/docs/cli/keto-relation-tuple-parse.md b/docs/docs/cli/keto-relation-tuple-parse.md index 7503679fc..c9c1f1e68 100644 --- a/docs/docs/cli/keto-relation-tuple-parse.md +++ b/docs/docs/cli/keto-relation-tuple-parse.md @@ -9,16 +9,15 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto relation-tuple parse Parse human readable relation tuples ### Synopsis -Parse human readable relation tuples as used in the documentation. Supports -various output formats. Especially useful for piping into other commands by -using `--format json`. Ignores comments (starting with `//`) and blank lines. +Parse human readable relation tuples as used in the documentation. +Supports various output formats. Especially useful for piping into other commands by using `--format json`. +Ignores comments (starting with `//`) and blank lines. ``` keto relation-tuple parse [flags] @@ -35,10 +34,10 @@ keto relation-tuple parse [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation - tuples +* [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation tuples + diff --git a/docs/docs/cli/keto-relation-tuple.md b/docs/docs/cli/keto-relation-tuple.md index dbd5e41a2..6f32be3ac 100644 --- a/docs/docs/cli/keto-relation-tuple.md +++ b/docs/docs/cli/keto-relation-tuple.md @@ -9,7 +9,6 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto relation-tuple Read and manipulate relation tuples @@ -23,16 +22,14 @@ Read and manipulate relation tuples ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto](keto) - Global and consistent permission and authorization server -- [keto relation-tuple create](keto-relation-tuple-create) - Create relation - tuples from JSON files -- [keto relation-tuple delete](keto-relation-tuple-delete) - Delete relation - tuples defined in JSON files -- [keto relation-tuple get](keto-relation-tuple-get) - Get relation tuples -- [keto relation-tuple parse](keto-relation-tuple-parse) - Parse human readable - relation tuples +* [keto](keto) - Global and consistent permission and authorization server +* [keto relation-tuple create](keto-relation-tuple-create) - Create relation tuples from JSON files +* [keto relation-tuple delete](keto-relation-tuple-delete) - Delete relation tuples defined in JSON files +* [keto relation-tuple get](keto-relation-tuple-get) - Get relation tuples +* [keto relation-tuple parse](keto-relation-tuple-parse) - Parse human readable relation tuples + diff --git a/docs/docs/cli/keto-serve.md b/docs/docs/cli/keto-serve.md index dd37544e9..7853495ed 100644 --- a/docs/docs/cli/keto-serve.md +++ b/docs/docs/cli/keto-serve.md @@ -9,7 +9,6 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto serve Starts the server and serves the HTTP REST and gRPC APIs @@ -20,9 +19,8 @@ This command opens the network ports and listens to HTTP and gRPC API requests. ## Configuration -ORY Keto can be configured using environment variables as well as a -configuration file. For more information on configuration options, open the -configuration documentation: +ORY Keto can be configured using environment variables as well as a configuration file. For more information +on configuration options, open the configuration documentation: >> https://www.ory.sh/keto/docs/reference/configuration << @@ -41,9 +39,10 @@ keto serve [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto](keto) - Global and consistent permission and authorization server +* [keto](keto) - Global and consistent permission and authorization server + diff --git a/docs/docs/cli/keto-status.md b/docs/docs/cli/keto-status.md index f50281060..e659431be 100644 --- a/docs/docs/cli/keto-status.md +++ b/docs/docs/cli/keto-status.md @@ -9,15 +9,13 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto status Get the status of the upstream Keto instance ### Synopsis -Get a status report about the upstream Keto instance. Can also block until the -service is healthy. +Get a status report about the upstream Keto instance. Can also block until the service is healthy. ``` keto status [flags] @@ -37,9 +35,10 @@ keto status [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto](keto) - Global and consistent permission and authorization server +* [keto](keto) - Global and consistent permission and authorization server + diff --git a/docs/docs/cli/keto-version.md b/docs/docs/cli/keto-version.md index 99b146c84..5b223e8f9 100644 --- a/docs/docs/cli/keto-version.md +++ b/docs/docs/cli/keto-version.md @@ -9,7 +9,6 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto version Show the build version, build time, and git hash @@ -27,9 +26,10 @@ keto version [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) ``` ### SEE ALSO -- [keto](keto) - Global and consistent permission and authorization server +* [keto](keto) - Global and consistent permission and authorization server + diff --git a/docs/docs/cli/keto.md b/docs/docs/cli/keto.md index 071746245..895cd0203 100644 --- a/docs/docs/cli/keto.md +++ b/docs/docs/cli/keto.md @@ -9,7 +9,6 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> - ## keto Global and consistent permission and authorization server @@ -17,20 +16,18 @@ Global and consistent permission and authorization server ### Options ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) -h, --help help for keto ``` ### SEE ALSO -- [keto check](keto-check) - Check whether a subject has a relation on an object -- [keto expand](keto-expand) - Expand a subject set -- [keto migrate](keto-migrate) - Commands to migrate the database -- [keto namespace](keto-namespace) - Read and manipulate namespaces -- [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation - tuples -- [keto serve](keto-serve) - Starts the server and serves the HTTP REST and gRPC - APIs -- [keto status](keto-status) - Get the status of the upstream Keto instance -- [keto version](keto-version) - Show the build version, build time, and git - hash +* [keto check](keto-check) - Check whether a subject has a relation on an object +* [keto expand](keto-expand) - Expand a subject set +* [keto migrate](keto-migrate) - Commands to migrate the database +* [keto namespace](keto-namespace) - Read and manipulate namespaces +* [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation tuples +* [keto serve](keto-serve) - Starts the server and serves the HTTP REST and gRPC APIs +* [keto status](keto-status) - Get the status of the upstream Keto instance +* [keto version](keto-version) - Show the build version, build time, and git hash + diff --git a/internal/check/handler.go b/internal/check/handler.go index 8eb6b5ae9..52750b3d7 100644 --- a/internal/check/handler.go +++ b/internal/check/handler.go @@ -84,16 +84,15 @@ type RESTResponse struct { // 500: genericError func (h *Handler) getCheck(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { tuple, err := (&relationtuple.InternalRelationTuple{}).FromURLQuery(r.URL.Query()) - if err != nil { - h.d.Writer().WriteError(w, r, herodot.ErrBadRequest.WithError(err.Error())) - return - } - - if tuple.Subject == nil { + if errors.Is(err, relationtuple.ErrNilSubject) { h.d.Writer().WriteError(w, r, herodot.ErrBadRequest.WithReason("Subject has to be specified.")) return + } else if err != nil { + h.d.Writer().WriteError(w, r, herodot.ErrBadRequest.WithError(err.Error())) + return } + h.d.Logger().Warn("checking against engine\n\n\n") allowed, err := h.d.PermissionEngine().SubjectIsAllowed(r.Context(), tuple) if err != nil { h.d.Writer().WriteError(w, r, err) diff --git a/internal/check/handler_test.go b/internal/check/handler_test.go index 541131c71..3612cae60 100644 --- a/internal/check/handler_test.go +++ b/internal/check/handler_test.go @@ -23,18 +23,18 @@ import ( ) func assertAllowed(t *testing.T, resp *http.Response) { - assert.Equal(t, http.StatusOK, resp.StatusCode) - body, err := io.ReadAll(resp.Body) require.NoError(t, err) + + assert.Equal(t, http.StatusOK, resp.StatusCode, "%s", body) assert.True(t, gjson.GetBytes(body, "allowed").Bool()) } func assertDenied(t *testing.T, resp *http.Response) { - assert.Equal(t, http.StatusForbidden, resp.StatusCode) - body, err := io.ReadAll(resp.Body) require.NoError(t, err) + + assert.Equal(t, http.StatusForbidden, resp.StatusCode, "%s", body) assert.False(t, gjson.GetBytes(body, "allowed").Bool()) } @@ -72,8 +72,8 @@ func TestRESTHandler(t *testing.T) { t.Run("case=returns denied on unknown namespace", func(t *testing.T) { resp, err := ts.Client().Get(ts.URL + check.RouteBase + "?" + url.Values{ - "namespace": {"not " + nspaces[0].Name}, - "subject": {"foo"}, + "namespace": {"not " + nspaces[0].Name}, + "subject_id": {"foo"}, }.Encode()) require.NoError(t, err) @@ -89,7 +89,9 @@ func TestRESTHandler(t *testing.T) { } require.NoError(t, reg.RelationTupleManager().WriteRelationTuples(context.Background(), rt)) - resp, err := ts.Client().Get(ts.URL + check.RouteBase + "?" + rt.ToURLQuery().Encode()) + q, err := rt.ToURLQuery() + require.NoError(t, err) + resp, err := ts.Client().Get(ts.URL + check.RouteBase + "?" + q.Encode()) require.NoError(t, err) assertAllowed(t, resp) @@ -97,8 +99,8 @@ func TestRESTHandler(t *testing.T) { t.Run("case=returns denied", func(t *testing.T) { resp, err := ts.Client().Get(ts.URL + check.RouteBase + "?" + url.Values{ - "namespace": {nspaces[0].Name}, - "subject": {"foo"}, + "namespace": {nspaces[0].Name}, + "subject_id": {"foo"}, }.Encode()) require.NoError(t, err) diff --git a/internal/e2e/cases_test.go b/internal/e2e/cases_test.go index 3ce004906..826072d12 100644 --- a/internal/e2e/cases_test.go +++ b/internal/e2e/cases_test.go @@ -5,6 +5,8 @@ import ( "strconv" "testing" + "github.com/stretchr/testify/require" + "github.com/ory/herodot" "github.com/ory/keto/internal/x" @@ -42,7 +44,8 @@ func runCases(c client, addNamespace func(*testing.T, ...*namespace.Namespace)) c.createTuple(t, tuple) resp := c.queryTuple(t, &relationtuple.RelationQuery{Namespace: tuple.Namespace}) - assert.Contains(t, resp.RelationTuples, tuple) + require.Len(t, resp.RelationTuples, 1) + assert.Equal(t, tuple, resp.RelationTuples[0]) // try the check API to see whether the tuple is interpreted correctly assert.True(t, c.check(t, tuple)) @@ -85,8 +88,8 @@ func runCases(c client, addNamespace func(*testing.T, ...*namespace.Namespace)) assert.Equal(t, expectedTree.Subject, actualTree.Subject) assert.Equal(t, len(expectedTree.Children), len(actualTree.Children), "expected: %+v; actual: %+v", expectedTree.Children, actualTree.Children) - for _, child := range expectedTree.Children { - assert.Contains(t, actualTree.Children, child) + for i, child := range expectedTree.Children { + assert.Contains(t, actualTree.Children, child, "%+v %+v", actualTree.Children[i], child) } }) @@ -147,12 +150,12 @@ func runCases(c client, addNamespace func(*testing.T, ...*namespace.Namespace)) } c.createTuple(t, rt) - resp := c.queryTuple(t, (*relationtuple.RelationQuery)(rt)) + resp := c.queryTuple(t, rt.ToQuery()) assert.Equal(t, []*relationtuple.InternalRelationTuple{rt}, resp.RelationTuples) c.deleteTuple(t, rt) - resp = c.queryTuple(t, (*relationtuple.RelationQuery)(rt)) + resp = c.queryTuple(t, rt.ToQuery()) assert.Len(t, resp.RelationTuples, 0) }) } diff --git a/internal/e2e/cli_client_test.go b/internal/e2e/cli_client_test.go index e5941d0d4..fa43910fd 100644 --- a/internal/e2e/cli_client_test.go +++ b/internal/e2e/cli_client_test.go @@ -47,8 +47,11 @@ func (g *cliClient) createTuple(t require.TestingT, r *relationtuple.InternalRel func (g *cliClient) assembleQueryFlags(q *relationtuple.RelationQuery, opts []x.PaginationOptionSetter) []string { var flags []string - if q.Subject != nil { - flags = append(flags, "--"+clirelationtuple.FlagSubject, q.Subject.String()) + if q.SubjectID != nil { + flags = append(flags, "--"+clirelationtuple.FlagSubjectID, *q.SubjectID) + } + if q.SubjectSet != nil { + flags = append(flags, "--"+clirelationtuple.FlagSubjectSet, q.SubjectSet.String()) } if q.Relation != "" { flags = append(flags, "--"+clirelationtuple.FlagRelation, q.Relation) diff --git a/internal/e2e/grpc_client_test.go b/internal/e2e/grpc_client_test.go index dc325f2fd..e4f035abe 100644 --- a/internal/e2e/grpc_client_test.go +++ b/internal/e2e/grpc_client_test.go @@ -62,8 +62,8 @@ func (g *grpcClient) queryTuple(t require.TestingT, q *relationtuple.RelationQue Object: q.Object, Relation: q.Relation, } - if q.Subject != nil { - query.Subject = q.Subject.ToProto() + if s := q.Subject(); s != nil { + query.Subject = s.ToProto() } pagination := x.GetPaginationOptions(opts...) @@ -95,8 +95,8 @@ func (g *grpcClient) queryTupleErr(t require.TestingT, expected herodot.DefaultE Object: q.Object, Relation: q.Relation, } - if q.Subject != nil { - query.Subject = q.Subject.ToProto() + if s := q.Subject(); s != nil { + query.Subject = s.ToProto() } pagination := x.GetPaginationOptions(opts...) diff --git a/internal/e2e/rest_client_test.go b/internal/e2e/rest_client_test.go index eee1c59f5..e16115ae4 100644 --- a/internal/e2e/rest_client_test.go +++ b/internal/e2e/rest_client_test.go @@ -63,7 +63,9 @@ func (rc *restClient) createTuple(t require.TestingT, r *relationtuple.InternalR } func (rc *restClient) deleteTuple(t require.TestingT, r *relationtuple.InternalRelationTuple) { - body, code := rc.makeRequest(t, http.MethodDelete, relationtuple.RouteBase+"?"+r.ToURLQuery().Encode(), "", true) + q, err := r.ToURLQuery() + require.NoError(t, err) + body, code := rc.makeRequest(t, http.MethodDelete, relationtuple.RouteBase+"?"+q.Encode(), "", true) require.Equal(t, http.StatusNoContent, code, body) } @@ -107,7 +109,9 @@ func (rc *restClient) queryTupleErr(t require.TestingT, expected herodot.Default } func (rc *restClient) check(t require.TestingT, r *relationtuple.InternalRelationTuple) bool { - bodyGet, codeGet := rc.makeRequest(t, http.MethodGet, fmt.Sprintf("%s?%s", check.RouteBase, r.ToURLQuery().Encode()), "", false) + q, err := r.ToURLQuery() + require.NoError(t, err) + bodyGet, codeGet := rc.makeRequest(t, http.MethodGet, fmt.Sprintf("%s?%s", check.RouteBase, q.Encode()), "", false) var respGet check.RESTResponse require.NoError(t, json.Unmarshal([]byte(bodyGet), &respGet)) diff --git a/internal/e2e/http_client_test.go b/internal/e2e/sdk_client_test.go similarity index 67% rename from internal/e2e/http_client_test.go rename to internal/e2e/sdk_client_test.go index 6f2b06ba8..ca136a73d 100644 --- a/internal/e2e/http_client_test.go +++ b/internal/e2e/sdk_client_test.go @@ -52,39 +52,64 @@ func (c *sdkClient) getWriteClient() *httpclient.OryKeto { } func (c *sdkClient) createTuple(t require.TestingT, r *relationtuple.InternalRelationTuple) { + payload := &models.RelationQuery{ + Namespace: &r.Namespace, + Object: r.Object, + Relation: r.Relation, + } + switch s := r.Subject.(type) { + case *relationtuple.SubjectID: + payload.SubjectID = s.ID + case *relationtuple.SubjectSet: + payload.SubjectSet = &models.SubjectSet{ + Namespace: &s.Namespace, + Object: &s.Object, + Relation: &s.Relation, + } + } + _, err := c.getWriteClient().Write.CreateRelationTuple( write.NewCreateRelationTupleParamsWithTimeout(time.Second). - WithPayload(&models.InternalRelationTuple{ - Namespace: &r.Namespace, - Object: &r.Object, - Relation: &r.Relation, - Subject: (*models.Subject)(pointerx.String(r.Subject.String())), - }), + WithPayload(payload), ) require.NoError(t, err) } func (c *sdkClient) deleteTuple(t require.TestingT, r *relationtuple.InternalRelationTuple) { - _, err := c.getWriteClient().Write.DeleteRelationTuple( - write.NewDeleteRelationTupleParamsWithTimeout(time.Second). - WithNamespace(r.Namespace). - WithObject(r.Object). - WithRelation(r.Relation). - WithSubject(pointerx.String(r.Subject.String())), - ) + params := write.NewDeleteRelationTupleParamsWithTimeout(time.Second). + WithNamespace(r.Namespace). + WithObject(r.Object). + WithRelation(r.Relation) + switch s := r.Subject.(type) { + case *relationtuple.SubjectID: + params = params.WithSubjectID(&s.ID) + case *relationtuple.SubjectSet: + params = params. + WithSubjectSetNamespace(&s.Namespace). + WithSubjectSetObject(&s.Object). + WithSubjectSetRelation(&s.Relation) + } + + _, err := c.getWriteClient().Write.DeleteRelationTuple(params) require.NoError(t, err) } func compileParams(q *relationtuple.RelationQuery, opts []x.PaginationOptionSetter) *read.GetRelationTuplesParams { params := read.NewGetRelationTuplesParams().WithNamespace(q.Namespace) if q.Relation != "" { - params = params.WithRelation(&q.Relation) + params = params.WithRelation(q.Relation) } if q.Object != "" { - params = params.WithObject(&q.Object) + params = params.WithObject(q.Object) + } + if q.SubjectID != nil { + params = params.WithSubjectID(q.SubjectID) } - if q.Subject != nil { - params = params.WithSubject(pointerx.String(q.Subject.String())) + if q.SubjectSet != nil { + params = params. + WithSubjectSetNamespace(&q.SubjectSet.Namespace). + WithSubjectSetObject(&q.SubjectSet.Object). + WithSubjectSetRelation(&q.SubjectSet.Relation) } pagination := x.GetPaginationOptions(opts...) @@ -108,13 +133,19 @@ func (c *sdkClient) queryTuple(t require.TestingT, q *relationtuple.RelationQuer } for i, rt := range resp.Payload.RelationTuples { - sub, err := relationtuple.SubjectFromString(string(*rt.Subject)) - require.NoError(t, err) getResp.RelationTuples[i] = &relationtuple.InternalRelationTuple{ Namespace: *rt.Namespace, Object: *rt.Object, Relation: *rt.Relation, - Subject: sub, + } + if rt.SubjectSet != nil { + getResp.RelationTuples[i].Subject = &relationtuple.SubjectSet{ + Namespace: *rt.SubjectSet.Namespace, + Object: *rt.SubjectSet.Object, + Relation: *rt.SubjectSet.Relation, + } + } else { + getResp.RelationTuples[i].Subject = &relationtuple.SubjectID{ID: rt.SubjectID} } } @@ -135,24 +166,38 @@ func (c *sdkClient) queryTupleErr(t require.TestingT, expected herodot.DefaultEr } func (c *sdkClient) check(t require.TestingT, r *relationtuple.InternalRelationTuple) bool { - resp, err := c.getReadClient().Read.GetCheck( - read.NewGetCheckParamsWithTimeout(time.Second). - WithNamespace(r.Namespace). - WithObject(r.Object). - WithRelation(r.Relation). - WithSubject(pointerx.String(r.Subject.String())), - ) + params := read.NewGetCheckParamsWithTimeout(time.Second). + WithNamespace(r.Namespace). + WithObject(r.Object). + WithRelation(r.Relation) + switch s := r.Subject.(type) { + case *relationtuple.SubjectID: + params = params.WithSubjectID(&s.ID) + case *relationtuple.SubjectSet: + params = params. + WithSubjectSetNamespace(&s.Namespace). + WithSubjectSetObject(&s.Object). + WithSubjectSetRelation(&s.Relation) + } + resp, err := c.getReadClient().Read.GetCheck(params) require.NoError(t, err) return *resp.Payload.Allowed } func buildTree(t require.TestingT, mt *models.ExpandTree) *expand.Tree { - sub, err := relationtuple.SubjectFromString(string(*mt.Subject)) - require.NoError(t, err) et := &expand.Tree{ - Type: expand.NodeType(*mt.Type), - Subject: sub, + Type: expand.NodeType(*mt.Type), } + if mt.SubjectSet != nil { + et.Subject = &relationtuple.SubjectSet{ + Namespace: *mt.SubjectSet.Namespace, + Object: *mt.SubjectSet.Object, + Relation: *mt.SubjectSet.Relation, + } + } else { + et.Subject = &relationtuple.SubjectID{ID: mt.SubjectID} + } + if et.Type != expand.Leaf && len(mt.Children) != 0 { et.Children = make([]*expand.Tree, len(mt.Children)) for i, c := range mt.Children { diff --git a/internal/expand/handler.go b/internal/expand/handler.go index 53fefb4c0..ceb5b1c05 100644 --- a/internal/expand/handler.go +++ b/internal/expand/handler.go @@ -52,7 +52,8 @@ func (h *handler) RegisterWriteGRPC(s *grpc.Server) {} // swagger:parameters getExpand // nolint:deadcode,unused type getExpandRequest struct { - Depth int `json:"max-depth"` + // in:query + MaxDepth int `json:"max-depth"` } // swagger:route GET /expand read getExpand diff --git a/internal/expand/tree.go b/internal/expand/tree.go index 87af39057..deeec891e 100644 --- a/internal/expand/tree.go +++ b/internal/expand/tree.go @@ -22,11 +22,9 @@ const ( Leaf NodeType = "leaf" ) -// swagger:model expandTree +// swagger:ignore type Tree struct { - // required: true - Type NodeType `json:"type"` - // required: true + Type NodeType `json:"type"` Subject relationtuple.Subject `json:"subject"` Children []*Tree `json:"children,omitempty"` } @@ -83,30 +81,86 @@ func NodeTypeFromProto(t acl.NodeType) NodeType { return Leaf } -func (t *Tree) UnmarshalJSON(v []byte) error { - type node struct { - Type NodeType `json:"type"` - Children []*Tree `json:"children,omitempty"` - Subject json.RawMessage `json:"subject"` +// swagger:model expandTree +type node struct { + // required: true + Type NodeType `json:"type"` + Children []*node `json:"children"` + SubjectID *string `json:"subject_id,omitempty"` + SubjectSet *relationtuple.SubjectSet `json:"subject_set,omitempty"` +} + +func (n *node) toTree() (*Tree, error) { + t := &Tree{} + if n.SubjectID == nil && n.SubjectSet == nil { + return nil, errors.WithStack(relationtuple.ErrNilSubject) + } else if n.SubjectID != nil && n.SubjectSet != nil { + return nil, errors.WithStack(relationtuple.ErrDuplicateSubject) + } + + if n.SubjectID != nil { + t.Subject = &relationtuple.SubjectID{ID: *n.SubjectID} + } else { + t.Subject = n.SubjectSet + } + + t.Type = n.Type + + if n.Children != nil { + t.Children = make([]*Tree, len(n.Children)) + for i := range n.Children { + var err error + t.Children[i], err = n.Children[i].toTree() + if err != nil { + return nil, err + } + } } - n := &node{} - if err := json.Unmarshal(v, n); err != nil { + return t, nil +} + +func (n *node) fromTree(t *Tree) error { + n.Type = t.Type + n.SubjectID = t.Subject.SubjectID() + n.SubjectSet = t.Subject.SubjectSet() + + if t.Children != nil { + n.Children = make([]*node, len(t.Children)) + for i := range t.Children { + n.Children[i] = &node{} + if err := n.Children[i].fromTree(t.Children[i]); err != nil { + return err + } + } + } + + return nil +} + +func (t *Tree) UnmarshalJSON(v []byte) error { + var n node + if err := json.Unmarshal(v, &n); err != nil { return errors.WithStack(err) } - var err error - t.Subject, err = relationtuple.SubjectFromJSON(n.Subject) + tt, err := (&n).toTree() if err != nil { return err } - t.Type = n.Type - t.Children = n.Children - + *t = *tt return nil } +func (t *Tree) MarshalJSON() ([]byte, error) { + var n node + if err := n.fromTree(t); err != nil { + return nil, err + } + return json.Marshal(n) +} + // swagger:ignore func (t *Tree) ToProto() *acl.SubjectTree { if t == nil { diff --git a/internal/httpclient/client/read/get_check_parameters.go b/internal/httpclient/client/read/get_check_parameters.go index 82a431505..4217ea64b 100644 --- a/internal/httpclient/client/read/get_check_parameters.go +++ b/internal/httpclient/client/read/get_check_parameters.go @@ -77,13 +77,29 @@ type GetCheckParams struct { */ Relation string - /* Subject. + /* SubjectID. - Subject of the Relation Tuple + SubjectID of the Relation Tuple + */ + SubjectID *string + + /* SubjectSetNamespace. - The subject follows the subject string encoding format. + Namespace of the Subject Set */ - Subject *string + SubjectSetNamespace *string + + /* SubjectSetObject. + + Object of the Subject Set + */ + SubjectSetObject *string + + /* SubjectSetRelation. + + Relation of the Subject Set + */ + SubjectSetRelation *string timeout time.Duration Context context.Context @@ -171,15 +187,48 @@ func (o *GetCheckParams) SetRelation(relation string) { o.Relation = relation } -// WithSubject adds the subject to the get check params -func (o *GetCheckParams) WithSubject(subject *string) *GetCheckParams { - o.SetSubject(subject) +// WithSubjectID adds the subjectID to the get check params +func (o *GetCheckParams) WithSubjectID(subjectID *string) *GetCheckParams { + o.SetSubjectID(subjectID) + return o +} + +// SetSubjectID adds the subjectId to the get check params +func (o *GetCheckParams) SetSubjectID(subjectID *string) { + o.SubjectID = subjectID +} + +// WithSubjectSetNamespace adds the subjectSetNamespace to the get check params +func (o *GetCheckParams) WithSubjectSetNamespace(subjectSetNamespace *string) *GetCheckParams { + o.SetSubjectSetNamespace(subjectSetNamespace) return o } -// SetSubject adds the subject to the get check params -func (o *GetCheckParams) SetSubject(subject *string) { - o.Subject = subject +// SetSubjectSetNamespace adds the subjectSetNamespace to the get check params +func (o *GetCheckParams) SetSubjectSetNamespace(subjectSetNamespace *string) { + o.SubjectSetNamespace = subjectSetNamespace +} + +// WithSubjectSetObject adds the subjectSetObject to the get check params +func (o *GetCheckParams) WithSubjectSetObject(subjectSetObject *string) *GetCheckParams { + o.SetSubjectSetObject(subjectSetObject) + return o +} + +// SetSubjectSetObject adds the subjectSetObject to the get check params +func (o *GetCheckParams) SetSubjectSetObject(subjectSetObject *string) { + o.SubjectSetObject = subjectSetObject +} + +// WithSubjectSetRelation adds the subjectSetRelation to the get check params +func (o *GetCheckParams) WithSubjectSetRelation(subjectSetRelation *string) *GetCheckParams { + o.SetSubjectSetRelation(subjectSetRelation) + return o +} + +// SetSubjectSetRelation adds the subjectSetRelation to the get check params +func (o *GetCheckParams) SetSubjectSetRelation(subjectSetRelation *string) { + o.SubjectSetRelation = subjectSetRelation } // WriteToRequest writes these params to a swagger request @@ -220,18 +269,69 @@ func (o *GetCheckParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Regi } } - if o.Subject != nil { + if o.SubjectID != nil { + + // query param subject_id + var qrSubjectID string + + if o.SubjectID != nil { + qrSubjectID = *o.SubjectID + } + qSubjectID := qrSubjectID + if qSubjectID != "" { + + if err := r.SetQueryParam("subject_id", qSubjectID); err != nil { + return err + } + } + } + + if o.SubjectSetNamespace != nil { + + // query param subject_set.namespace + var qrSubjectSetNamespace string + + if o.SubjectSetNamespace != nil { + qrSubjectSetNamespace = *o.SubjectSetNamespace + } + qSubjectSetNamespace := qrSubjectSetNamespace + if qSubjectSetNamespace != "" { + + if err := r.SetQueryParam("subject_set.namespace", qSubjectSetNamespace); err != nil { + return err + } + } + } + + if o.SubjectSetObject != nil { + + // query param subject_set.object + var qrSubjectSetObject string + + if o.SubjectSetObject != nil { + qrSubjectSetObject = *o.SubjectSetObject + } + qSubjectSetObject := qrSubjectSetObject + if qSubjectSetObject != "" { + + if err := r.SetQueryParam("subject_set.object", qSubjectSetObject); err != nil { + return err + } + } + } + + if o.SubjectSetRelation != nil { - // query param subject - var qrSubject string + // query param subject_set.relation + var qrSubjectSetRelation string - if o.Subject != nil { - qrSubject = *o.Subject + if o.SubjectSetRelation != nil { + qrSubjectSetRelation = *o.SubjectSetRelation } - qSubject := qrSubject - if qSubject != "" { + qSubjectSetRelation := qrSubjectSetRelation + if qSubjectSetRelation != "" { - if err := r.SetQueryParam("subject", qSubject); err != nil { + if err := r.SetQueryParam("subject_set.relation", qSubjectSetRelation); err != nil { return err } } diff --git a/internal/httpclient/client/read/get_expand_parameters.go b/internal/httpclient/client/read/get_expand_parameters.go index ea27fc0f5..5e81db9cf 100644 --- a/internal/httpclient/client/read/get_expand_parameters.go +++ b/internal/httpclient/client/read/get_expand_parameters.go @@ -67,19 +67,19 @@ type GetExpandParams struct { /* Namespace. - Namespace of the Relation Tuple + Namespace of the Subject Set */ Namespace string /* Object. - Object of the Relation Tuple + Object of the Subject Set */ Object string /* Relation. - Relation of the Relation Tuple + Relation of the Subject Set */ Relation string diff --git a/internal/httpclient/client/read/get_relation_tuples_parameters.go b/internal/httpclient/client/read/get_relation_tuples_parameters.go index 1e2d6ae1a..e719e1ad9 100644 --- a/internal/httpclient/client/read/get_relation_tuples_parameters.go +++ b/internal/httpclient/client/read/get_relation_tuples_parameters.go @@ -60,11 +60,17 @@ func NewGetRelationTuplesParamsWithHTTPClient(client *http.Client) *GetRelationT */ type GetRelationTuplesParams struct { - // Namespace. + /* Namespace. + + Namespace of the Relation Tuple + */ Namespace string - // Object. - Object *string + /* Object. + + Object of the Relation Tuple + */ + Object string // PageSize. // @@ -74,11 +80,35 @@ type GetRelationTuplesParams struct { // PageToken. PageToken *string - // Relation. - Relation *string + /* Relation. + + Relation of the Relation Tuple + */ + Relation string + + /* SubjectID. + + SubjectID of the Relation Tuple + */ + SubjectID *string + + /* SubjectSetNamespace. + + Namespace of the Subject Set + */ + SubjectSetNamespace *string - // Subject. - Subject *string + /* SubjectSetObject. + + Object of the Subject Set + */ + SubjectSetObject *string + + /* SubjectSetRelation. + + Relation of the Subject Set + */ + SubjectSetRelation *string timeout time.Duration Context context.Context @@ -145,13 +175,13 @@ func (o *GetRelationTuplesParams) SetNamespace(namespace string) { } // WithObject adds the object to the get relation tuples params -func (o *GetRelationTuplesParams) WithObject(object *string) *GetRelationTuplesParams { +func (o *GetRelationTuplesParams) WithObject(object string) *GetRelationTuplesParams { o.SetObject(object) return o } // SetObject adds the object to the get relation tuples params -func (o *GetRelationTuplesParams) SetObject(object *string) { +func (o *GetRelationTuplesParams) SetObject(object string) { o.Object = object } @@ -178,25 +208,58 @@ func (o *GetRelationTuplesParams) SetPageToken(pageToken *string) { } // WithRelation adds the relation to the get relation tuples params -func (o *GetRelationTuplesParams) WithRelation(relation *string) *GetRelationTuplesParams { +func (o *GetRelationTuplesParams) WithRelation(relation string) *GetRelationTuplesParams { o.SetRelation(relation) return o } // SetRelation adds the relation to the get relation tuples params -func (o *GetRelationTuplesParams) SetRelation(relation *string) { +func (o *GetRelationTuplesParams) SetRelation(relation string) { o.Relation = relation } -// WithSubject adds the subject to the get relation tuples params -func (o *GetRelationTuplesParams) WithSubject(subject *string) *GetRelationTuplesParams { - o.SetSubject(subject) +// WithSubjectID adds the subjectID to the get relation tuples params +func (o *GetRelationTuplesParams) WithSubjectID(subjectID *string) *GetRelationTuplesParams { + o.SetSubjectID(subjectID) return o } -// SetSubject adds the subject to the get relation tuples params -func (o *GetRelationTuplesParams) SetSubject(subject *string) { - o.Subject = subject +// SetSubjectID adds the subjectId to the get relation tuples params +func (o *GetRelationTuplesParams) SetSubjectID(subjectID *string) { + o.SubjectID = subjectID +} + +// WithSubjectSetNamespace adds the subjectSetNamespace to the get relation tuples params +func (o *GetRelationTuplesParams) WithSubjectSetNamespace(subjectSetNamespace *string) *GetRelationTuplesParams { + o.SetSubjectSetNamespace(subjectSetNamespace) + return o +} + +// SetSubjectSetNamespace adds the subjectSetNamespace to the get relation tuples params +func (o *GetRelationTuplesParams) SetSubjectSetNamespace(subjectSetNamespace *string) { + o.SubjectSetNamespace = subjectSetNamespace +} + +// WithSubjectSetObject adds the subjectSetObject to the get relation tuples params +func (o *GetRelationTuplesParams) WithSubjectSetObject(subjectSetObject *string) *GetRelationTuplesParams { + o.SetSubjectSetObject(subjectSetObject) + return o +} + +// SetSubjectSetObject adds the subjectSetObject to the get relation tuples params +func (o *GetRelationTuplesParams) SetSubjectSetObject(subjectSetObject *string) { + o.SubjectSetObject = subjectSetObject +} + +// WithSubjectSetRelation adds the subjectSetRelation to the get relation tuples params +func (o *GetRelationTuplesParams) WithSubjectSetRelation(subjectSetRelation *string) *GetRelationTuplesParams { + o.SetSubjectSetRelation(subjectSetRelation) + return o +} + +// SetSubjectSetRelation adds the subjectSetRelation to the get relation tuples params +func (o *GetRelationTuplesParams) SetSubjectSetRelation(subjectSetRelation *string) { + o.SubjectSetRelation = subjectSetRelation } // WriteToRequest writes these params to a swagger request @@ -217,20 +280,13 @@ func (o *GetRelationTuplesParams) WriteToRequest(r runtime.ClientRequest, reg st } } - if o.Object != nil { - - // query param object - var qrObject string - - if o.Object != nil { - qrObject = *o.Object - } - qObject := qrObject - if qObject != "" { + // query param object + qrObject := o.Object + qObject := qrObject + if qObject != "" { - if err := r.SetQueryParam("object", qObject); err != nil { - return err - } + if err := r.SetQueryParam("object", qObject); err != nil { + return err } } @@ -268,35 +324,79 @@ func (o *GetRelationTuplesParams) WriteToRequest(r runtime.ClientRequest, reg st } } - if o.Relation != nil { + // query param relation + qrRelation := o.Relation + qRelation := qrRelation + if qRelation != "" { + + if err := r.SetQueryParam("relation", qRelation); err != nil { + return err + } + } + + if o.SubjectID != nil { + + // query param subject_id + var qrSubjectID string + + if o.SubjectID != nil { + qrSubjectID = *o.SubjectID + } + qSubjectID := qrSubjectID + if qSubjectID != "" { + + if err := r.SetQueryParam("subject_id", qSubjectID); err != nil { + return err + } + } + } + + if o.SubjectSetNamespace != nil { + + // query param subject_set.namespace + var qrSubjectSetNamespace string + + if o.SubjectSetNamespace != nil { + qrSubjectSetNamespace = *o.SubjectSetNamespace + } + qSubjectSetNamespace := qrSubjectSetNamespace + if qSubjectSetNamespace != "" { + + if err := r.SetQueryParam("subject_set.namespace", qSubjectSetNamespace); err != nil { + return err + } + } + } + + if o.SubjectSetObject != nil { - // query param relation - var qrRelation string + // query param subject_set.object + var qrSubjectSetObject string - if o.Relation != nil { - qrRelation = *o.Relation + if o.SubjectSetObject != nil { + qrSubjectSetObject = *o.SubjectSetObject } - qRelation := qrRelation - if qRelation != "" { + qSubjectSetObject := qrSubjectSetObject + if qSubjectSetObject != "" { - if err := r.SetQueryParam("relation", qRelation); err != nil { + if err := r.SetQueryParam("subject_set.object", qSubjectSetObject); err != nil { return err } } } - if o.Subject != nil { + if o.SubjectSetRelation != nil { - // query param subject - var qrSubject string + // query param subject_set.relation + var qrSubjectSetRelation string - if o.Subject != nil { - qrSubject = *o.Subject + if o.SubjectSetRelation != nil { + qrSubjectSetRelation = *o.SubjectSetRelation } - qSubject := qrSubject - if qSubject != "" { + qSubjectSetRelation := qrSubjectSetRelation + if qSubjectSetRelation != "" { - if err := r.SetQueryParam("subject", qSubject); err != nil { + if err := r.SetQueryParam("subject_set.relation", qSubjectSetRelation); err != nil { return err } } diff --git a/internal/httpclient/client/read/post_check_parameters.go b/internal/httpclient/client/read/post_check_parameters.go index 4c43f0b0b..1b423a5ab 100644 --- a/internal/httpclient/client/read/post_check_parameters.go +++ b/internal/httpclient/client/read/post_check_parameters.go @@ -62,7 +62,7 @@ func NewPostCheckParamsWithHTTPClient(client *http.Client) *PostCheckParams { type PostCheckParams struct { // Payload. - Payload *models.InternalRelationTuple + Payload *models.RelationQuery timeout time.Duration Context context.Context @@ -118,13 +118,13 @@ func (o *PostCheckParams) SetHTTPClient(client *http.Client) { } // WithPayload adds the payload to the post check params -func (o *PostCheckParams) WithPayload(payload *models.InternalRelationTuple) *PostCheckParams { +func (o *PostCheckParams) WithPayload(payload *models.RelationQuery) *PostCheckParams { o.SetPayload(payload) return o } // SetPayload adds the payload to the post check params -func (o *PostCheckParams) SetPayload(payload *models.InternalRelationTuple) { +func (o *PostCheckParams) SetPayload(payload *models.RelationQuery) { o.Payload = payload } diff --git a/internal/httpclient/client/write/create_relation_tuple_parameters.go b/internal/httpclient/client/write/create_relation_tuple_parameters.go index 61e5625cc..3be845ad1 100644 --- a/internal/httpclient/client/write/create_relation_tuple_parameters.go +++ b/internal/httpclient/client/write/create_relation_tuple_parameters.go @@ -62,7 +62,7 @@ func NewCreateRelationTupleParamsWithHTTPClient(client *http.Client) *CreateRela type CreateRelationTupleParams struct { // Payload. - Payload *models.InternalRelationTuple + Payload *models.RelationQuery timeout time.Duration Context context.Context @@ -118,13 +118,13 @@ func (o *CreateRelationTupleParams) SetHTTPClient(client *http.Client) { } // WithPayload adds the payload to the create relation tuple params -func (o *CreateRelationTupleParams) WithPayload(payload *models.InternalRelationTuple) *CreateRelationTupleParams { +func (o *CreateRelationTupleParams) WithPayload(payload *models.RelationQuery) *CreateRelationTupleParams { o.SetPayload(payload) return o } // SetPayload adds the payload to the create relation tuple params -func (o *CreateRelationTupleParams) SetPayload(payload *models.InternalRelationTuple) { +func (o *CreateRelationTupleParams) SetPayload(payload *models.RelationQuery) { o.Payload = payload } diff --git a/internal/httpclient/client/write/create_relation_tuple_responses.go b/internal/httpclient/client/write/create_relation_tuple_responses.go index 4ffbe233d..e3b365f3c 100644 --- a/internal/httpclient/client/write/create_relation_tuple_responses.go +++ b/internal/httpclient/client/write/create_relation_tuple_responses.go @@ -55,22 +55,22 @@ func NewCreateRelationTupleCreated() *CreateRelationTupleCreated { /* CreateRelationTupleCreated describes a response with status code 201, with default header values. -InternalRelationTuple +RelationQuery */ type CreateRelationTupleCreated struct { - Payload *models.InternalRelationTuple + Payload *models.RelationQuery } func (o *CreateRelationTupleCreated) Error() string { return fmt.Sprintf("[PUT /relation-tuples][%d] createRelationTupleCreated %+v", 201, o.Payload) } -func (o *CreateRelationTupleCreated) GetPayload() *models.InternalRelationTuple { +func (o *CreateRelationTupleCreated) GetPayload() *models.RelationQuery { return o.Payload } func (o *CreateRelationTupleCreated) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { - o.Payload = new(models.InternalRelationTuple) + o.Payload = new(models.RelationQuery) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { diff --git a/internal/httpclient/client/write/delete_relation_tuple_parameters.go b/internal/httpclient/client/write/delete_relation_tuple_parameters.go index 415ec6836..f0117da4d 100644 --- a/internal/httpclient/client/write/delete_relation_tuple_parameters.go +++ b/internal/httpclient/client/write/delete_relation_tuple_parameters.go @@ -77,13 +77,29 @@ type DeleteRelationTupleParams struct { */ Relation string - /* Subject. + /* SubjectID. - Subject of the Relation Tuple + SubjectID of the Relation Tuple + */ + SubjectID *string + + /* SubjectSetNamespace. - The subject follows the subject string encoding format. + Namespace of the Subject Set */ - Subject *string + SubjectSetNamespace *string + + /* SubjectSetObject. + + Object of the Subject Set + */ + SubjectSetObject *string + + /* SubjectSetRelation. + + Relation of the Subject Set + */ + SubjectSetRelation *string timeout time.Duration Context context.Context @@ -171,15 +187,48 @@ func (o *DeleteRelationTupleParams) SetRelation(relation string) { o.Relation = relation } -// WithSubject adds the subject to the delete relation tuple params -func (o *DeleteRelationTupleParams) WithSubject(subject *string) *DeleteRelationTupleParams { - o.SetSubject(subject) +// WithSubjectID adds the subjectID to the delete relation tuple params +func (o *DeleteRelationTupleParams) WithSubjectID(subjectID *string) *DeleteRelationTupleParams { + o.SetSubjectID(subjectID) + return o +} + +// SetSubjectID adds the subjectId to the delete relation tuple params +func (o *DeleteRelationTupleParams) SetSubjectID(subjectID *string) { + o.SubjectID = subjectID +} + +// WithSubjectSetNamespace adds the subjectSetNamespace to the delete relation tuple params +func (o *DeleteRelationTupleParams) WithSubjectSetNamespace(subjectSetNamespace *string) *DeleteRelationTupleParams { + o.SetSubjectSetNamespace(subjectSetNamespace) return o } -// SetSubject adds the subject to the delete relation tuple params -func (o *DeleteRelationTupleParams) SetSubject(subject *string) { - o.Subject = subject +// SetSubjectSetNamespace adds the subjectSetNamespace to the delete relation tuple params +func (o *DeleteRelationTupleParams) SetSubjectSetNamespace(subjectSetNamespace *string) { + o.SubjectSetNamespace = subjectSetNamespace +} + +// WithSubjectSetObject adds the subjectSetObject to the delete relation tuple params +func (o *DeleteRelationTupleParams) WithSubjectSetObject(subjectSetObject *string) *DeleteRelationTupleParams { + o.SetSubjectSetObject(subjectSetObject) + return o +} + +// SetSubjectSetObject adds the subjectSetObject to the delete relation tuple params +func (o *DeleteRelationTupleParams) SetSubjectSetObject(subjectSetObject *string) { + o.SubjectSetObject = subjectSetObject +} + +// WithSubjectSetRelation adds the subjectSetRelation to the delete relation tuple params +func (o *DeleteRelationTupleParams) WithSubjectSetRelation(subjectSetRelation *string) *DeleteRelationTupleParams { + o.SetSubjectSetRelation(subjectSetRelation) + return o +} + +// SetSubjectSetRelation adds the subjectSetRelation to the delete relation tuple params +func (o *DeleteRelationTupleParams) SetSubjectSetRelation(subjectSetRelation *string) { + o.SubjectSetRelation = subjectSetRelation } // WriteToRequest writes these params to a swagger request @@ -220,18 +269,69 @@ func (o *DeleteRelationTupleParams) WriteToRequest(r runtime.ClientRequest, reg } } - if o.Subject != nil { + if o.SubjectID != nil { + + // query param subject_id + var qrSubjectID string + + if o.SubjectID != nil { + qrSubjectID = *o.SubjectID + } + qSubjectID := qrSubjectID + if qSubjectID != "" { + + if err := r.SetQueryParam("subject_id", qSubjectID); err != nil { + return err + } + } + } + + if o.SubjectSetNamespace != nil { + + // query param subject_set.namespace + var qrSubjectSetNamespace string + + if o.SubjectSetNamespace != nil { + qrSubjectSetNamespace = *o.SubjectSetNamespace + } + qSubjectSetNamespace := qrSubjectSetNamespace + if qSubjectSetNamespace != "" { + + if err := r.SetQueryParam("subject_set.namespace", qSubjectSetNamespace); err != nil { + return err + } + } + } + + if o.SubjectSetObject != nil { + + // query param subject_set.object + var qrSubjectSetObject string + + if o.SubjectSetObject != nil { + qrSubjectSetObject = *o.SubjectSetObject + } + qSubjectSetObject := qrSubjectSetObject + if qSubjectSetObject != "" { + + if err := r.SetQueryParam("subject_set.object", qSubjectSetObject); err != nil { + return err + } + } + } + + if o.SubjectSetRelation != nil { - // query param subject - var qrSubject string + // query param subject_set.relation + var qrSubjectSetRelation string - if o.Subject != nil { - qrSubject = *o.Subject + if o.SubjectSetRelation != nil { + qrSubjectSetRelation = *o.SubjectSetRelation } - qSubject := qrSubject - if qSubject != "" { + qSubjectSetRelation := qrSubjectSetRelation + if qSubjectSetRelation != "" { - if err := r.SetQueryParam("subject", qSubject); err != nil { + if err := r.SetQueryParam("subject_set.relation", qSubjectSetRelation); err != nil { return err } } diff --git a/internal/httpclient/models/expand_tree.go b/internal/httpclient/models/expand_tree.go index ad1a0d33d..986d71d19 100644 --- a/internal/httpclient/models/expand_tree.go +++ b/internal/httpclient/models/expand_tree.go @@ -24,9 +24,11 @@ type ExpandTree struct { // children Children []*ExpandTree `json:"children"` - // subject - // Required: true - Subject *Subject `json:"subject"` + // subject id + SubjectID string `json:"subject_id,omitempty"` + + // subject set + SubjectSet *SubjectSet `json:"subject_set,omitempty"` // type // Required: true @@ -42,7 +44,7 @@ func (m *ExpandTree) Validate(formats strfmt.Registry) error { res = append(res, err) } - if err := m.validateSubject(formats); err != nil { + if err := m.validateSubjectSet(formats); err != nil { res = append(res, err) } @@ -80,20 +82,15 @@ func (m *ExpandTree) validateChildren(formats strfmt.Registry) error { return nil } -func (m *ExpandTree) validateSubject(formats strfmt.Registry) error { - - if err := validate.Required("subject", "body", m.Subject); err != nil { - return err - } - - if err := validate.Required("subject", "body", m.Subject); err != nil { - return err +func (m *ExpandTree) validateSubjectSet(formats strfmt.Registry) error { + if swag.IsZero(m.SubjectSet) { // not required + return nil } - if m.Subject != nil { - if err := m.Subject.Validate(formats); err != nil { + if m.SubjectSet != nil { + if err := m.SubjectSet.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("subject") + return ve.ValidateName("subject_set") } return err } @@ -159,7 +156,7 @@ func (m *ExpandTree) ContextValidate(ctx context.Context, formats strfmt.Registr res = append(res, err) } - if err := m.contextValidateSubject(ctx, formats); err != nil { + if err := m.contextValidateSubjectSet(ctx, formats); err != nil { res = append(res, err) } @@ -187,12 +184,12 @@ func (m *ExpandTree) contextValidateChildren(ctx context.Context, formats strfmt return nil } -func (m *ExpandTree) contextValidateSubject(ctx context.Context, formats strfmt.Registry) error { +func (m *ExpandTree) contextValidateSubjectSet(ctx context.Context, formats strfmt.Registry) error { - if m.Subject != nil { - if err := m.Subject.ContextValidate(ctx, formats); err != nil { + if m.SubjectSet != nil { + if err := m.SubjectSet.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("subject") + return ve.ValidateName("subject_set") } return err } diff --git a/internal/httpclient/models/internal_relation_tuple.go b/internal/httpclient/models/internal_relation_tuple.go index b002433d5..e17517c99 100644 --- a/internal/httpclient/models/internal_relation_tuple.go +++ b/internal/httpclient/models/internal_relation_tuple.go @@ -20,26 +20,24 @@ import ( type InternalRelationTuple struct { // Namespace of the Relation Tuple - // - // in: query // Required: true Namespace *string `json:"namespace"` // Object of the Relation Tuple - // - // in: query // Required: true Object *string `json:"object"` // Relation of the Relation Tuple - // - // in: query // Required: true Relation *string `json:"relation"` - // subject - // Required: true - Subject *Subject `json:"subject"` + // SubjectID of the Relation Tuple + // + // Either SubjectSet or SubjectID are required. + SubjectID string `json:"subject_id,omitempty"` + + // subject set + SubjectSet *SubjectSet `json:"subject_set,omitempty"` } // Validate validates this internal relation tuple @@ -58,7 +56,7 @@ func (m *InternalRelationTuple) Validate(formats strfmt.Registry) error { res = append(res, err) } - if err := m.validateSubject(formats); err != nil { + if err := m.validateSubjectSet(formats); err != nil { res = append(res, err) } @@ -95,20 +93,15 @@ func (m *InternalRelationTuple) validateRelation(formats strfmt.Registry) error return nil } -func (m *InternalRelationTuple) validateSubject(formats strfmt.Registry) error { - - if err := validate.Required("subject", "body", m.Subject); err != nil { - return err - } - - if err := validate.Required("subject", "body", m.Subject); err != nil { - return err +func (m *InternalRelationTuple) validateSubjectSet(formats strfmt.Registry) error { + if swag.IsZero(m.SubjectSet) { // not required + return nil } - if m.Subject != nil { - if err := m.Subject.Validate(formats); err != nil { + if m.SubjectSet != nil { + if err := m.SubjectSet.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("subject") + return ve.ValidateName("subject_set") } return err } @@ -121,7 +114,7 @@ func (m *InternalRelationTuple) validateSubject(formats strfmt.Registry) error { func (m *InternalRelationTuple) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error - if err := m.contextValidateSubject(ctx, formats); err != nil { + if err := m.contextValidateSubjectSet(ctx, formats); err != nil { res = append(res, err) } @@ -131,12 +124,12 @@ func (m *InternalRelationTuple) ContextValidate(ctx context.Context, formats str return nil } -func (m *InternalRelationTuple) contextValidateSubject(ctx context.Context, formats strfmt.Registry) error { +func (m *InternalRelationTuple) contextValidateSubjectSet(ctx context.Context, formats strfmt.Registry) error { - if m.Subject != nil { - if err := m.Subject.ContextValidate(ctx, formats); err != nil { + if m.SubjectSet != nil { + if err := m.SubjectSet.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("subject") + return ve.ValidateName("subject_set") } return err } diff --git a/internal/httpclient/models/relation_query.go b/internal/httpclient/models/relation_query.go new file mode 100644 index 000000000..e07d68ce3 --- /dev/null +++ b/internal/httpclient/models/relation_query.go @@ -0,0 +1,129 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// RelationQuery relation query +// +// swagger:model RelationQuery +type RelationQuery struct { + + // Namespace of the Relation Tuple + // Required: true + Namespace *string `json:"namespace"` + + // Object of the Relation Tuple + Object string `json:"object,omitempty"` + + // Relation of the Relation Tuple + Relation string `json:"relation,omitempty"` + + // SubjectID of the Relation Tuple + // + // Either SubjectSet or SubjectID can be provided. + SubjectID string `json:"subject_id,omitempty"` + + // subject set + SubjectSet *SubjectSet `json:"subject_set,omitempty"` +} + +// Validate validates this relation query +func (m *RelationQuery) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateNamespace(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSubjectSet(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *RelationQuery) validateNamespace(formats strfmt.Registry) error { + + if err := validate.Required("namespace", "body", m.Namespace); err != nil { + return err + } + + return nil +} + +func (m *RelationQuery) validateSubjectSet(formats strfmt.Registry) error { + if swag.IsZero(m.SubjectSet) { // not required + return nil + } + + if m.SubjectSet != nil { + if err := m.SubjectSet.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("subject_set") + } + return err + } + } + + return nil +} + +// ContextValidate validate this relation query based on the context it is used +func (m *RelationQuery) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateSubjectSet(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *RelationQuery) contextValidateSubjectSet(ctx context.Context, formats strfmt.Registry) error { + + if m.SubjectSet != nil { + if err := m.SubjectSet.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("subject_set") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *RelationQuery) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *RelationQuery) UnmarshalBinary(b []byte) error { + var res RelationQuery + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/internal/httpclient/models/subject.go b/internal/httpclient/models/subject.go deleted file mode 100644 index 2c079333c..000000000 --- a/internal/httpclient/models/subject.go +++ /dev/null @@ -1,27 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package models - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - "context" - - "github.com/go-openapi/strfmt" -) - -// Subject subject -// -// swagger:model subject -type Subject string - -// Validate validates this subject -func (m Subject) Validate(formats strfmt.Registry) error { - return nil -} - -// ContextValidate validates this subject based on context it is used -func (m Subject) ContextValidate(ctx context.Context, formats strfmt.Registry) error { - return nil -} diff --git a/internal/httpclient/models/subject_set.go b/internal/httpclient/models/subject_set.go new file mode 100644 index 000000000..d6a2b3999 --- /dev/null +++ b/internal/httpclient/models/subject_set.go @@ -0,0 +1,105 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// SubjectSet subject set +// +// swagger:model SubjectSet +type SubjectSet struct { + + // Namespace of the Subject Set + // Required: true + Namespace *string `json:"namespace"` + + // Object of the Subject Set + // Required: true + Object *string `json:"object"` + + // Relation of the Subject Set + // Required: true + Relation *string `json:"relation"` +} + +// Validate validates this subject set +func (m *SubjectSet) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateNamespace(formats); err != nil { + res = append(res, err) + } + + if err := m.validateObject(formats); err != nil { + res = append(res, err) + } + + if err := m.validateRelation(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *SubjectSet) validateNamespace(formats strfmt.Registry) error { + + if err := validate.Required("namespace", "body", m.Namespace); err != nil { + return err + } + + return nil +} + +func (m *SubjectSet) validateObject(formats strfmt.Registry) error { + + if err := validate.Required("object", "body", m.Object); err != nil { + return err + } + + return nil +} + +func (m *SubjectSet) validateRelation(formats strfmt.Registry) error { + + if err := validate.Required("relation", "body", m.Relation); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this subject set based on context it is used +func (m *SubjectSet) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *SubjectSet) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *SubjectSet) UnmarshalBinary(b []byte) error { + var res SubjectSet + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/internal/persistence/sql/persister.go b/internal/persistence/sql/persister.go index b44aeeb9d..df78b5109 100644 --- a/internal/persistence/sql/persister.go +++ b/internal/persistence/sql/persister.go @@ -58,9 +58,6 @@ var ( ) func NewPersister(reg dependencies, nid uuid.UUID) (*Persister, error) { - //pop.SetLogger(reg.Logger().PopLogger) - - pop.Debug = true conn, err := reg.PopConnection() if err != nil { return nil, err diff --git a/internal/persistence/sql/relationtuples.go b/internal/persistence/sql/relationtuples.go index 16044ed9c..7f8f381e2 100644 --- a/internal/persistence/sql/relationtuples.go +++ b/internal/persistence/sql/relationtuples.go @@ -221,8 +221,8 @@ func (p *Persister) GetRelationTuples(ctx context.Context, query *relationtuple. if query.Object != "" { sqlQuery.Where("object = ?", query.Object) } - if query.Subject != nil { - if err := p.whereSubject(ctx, sqlQuery, query.Subject); err != nil { + if s := query.Subject(); s != nil { + if err := p.whereSubject(ctx, sqlQuery, s); err != nil { return nil, "", err } } diff --git a/internal/relationtuple/definitions.go b/internal/relationtuple/definitions.go index 841836a90..0048af266 100644 --- a/internal/relationtuple/definitions.go +++ b/internal/relationtuple/definitions.go @@ -8,6 +8,8 @@ import ( "strings" "testing" + "github.com/ory/x/pointerx" + "github.com/ory/herodot" "github.com/sirupsen/logrus" @@ -17,8 +19,6 @@ import ( "github.com/pkg/errors" "github.com/ory/keto/internal/x" - - "github.com/tidwall/gjson" ) type ( @@ -42,11 +42,54 @@ type ( ) type RelationQuery struct { + // Namespace of the Relation Tuple + // // required: true - Namespace string `json:"namespace"` - Object string `json:"object"` - Relation string `json:"relation"` - Subject Subject `json:"subject"` + Namespace string `json:"namespace"` + + // Object of the Relation Tuple + Object string `json:"object"` + + // Relation of the Relation Tuple + Relation string `json:"relation"` + + // SubjectID of the Relation Tuple + // + // Either SubjectSet or SubjectID can be provided. + SubjectID *string `json:"subject_id,omitempty"` + // SubjectSet of the Relation Tuple + // + // Either SubjectSet or SubjectID can be provided. + // + // swagger:allOf + SubjectSet *SubjectSet `json:"subject_set,omitempty"` +} + +// swagger:model InternalRelationTuple +type relationTupleWithRequired struct { + // Namespace of the Relation Tuple + // + // required: true + Namespace string `json:"namespace"` + + // Object of the Relation Tuple + // + // required: true + Object string `json:"object"` + + // Relation of the Relation Tuple + // + // required: true + Relation string `json:"relation"` + + // SubjectID of the Relation Tuple + // + // Either SubjectSet or SubjectID are required. + SubjectID *string `json:"subject_id,omitempty"` + // SubjectSet of the Relation Tuple + // + // Either SubjectSet or SubjectID are required. + SubjectSet *SubjectSet `json:"subject_set,omitempty"` } // swagger:ignore @@ -66,62 +109,37 @@ type Subject interface { FromString(string) (Subject, error) // swagger:ignore Equals(interface{}) bool + // swagger:ignore + SubjectID() *string + // swagger:ignore + SubjectSet() *SubjectSet // swagger:ignore ToProto() *acl.Subject } -// swagger:parameters getCheck deleteRelationTuple +// swagger:ignore type InternalRelationTuple struct { - // Namespace of the Relation Tuple - // - // in: query - // required: true - Namespace string `json:"namespace"` - - // Object of the Relation Tuple - // - // in: query - // required: true - Object string `json:"object"` - - // Relation of the Relation Tuple - // - // in: query - // required: true - Relation string `json:"relation"` - - // Subject of the Relation Tuple - // - // The subject follows the subject string encoding format. - // - // in: query - // required: true - Subject Subject `json:"subject"` + Namespace string `json:"namespace"` + Object string `json:"object"` + Relation string `json:"relation"` + Subject Subject `json:"subject"` } -// swagger:model subject -// nolint:deadcode,unused -// this is overwritten with the right definition by /spec/patches/subjects.yml -type stringEncodedSubject string - // swagger:parameters getExpand type SubjectSet struct { - // Namespace of the Relation Tuple + // Namespace of the Subject Set // - // in: query // required: true Namespace string `json:"namespace"` - // Object of the Relation Tuple + // Object of the Subject Set // - // in: query // required: true Object string `json:"object"` - // Relation of the Relation Tuple + // Relation of the Subject Set // - // in: query // required: true Relation string `json:"relation"` } @@ -129,9 +147,11 @@ type SubjectSet struct { var ( _, _ Subject = &SubjectID{}, &SubjectSet{} - ErrMalformedInput = herodot.ErrBadRequest.WithError("malformed string input") - ErrNilSubject = herodot.ErrBadRequest.WithError("subject is not allowed to be nil") - ErrNonParsableSubject = herodot.ErrBadRequest.WithError("subject is not parsable") + ErrMalformedInput = herodot.ErrBadRequest.WithError("malformed string input") + ErrNilSubject = herodot.ErrBadRequest.WithError("subject is not allowed to be nil") + ErrDuplicateSubject = herodot.ErrBadRequest.WithError("exactly one of subject_set or subject_id has to be provided") + ErrDroppedSubjectKey = herodot.ErrBadRequest.WithDebug(`provide "subject_id" or "subject_set.*"; support for "subject" was dropped`) + ErrIncompleteSubject = herodot.ErrBadRequest.WithError(`incomplete subject, provide "subject_id" or a complete "subject_set.*"`) ) // swagger:enum patchAction @@ -163,22 +183,6 @@ func SubjectFromString(s string) (Subject, error) { return (&SubjectID{}).FromString(s) } -func SubjectFromJSON(raw []byte) (subject Subject, err error) { - s := gjson.ParseBytes(raw) - if s.IsObject() { - subject = &SubjectSet{} - if err := json.Unmarshal([]byte(s.Raw), subject); err != nil { - return nil, errors.WithStack(err) - } - } else if s.Type == gjson.String { - subject = &SubjectID{ID: s.Str} - } else { - return nil, errors.Wrapf(ErrNonParsableSubject, "expected subject to be of type string or object, but got %s", s.Type) - } - - return -} - // swagger:ignore func SubjectFromProto(gs *acl.Subject) (Subject, error) { switch s := gs.GetRef().(type) { @@ -249,6 +253,22 @@ func (s *SubjectSet) ToURLQuery() url.Values { } } +func (s *SubjectSet) SubjectID() *string { + return nil +} + +func (s *SubjectSet) SubjectSet() *SubjectSet { + return s +} + +func (s *SubjectID) SubjectID() *string { + return &s.ID +} + +func (s *SubjectID) SubjectSet() *SubjectSet { + return nil +} + // swagger:ignore func (s *SubjectID) ToProto() *acl.Subject { return &acl.Subject{ @@ -335,30 +355,32 @@ func (r *InternalRelationTuple) DeriveSubject() *SubjectSet { } func (r *InternalRelationTuple) UnmarshalJSON(raw []byte) error { - parsed := gjson.ParseBytes(raw) - - var err error - r.Subject, err = SubjectFromJSON([]byte(parsed.Get("subject").Raw)) - if err != nil { - return err + var rq RelationQuery + if err := json.Unmarshal(raw, &rq); err != nil { + return errors.WithStack(err) + } + if rq.SubjectID != nil && rq.SubjectSet != nil { + return errors.WithStack(ErrDuplicateSubject) + } else if rq.SubjectID == nil && rq.SubjectSet == nil { + return errors.WithStack(ErrNilSubject) } - r.Namespace = parsed.Get("namespace").Str - r.Object = parsed.Get("object").Str - r.Relation = parsed.Get("relation").Str + r.Namespace = rq.Namespace + r.Object = rq.Object + r.Relation = rq.Relation + + // validation was done before already + if rq.SubjectID == nil { + r.Subject = rq.SubjectSet + } else { + r.Subject = &SubjectID{ID: *rq.SubjectID} + } return nil } func (r *InternalRelationTuple) MarshalJSON() ([]byte, error) { - type t InternalRelationTuple - - enc, err := json.Marshal((*t)(r)) - if err != nil { - return nil, errors.WithStack(err) - } - - return enc, nil + return json.Marshal(r.ToQuery()) } func (r *InternalRelationTuple) FromDataProvider(d TupleData) (*InternalRelationTuple, error) { @@ -384,32 +406,52 @@ func (r *InternalRelationTuple) ToProto() *acl.RelationTuple { } } +func (r *InternalRelationTuple) ToQuery() *RelationQuery { + return &RelationQuery{ + Namespace: r.Namespace, + Object: r.Object, + Relation: r.Relation, + SubjectID: r.Subject.SubjectID(), + SubjectSet: r.Subject.SubjectSet(), + } +} + func (r *InternalRelationTuple) FromURLQuery(query url.Values) (*InternalRelationTuple, error) { - if s := query.Get("subject"); s != "" { - var err error - r.Subject, err = SubjectFromString(s) - if err != nil { - return nil, err - } + q, err := (&RelationQuery{}).FromURLQuery(query) + if err != nil { + return nil, err + } + + if s := q.Subject(); s == nil { + return nil, errors.WithStack(ErrNilSubject) + } else { + r.Subject = s } - r.Object = query.Get("object") - r.Relation = query.Get("relation") - r.Namespace = query.Get("namespace") + r.Namespace = q.Namespace + r.Object = q.Object + r.Relation = q.Relation return r, nil } -func (r *InternalRelationTuple) ToURLQuery() url.Values { +func (r *InternalRelationTuple) ToURLQuery() (url.Values, error) { vals := url.Values{ "namespace": []string{r.Namespace}, "object": []string{r.Object}, "relation": []string{r.Relation}, } - if r.Subject != nil { - vals.Set("subject", r.Subject.String()) + switch s := r.Subject.(type) { + case *SubjectID: + vals.Set(subjectIDKey, s.ID) + case *SubjectSet: + vals.Set(subjectSetNamespaceKey, s.Namespace) + vals.Set(subjectSetObjectKey, s.Object) + vals.Set(subjectSetRelationKey, s.Relation) + case nil: + return nil, errors.WithStack(ErrNilSubject) } - return vals + return vals, nil } func (r *InternalRelationTuple) ToLoggerFields() logrus.Fields { @@ -417,31 +459,71 @@ func (r *InternalRelationTuple) ToLoggerFields() logrus.Fields { "namespace": r.Namespace, "object": r.Object, "relation": r.Relation, - "subject": r.Subject, + "subject": r.Subject.String(), } } func (q *RelationQuery) FromProto(query *acl.ListRelationTuplesRequest_Query) (*RelationQuery, error) { - r, err := (&InternalRelationTuple{}).FromDataProvider(query) - if err != nil { - return nil, err + q.Namespace = query.Namespace + q.Object = query.Object + q.Relation = query.Relation + // reset subject + q.SubjectID = nil + q.SubjectSet = nil + + if query.Subject != nil { + switch s := query.Subject.Ref.(type) { + case *acl.Subject_Id: + q.SubjectID = &s.Id + case *acl.Subject_Set: + q.SubjectSet = &SubjectSet{ + Namespace: s.Set.Namespace, + Object: s.Set.Object, + Relation: s.Set.Relation, + } + case nil: + return nil, errors.WithStack(ErrNilSubject) + } } - *q = RelationQuery(*r) return q, nil } +const ( + subjectIDKey = "subject_id" + subjectSetNamespaceKey = "subject_set.namespace" + subjectSetObjectKey = "subject_set.object" + subjectSetRelationKey = "subject_set.relation" +) + func (q *RelationQuery) FromURLQuery(query url.Values) (*RelationQuery, error) { if q == nil { q = &RelationQuery{} } - if s := query.Get("subject"); s != "" { - var err error - q.Subject, err = SubjectFromString(s) - if err != nil { - return nil, err + if query.Has("subject") { + return nil, errors.WithStack(ErrDroppedSubjectKey) + } + + // reset subject + q.SubjectID = nil + q.SubjectSet = nil + + switch { + case !query.Has(subjectIDKey) && !query.Has(subjectSetNamespaceKey) && !query.Has(subjectSetObjectKey) && !query.Has(subjectSetRelationKey): + // was not queried for the subject + case query.Has(subjectIDKey) && query.Has(subjectSetNamespaceKey) && query.Has(subjectSetObjectKey) && query.Has(subjectSetRelationKey): + return nil, errors.WithStack(ErrDuplicateSubject) + case query.Has(subjectIDKey): + q.SubjectID = pointerx.String(query.Get(subjectIDKey)) + case query.Has(subjectSetNamespaceKey) && query.Has(subjectSetObjectKey) && query.Has(subjectSetRelationKey): + q.SubjectSet = &SubjectSet{ + Namespace: query.Get(subjectSetNamespaceKey), + Object: query.Get(subjectSetObjectKey), + Relation: query.Get(subjectSetRelationKey), } + default: + return nil, errors.WithStack(ErrIncompleteSubject) } q.Object = query.Get("object") @@ -463,15 +545,31 @@ func (q *RelationQuery) ToURLQuery() url.Values { if q.Object != "" { v.Add("object", q.Object) } - if q.Subject != nil { - v.Add("subject", q.Subject.String()) + if q.SubjectID != nil { + v.Add(subjectIDKey, *q.SubjectID) + } else if q.SubjectSet != nil { + v.Add(subjectSetNamespaceKey, q.SubjectSet.Namespace) + v.Add(subjectSetObjectKey, q.SubjectSet.Object) + v.Add(subjectSetRelationKey, q.SubjectSet.Relation) } return v } +func (q *RelationQuery) Subject() Subject { + if q.SubjectID != nil { + return &SubjectID{ID: *q.SubjectID} + } else if q.SubjectSet != nil { + return q.SubjectSet + } + return nil +} + func (q *RelationQuery) String() string { - return fmt.Sprintf("namespace: %s; object: %s; relation: %s; subject: %s", q.Namespace, q.Object, q.Relation, q.Subject) + if q.SubjectID != nil { + return fmt.Sprintf("namespace: %s; object: %s; relation: %s; subject: %s", q.Namespace, q.Object, q.Relation, *q.SubjectID) + } + return fmt.Sprintf("namespace: %s; object: %s; relation: %s; subject: %v", q.Namespace, q.Object, q.Relation, q.SubjectSet) } func (r *InternalRelationTuple) Header() []string { @@ -479,7 +577,7 @@ func (r *InternalRelationTuple) Header() []string { "NAMESPACE", "OBJECT ID", "RELATION NAME", - "SUBJECT ID", + "SUBJECT", } } diff --git a/internal/relationtuple/definitions_test.go b/internal/relationtuple/definitions_test.go index 21ebf8be0..e97efff16 100644 --- a/internal/relationtuple/definitions_test.go +++ b/internal/relationtuple/definitions_test.go @@ -8,6 +8,8 @@ import ( "strconv" "testing" + "github.com/ory/x/pointerx" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" @@ -360,11 +362,15 @@ func TestInternalRelationTuple(t *testing.T) { Relation: "sr", }, }, - {}, + { + Subject: &SubjectID{}, + }, } { t.Run(fmt.Sprintf("case=%d", i), func(t *testing.T) { - res, err := (&InternalRelationTuple{}).FromURLQuery(r.ToURLQuery()) + vals, err := r.ToURLQuery() require.NoError(t, err) + res, err := (&InternalRelationTuple{}).FromURLQuery(vals) + require.NoError(t, err, "raw: %+v, enc: %+v", r, vals) assert.Equal(t, r, res) }) } @@ -373,22 +379,26 @@ func TestInternalRelationTuple(t *testing.T) { t.Run("case=url decoding-encoding", func(t *testing.T) { for i, v := range []url.Values{ { - "namespace": []string{"n"}, - "object": []string{"o"}, - "relation": []string{"r"}, - "subject": []string{"foo"}, + "namespace": []string{"n"}, + "object": []string{"o"}, + "relation": []string{"r"}, + "subject_id": []string{"foo"}, }, { - "namespace": []string{"n"}, - "object": []string{"o"}, - "relation": []string{"r"}, - "subject": []string{"sn:so#sr"}, + "namespace": []string{"n"}, + "object": []string{"o"}, + "relation": []string{"r"}, + "subject_set.namespace": []string{"sn"}, + "subject_set.object": []string{"so"}, + "subject_set.relation": []string{"sr"}, }, } { t.Run(fmt.Sprintf("case=%d", i), func(t *testing.T) { rt, err := (&InternalRelationTuple{}).FromURLQuery(v) require.NoError(t, err) - assert.Equal(t, v, rt.ToURLQuery()) + q, err := rt.ToURLQuery() + require.NoError(t, err) + assert.Equal(t, v, q) }) } }) @@ -483,7 +493,7 @@ func TestInternalRelationTuple(t *testing.T) { "namespace": "n", "object": "o", "relation": "r", - "subject": "s" + "subject_id": "s" }`, }, { @@ -503,7 +513,7 @@ func TestInternalRelationTuple(t *testing.T) { "namespace": "n", "object": "o", "relation": "r", - "subject": { + "subject_set": { "namespace": "sn", "object": "so", "relation": "sr" @@ -533,30 +543,32 @@ func TestRelationQuery(t *testing.T) { }{ { v: url.Values{ - "namespace": []string{"n"}, - "object": []string{"o"}, - "relation": []string{"r"}, - "subject": []string{"foo"}, + "namespace": []string{"n"}, + "object": []string{"o"}, + "relation": []string{"r"}, + "subject_id": []string{"foo"}, }, r: &RelationQuery{ Namespace: "n", Object: "o", Relation: "r", - Subject: &SubjectID{ID: "foo"}, + SubjectID: pointerx.String("foo"), }, }, { v: url.Values{ - "namespace": []string{"n"}, - "object": []string{"o"}, - "relation": []string{"r"}, - "subject": []string{"sn:so#sr"}, + "namespace": []string{"n"}, + "object": []string{"o"}, + "relation": []string{"r"}, + "subject_set.namespace": []string{"sn"}, + "subject_set.object": []string{"so"}, + "subject_set.relation": []string{"sr"}, }, r: &RelationQuery{ Namespace: "n", Object: "o", Relation: "r", - Subject: &SubjectSet{ + SubjectSet: &SubjectSet{ Namespace: "sn", Object: "so", Relation: "sr", diff --git a/internal/relationtuple/manager_requirements.go b/internal/relationtuple/manager_requirements.go index f9fcfdb02..568917112 100644 --- a/internal/relationtuple/manager_requirements.go +++ b/internal/relationtuple/manager_requirements.go @@ -7,6 +7,8 @@ import ( "strconv" "testing" + "github.com/ory/x/pointerx" + "github.com/ory/herodot" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -45,7 +47,7 @@ func ManagerTest(t *testing.T, m Manager, addNamespace func(context.Context, *te tupC := *tup t.Run(fmt.Sprintf("subject_type=%T", tupC.Subject), func(t *testing.T) { - resp, nextPage, err := m.GetRelationTuples(context.Background(), (*RelationQuery)(&tupC)) + resp, nextPage, err := m.GetRelationTuples(context.Background(), tupC.ToQuery()) require.NoError(t, err) assert.Equal(t, "", nextPage) assert.Equal(t, []*InternalRelationTuple{&tupC}, resp) @@ -129,7 +131,7 @@ func ManagerTest(t *testing.T, m Manager, addNamespace func(context.Context, *te { query: &RelationQuery{ Namespace: nspace, - Subject: &SubjectID{ID: "s 0"}, + SubjectID: pointerx.String("s 0"), }, expected: []*InternalRelationTuple{ tuples[0], @@ -139,7 +141,7 @@ func ManagerTest(t *testing.T, m Manager, addNamespace func(context.Context, *te query: &RelationQuery{ Namespace: nspace, Object: "o 0", - Subject: &SubjectID{ID: "s 0"}, + SubjectID: pointerx.String("s 0"), }, expected: []*InternalRelationTuple{ tuples[0], @@ -149,7 +151,7 @@ func ManagerTest(t *testing.T, m Manager, addNamespace func(context.Context, *te query: &RelationQuery{ Namespace: nspace, Relation: "r 0", - Subject: &SubjectID{ID: "s 0"}, + SubjectID: pointerx.String("s 0"), }, expected: []*InternalRelationTuple{ tuples[0], @@ -160,7 +162,7 @@ func ManagerTest(t *testing.T, m Manager, addNamespace func(context.Context, *te Namespace: nspace, Object: "o 0", Relation: "r 0", - Subject: &SubjectID{ID: "s 0"}, + SubjectID: pointerx.String("s 0"), }, expected: []*InternalRelationTuple{ tuples[0], @@ -284,13 +286,13 @@ func ManagerTest(t *testing.T, m Manager, addNamespace func(context.Context, *te t.Run(fmt.Sprintf("subject_type=%T", rt.Subject), func(t *testing.T) { require.NoError(t, m.WriteRelationTuples(context.Background(), rt)) - res, _, err := m.GetRelationTuples(context.Background(), (*RelationQuery)(rt)) + res, _, err := m.GetRelationTuples(context.Background(), rt.ToQuery()) require.NoError(t, err) assert.Equal(t, []*InternalRelationTuple{rt}, res) require.NoError(t, m.DeleteRelationTuples(context.Background(), rt)) - res, _, err = m.GetRelationTuples(context.Background(), (*RelationQuery)(rt)) + res, _, err = m.GetRelationTuples(context.Background(), rt.ToQuery()) require.NoError(t, err) assert.Len(t, res, 0) }) diff --git a/internal/relationtuple/read_server.go b/internal/relationtuple/read_server.go index 4ba4fe84d..cd3e9dbd7 100644 --- a/internal/relationtuple/read_server.go +++ b/internal/relationtuple/read_server.go @@ -23,19 +23,12 @@ func (h *handler) ListRelationTuples(ctx context.Context, req *acl.ListRelationT return nil, errors.New("invalid request") } - sub, err := SubjectFromProto(req.Query.Subject) + q, err := (&RelationQuery{}).FromProto(req.Query) if err != nil { - // this means we are not querying by subject - sub = nil + return nil, err } - rels, nextPage, err := h.d.RelationTupleManager().GetRelationTuples(ctx, - &RelationQuery{ - Namespace: req.Query.Namespace, - Object: req.Query.Object, - Relation: req.Query.Relation, - Subject: sub, - }, + rels, nextPage, err := h.d.RelationTupleManager().GetRelationTuples(ctx, q, x.WithSize(int(req.PageSize)), x.WithToken(req.PageToken), ) @@ -58,7 +51,7 @@ func (h *handler) ListRelationTuples(ctx context.Context, req *acl.ListRelationT // nolint:deadcode,unused type getRelationsParams struct { // swagger:allOf - RelationQuery + queryRelationTuple // swagger:allOf x.PaginationOptions } diff --git a/internal/relationtuple/transact_server.go b/internal/relationtuple/transact_server.go index b9c30ee78..80afcb1b7 100644 --- a/internal/relationtuple/transact_server.go +++ b/internal/relationtuple/transact_server.go @@ -58,7 +58,55 @@ func (h *handler) TransactRelationTuples(ctx context.Context, req *acl.TransactR // nolint:deadcode,unused type bodyRelationTuple struct { // in: body - Payload InternalRelationTuple + Payload RelationQuery +} + +// The basic ACL relation tuple +// +// swagger:parameters getCheck deleteRelationTuple +// nolint:deadcode,unused +type queryRelationTuple struct { + // Namespace of the Relation Tuple + // + // in: query + // required: true + Namespace string `json:"namespace"` + + // Object of the Relation Tuple + // + // in: query + // required: true + Object string `json:"object"` + + // Relation of the Relation Tuple + // + // in: query + // required: true + Relation string `json:"relation"` + + // SubjectID of the Relation Tuple + // + // in: query + // Either subject_set.* or subject_id are required. + SubjectID string `json:"subject_id"` + + // Namespace of the Subject Set + // + // in: query + // Either subject_set.* or subject_id are required. + SNamespace string `json:"subject_set.namespace"` + + // Object of the Subject Set + // + // in: query + // Either subject_set.* or subject_id are required. + SObject string `json:"subject_set.object"` + + // Relation of the Subject Set + // + // in: query + // Either subject_set.* or subject_id are required. + SRelation string `json:"subject_set.relation"` } // swagger:route PUT /relation-tuples write createRelationTuple @@ -76,7 +124,7 @@ type bodyRelationTuple struct { // Schemes: http, https // // Responses: -// 201: InternalRelationTuple +// 201: RelationQuery // 400: genericError // 500: genericError func (h *handler) createRelation(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { @@ -95,7 +143,13 @@ func (h *handler) createRelation(w http.ResponseWriter, r *http.Request, _ httpr return } - h.d.Writer().WriteCreated(w, r, RouteBase+"?"+rel.ToURLQuery().Encode(), rel) + q, err := rel.ToURLQuery() + if err != nil { + h.d.Writer().WriteError(w, r, err) + return + } + + h.d.Writer().WriteCreated(w, r, RouteBase+"?"+q.Encode(), &rel) } // swagger:route DELETE /relation-tuples write deleteRelationTuple diff --git a/internal/relationtuple/transact_server_test.go b/internal/relationtuple/transact_server_test.go index 5a7b4b7b9..e401dae65 100644 --- a/internal/relationtuple/transact_server_test.go +++ b/internal/relationtuple/transact_server_test.go @@ -80,7 +80,7 @@ func TestWriteHandlers(t *testing.T) { t.Run("check=is contained in the manager", func(t *testing.T) { // set a size > 1 just to make sure it gets all - actualRTs, _, err := reg.RelationTupleManager().GetRelationTuples(context.Background(), (*relationtuple.RelationQuery)(rt), x.WithSize(10)) + actualRTs, _, err := reg.RelationTupleManager().GetRelationTuples(context.Background(), rt.ToQuery(), x.WithSize(10)) require.NoError(t, err) assert.Equal(t, []*relationtuple.InternalRelationTuple{rt}, actualRTs) }) @@ -155,14 +155,16 @@ func TestWriteHandlers(t *testing.T) { } require.NoError(t, reg.RelationTupleManager().WriteRelationTuples(context.Background(), rt)) - req, err := http.NewRequest(http.MethodDelete, ts.URL+relationtuple.RouteBase+"?"+rt.ToURLQuery().Encode(), nil) + q, err := rt.ToURLQuery() + require.NoError(t, err) + req, err := http.NewRequest(http.MethodDelete, ts.URL+relationtuple.RouteBase+"?"+q.Encode(), nil) require.NoError(t, err) resp, err := ts.Client().Do(req) require.NoError(t, err) assert.Equal(t, http.StatusNoContent, resp.StatusCode) // set a size > 1 just to make sure it gets all - actualRTs, _, err := reg.RelationTupleManager().GetRelationTuples(context.Background(), (*relationtuple.RelationQuery)(rt), x.WithSize(10)) + actualRTs, _, err := reg.RelationTupleManager().GetRelationTuples(context.Background(), rt.ToQuery(), x.WithSize(10)) require.NoError(t, err) assert.Equal(t, []*relationtuple.InternalRelationTuple{}, actualRTs) }) @@ -243,7 +245,7 @@ func TestWriteHandlers(t *testing.T) { assert.Equal(t, http.StatusNotFound, resp.StatusCode) // set a size > 1 just to make sure it gets all - actualRTs, _, err := reg.RelationTupleManager().GetRelationTuples(context.Background(), (*relationtuple.RelationQuery)(deltas[0].RelationTuple), x.WithSize(10)) + actualRTs, _, err := reg.RelationTupleManager().GetRelationTuples(context.Background(), deltas[0].RelationTuple.ToQuery(), x.WithSize(10)) require.NoError(t, err) assert.Len(t, actualRTs, 0) }) @@ -340,7 +342,7 @@ func TestWriteHandlers(t *testing.T) { "namespace":"role", "object":"super-admin", "relation":"member", - "subject":"role:company-admin" + "subject_id":"role:company-admin" } } ]` diff --git a/spec/api.json b/spec/api.json index 4a3205a0a..dd51daf30 100755 --- a/spec/api.json +++ b/spec/api.json @@ -67,9 +67,27 @@ "required": true }, { - "type": "object", - "description": "Subject of the Relation Tuple\n\nThe subject follows the subject string encoding format.", - "name": "subject", + "type": "string", + "description": "SubjectID of the Relation Tuple", + "name": "subject_id", + "in": "query" + }, + { + "type": "string", + "description": "Namespace of the Subject Set", + "name": "subject_set.namespace", + "in": "query" + }, + { + "type": "string", + "description": "Object of the Subject Set", + "name": "subject_set.object", + "in": "query" + }, + { + "type": "string", + "description": "Relation of the Subject Set", + "name": "subject_set.relation", "in": "query" } ], @@ -172,7 +190,7 @@ "name": "Payload", "in": "body", "schema": { - "$ref": "#/definitions/InternalRelationTuple" + "$ref": "#/definitions/RelationQuery" } } ], @@ -275,21 +293,21 @@ "parameters": [ { "type": "string", - "description": "Namespace of the Relation Tuple", + "description": "Namespace of the Subject Set", "name": "namespace", "in": "query", "required": true }, { "type": "string", - "description": "Object of the Relation Tuple", + "description": "Object of the Subject Set", "name": "object", "in": "query", "required": true }, { "type": "string", - "description": "Relation of the Relation Tuple", + "description": "Relation of the Subject Set", "name": "relation", "in": "query", "required": true @@ -504,23 +522,47 @@ "parameters": [ { "type": "string", + "description": "Namespace of the Relation Tuple", "name": "namespace", "in": "query", "required": true }, { "type": "string", + "description": "Object of the Relation Tuple", "name": "object", - "in": "query" + "in": "query", + "required": true }, { "type": "string", + "description": "Relation of the Relation Tuple", "name": "relation", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "SubjectID of the Relation Tuple", + "name": "subject_id", + "in": "query" + }, + { + "type": "string", + "description": "Namespace of the Subject Set", + "name": "subject_set.namespace", + "in": "query" + }, + { + "type": "string", + "description": "Object of the Subject Set", + "name": "subject_set.object", "in": "query" }, { - "type": "object", - "name": "subject", + "type": "string", + "description": "Relation of the Subject Set", + "name": "subject_set.relation", "in": "query" }, { @@ -628,15 +670,15 @@ "name": "Payload", "in": "body", "schema": { - "$ref": "#/definitions/InternalRelationTuple" + "$ref": "#/definitions/RelationQuery" } } ], "responses": { "201": { - "description": "InternalRelationTuple", + "description": "RelationQuery", "schema": { - "$ref": "#/definitions/InternalRelationTuple" + "$ref": "#/definitions/RelationQuery" } }, "400": { @@ -743,9 +785,27 @@ "required": true }, { - "type": "object", - "description": "Subject of the Relation Tuple\n\nThe subject follows the subject string encoding format.", - "name": "subject", + "type": "string", + "description": "SubjectID of the Relation Tuple", + "name": "subject_id", + "in": "query" + }, + { + "type": "string", + "description": "Namespace of the Subject Set", + "name": "subject_set.namespace", + "in": "query" + }, + { + "type": "string", + "description": "Object of the Subject Set", + "name": "subject_set.object", + "in": "query" + }, + { + "type": "string", + "description": "Relation of the Subject Set", + "name": "subject_set.relation", "in": "query" } ], @@ -974,24 +1034,27 @@ "required": [ "namespace", "object", - "relation", - "subject" + "relation" ], "properties": { "namespace": { - "description": "Namespace of the Relation Tuple\n\nin: query", + "description": "Namespace of the Relation Tuple", "type": "string" }, "object": { - "description": "Object of the Relation Tuple\n\nin: query", + "description": "Object of the Relation Tuple", "type": "string" }, "relation": { - "description": "Relation of the Relation Tuple\n\nin: query", + "description": "Relation of the Relation Tuple", + "type": "string" + }, + "subject_id": { + "description": "SubjectID of the Relation Tuple\n\nEither SubjectSet or SubjectID are required.", "type": "string" }, - "subject": { - "$ref": "#/definitions/subject" + "subject_set": { + "$ref": "#/definitions/SubjectSet" } } }, @@ -1010,11 +1073,59 @@ } } }, + "RelationQuery": { + "type": "object", + "required": [ + "namespace" + ], + "properties": { + "namespace": { + "description": "Namespace of the Relation Tuple", + "type": "string" + }, + "object": { + "description": "Object of the Relation Tuple", + "type": "string" + }, + "relation": { + "description": "Relation of the Relation Tuple", + "type": "string" + }, + "subject_id": { + "description": "SubjectID of the Relation Tuple\n\nEither SubjectSet or SubjectID can be provided.", + "type": "string" + }, + "subject_set": { + "$ref": "#/definitions/SubjectSet" + } + } + }, + "SubjectSet": { + "type": "object", + "required": [ + "namespace", + "object", + "relation" + ], + "properties": { + "namespace": { + "description": "Namespace of the Subject Set", + "type": "string" + }, + "object": { + "description": "Object of the Subject Set", + "type": "string" + }, + "relation": { + "description": "Relation of the Subject Set", + "type": "string" + } + } + }, "expandTree": { "type": "object", "required": [ - "type", - "subject" + "type" ], "properties": { "children": { @@ -1023,8 +1134,11 @@ "$ref": "#/definitions/expandTree" } }, - "subject": { - "$ref": "#/definitions/subject" + "subject_id": { + "type": "string" + }, + "subject_set": { + "$ref": "#/definitions/SubjectSet" }, "type": { "type": "string", @@ -1087,9 +1201,6 @@ } } }, - "subject": { - "type": "object" - }, "version": { "type": "object", "properties": { diff --git a/spec/swagger.json b/spec/swagger.json index 04044fc34..6753b1843 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -67,9 +67,27 @@ "required": true }, { - "$ref": "#/definitions/subject", - "description": "Subject of the Relation Tuple\n\nThe subject follows the subject string encoding format.", - "name": "subject", + "type": "string", + "description": "SubjectID of the Relation Tuple", + "name": "subject_id", + "in": "query" + }, + { + "type": "string", + "description": "Namespace of the Subject Set", + "name": "subject_set.namespace", + "in": "query" + }, + { + "type": "string", + "description": "Object of the Subject Set", + "name": "subject_set.object", + "in": "query" + }, + { + "type": "string", + "description": "Relation of the Subject Set", + "name": "subject_set.relation", "in": "query" } ], @@ -81,7 +99,35 @@ } }, "400": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } }, "403": { "description": "getCheckResponse", @@ -90,7 +136,35 @@ } }, "500": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } } } }, @@ -116,7 +190,7 @@ "name": "Payload", "in": "body", "schema": { - "$ref": "#/definitions/InternalRelationTuple" + "$ref": "#/definitions/RelationQuery" } } ], @@ -128,7 +202,35 @@ } }, "400": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } }, "403": { "description": "getCheckResponse", @@ -137,7 +239,35 @@ } }, "500": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } } } } @@ -163,21 +293,21 @@ "parameters": [ { "type": "string", - "description": "Namespace of the Relation Tuple", + "description": "Namespace of the Subject Set", "name": "namespace", "in": "query", "required": true }, { "type": "string", - "description": "Object of the Relation Tuple", + "description": "Object of the Subject Set", "name": "object", "in": "query", "required": true }, { "type": "string", - "description": "Relation of the Relation Tuple", + "description": "Relation of the Subject Set", "name": "relation", "in": "query", "required": true @@ -197,13 +327,97 @@ } }, "400": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } }, "404": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } }, "500": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } } } } @@ -227,7 +441,35 @@ } }, "500": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } } } } @@ -280,23 +522,47 @@ "parameters": [ { "type": "string", + "description": "Namespace of the Relation Tuple", "name": "namespace", "in": "query", "required": true }, { "type": "string", + "description": "Object of the Relation Tuple", "name": "object", - "in": "query" + "in": "query", + "required": true }, { "type": "string", + "description": "Relation of the Relation Tuple", "name": "relation", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "SubjectID of the Relation Tuple", + "name": "subject_id", + "in": "query" + }, + { + "type": "string", + "description": "Namespace of the Subject Set", + "name": "subject_set.namespace", + "in": "query" + }, + { + "type": "string", + "description": "Object of the Subject Set", + "name": "subject_set.object", "in": "query" }, { - "$ref": "#/definitions/subject", - "name": "subject", + "type": "string", + "description": "Relation of the Subject Set", + "name": "subject_set.relation", "in": "query" }, { @@ -319,10 +585,66 @@ } }, "404": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } }, "500": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } } } }, @@ -348,22 +670,78 @@ "name": "Payload", "in": "body", "schema": { - "$ref": "#/definitions/InternalRelationTuple" + "$ref": "#/definitions/RelationQuery" } } ], "responses": { "201": { - "description": "InternalRelationTuple", + "description": "RelationQuery", "schema": { - "$ref": "#/definitions/InternalRelationTuple" + "$ref": "#/definitions/RelationQuery" } }, "400": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } }, "500": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } } } }, @@ -407,21 +785,95 @@ "required": true }, { - "$ref": "#/definitions/subject", - "description": "Subject of the Relation Tuple\n\nThe subject follows the subject string encoding format.", - "name": "subject", + "type": "string", + "description": "SubjectID of the Relation Tuple", + "name": "subject_id", + "in": "query" + }, + { + "type": "string", + "description": "Namespace of the Subject Set", + "name": "subject_set.namespace", + "in": "query" + }, + { + "type": "string", + "description": "Object of the Subject Set", + "name": "subject_set.object", + "in": "query" + }, + { + "type": "string", + "description": "Relation of the Subject Set", + "name": "subject_set.relation", "in": "query" } ], "responses": { "204": { - "$ref": "#/responses/emptyResponse" + "description": "Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is typically 201." }, "400": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } }, "500": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } } } }, @@ -456,16 +908,100 @@ ], "responses": { "204": { - "$ref": "#/responses/emptyResponse" + "description": "Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is typically 201." }, "400": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } }, "404": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } }, "500": { - "$ref": "#/responses/genericError" + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "request": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } } } } @@ -498,24 +1034,27 @@ "required": [ "namespace", "object", - "relation", - "subject" + "relation" ], "properties": { "namespace": { - "description": "Namespace of the Relation Tuple\n\nin: query", + "description": "Namespace of the Relation Tuple", "type": "string" }, "object": { - "description": "Object of the Relation Tuple\n\nin: query", + "description": "Object of the Relation Tuple", "type": "string" }, "relation": { - "description": "Relation of the Relation Tuple\n\nin: query", + "description": "Relation of the Relation Tuple", "type": "string" }, - "subject": { - "$ref": "#/definitions/subject" + "subject_id": { + "description": "SubjectID of the Relation Tuple\n\nEither SubjectSet or SubjectID are required.", + "type": "string" + }, + "subject_set": { + "$ref": "#/definitions/SubjectSet" } } }, @@ -534,6 +1073,55 @@ } } }, + "RelationQuery": { + "type": "object", + "required": [ + "namespace" + ], + "properties": { + "namespace": { + "description": "Namespace of the Relation Tuple", + "type": "string" + }, + "object": { + "description": "Object of the Relation Tuple", + "type": "string" + }, + "relation": { + "description": "Relation of the Relation Tuple", + "type": "string" + }, + "subject_id": { + "description": "SubjectID of the Relation Tuple\n\nEither SubjectSet or SubjectID can be provided.", + "type": "string" + }, + "subject_set": { + "$ref": "#/definitions/SubjectSet" + } + } + }, + "SubjectSet": { + "type": "object", + "required": [ + "namespace", + "object", + "relation" + ], + "properties": { + "namespace": { + "description": "Namespace of the Subject Set", + "type": "string" + }, + "object": { + "description": "Object of the Subject Set", + "type": "string" + }, + "relation": { + "description": "Relation of the Subject Set", + "type": "string" + } + } + }, "expandTree": { "type": "object", "required": [ @@ -561,50 +1149,6 @@ } } }, - "genericError": { - "type": "object", - "required": [ - "message" - ], - "properties": { - "code": { - "description": "The status code", - "type": "integer", - "format": "int64", - "example": 404 - }, - "debug": { - "description": "Debug information\n\nThis field is often not exposed to protect against leaking\nsensitive information.", - "type": "string", - "example": "SQL field \"foo\" is not a bool." - }, - "details": { - "description": "Further error details", - "type": "object", - "additionalProperties": true - }, - "message": { - "description": "Error message\n\nThe error's message.", - "type": "string", - "example": "The resource could not be found" - }, - "reason": { - "description": "A human-readable reason for the error", - "type": "string", - "example": "User with ID 1234 does not exist." - }, - "request": { - "description": "The request ID\n\nThe request ID is often exposed internally in order to trace\nerrors across service architectures. This is often a UUID.", - "type": "string", - "example": "d7ef54b1-ec15-46e6-bccb-524b82c035e6" - }, - "status": { - "description": "The status description", - "type": "string", - "example": "Not Found" - } - } - }, "getCheckResponse": { "description": "The content of the allowed field is mirrored in the HTTP status code.", "type": "object", @@ -667,41 +1211,5 @@ } } } - ,"UUID":{"type": "string", "format": "uuid4"}}, - "responses": { - "emptyResponse": { - "description": "Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is typically 201." - }, - "genericError": { - "description": "The standard error format", - "schema": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int64" - }, - "details": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true - } - }, - "message": { - "type": "string" - }, - "reason": { - "type": "string" - }, - "request": { - "type": "string" - }, - "status": { - "type": "string" - } - } - } - } } } \ No newline at end of file From 2db99b573b4808e20e62577e913f43a35642ede5 Mon Sep 17 00:00:00 2001 From: zepatrik Date: Thu, 23 Sep 2021 11:03:18 +0200 Subject: [PATCH 3/9] chore: use go1.17 --- .circleci/config.yml | 6 +++--- .github/workflows/buf.yml | 2 +- .github/workflows/release-go-grpc-client.yml | 2 +- go.mod | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9658b440a..a2351a7fd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -14,7 +14,7 @@ orbs: jobs: test: docker: - - image: cimg/go:1.16 + - image: cimg/go:1.17 environment: TEST_DATABASE_POSTGRESQL: postgres://test:test@localhost:5432/keto?sslmode=disable TEST_DATABASE_MYSQL: mysql://root:test@(localhost:3306)/mysql?parseTime=true&multiStatements=true @@ -53,7 +53,7 @@ jobs: test-race: docker: - - image: cimg/go:1.16 + - image: cimg/go:1.17 steps: - checkout - go/load-cache @@ -64,7 +64,7 @@ jobs: validate: docker: - - image: cimg/go:1.16-node + - image: cimg/go:1.17-node steps: - checkout diff --git a/.github/workflows/buf.yml b/.github/workflows/buf.yml index 217bfab47..60b87533c 100644 --- a/.github/workflows/buf.yml +++ b/.github/workflows/buf.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: '1.16' + go-version: '1.17' - uses: actions/setup-node@v2 with: node-version: '15' diff --git a/.github/workflows/release-go-grpc-client.yml b/.github/workflows/release-go-grpc-client.yml index 3df361a23..e6064f60e 100644 --- a/.github/workflows/release-go-grpc-client.yml +++ b/.github/workflows/release-go-grpc-client.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: '1.16' + go-version: '1.17' - name: Download dependencies run: cd proto; go mod tidy - name: Test diff --git a/go.mod b/go.mod index 3a337823c..1d6ec397a 100644 --- a/go.mod +++ b/go.mod @@ -55,4 +55,4 @@ require ( google.golang.org/protobuf v1.26.0 ) -go 1.16 +go 1.17 From bdd176de613b44b74ef382afb7485cf65021ba9d Mon Sep 17 00:00:00 2001 From: zepatrik Date: Thu, 23 Sep 2021 11:27:38 +0200 Subject: [PATCH 4/9] chore: fix linter issues & clidoc CI --- .circleci/config.yml | 10 +- go.mod | 151 ++++++++++++++++++ internal/relationtuple/definitions.go | 41 ----- internal/relationtuple/swagger_definitions.go | 43 +++++ 4 files changed, 203 insertions(+), 42 deletions(-) create mode 100644 internal/relationtuple/swagger_definitions.go diff --git a/.circleci/config.yml b/.circleci/config.yml index a2351a7fd..15611e4cb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -95,6 +95,14 @@ jobs: # Test documentation examples - run: make test-docs-samples + clidocs: + docker: + - image: cimg/go:1.17-node + steps: + - checkout + - run: + name: Build and push CLI docs + command: "bash <(curl -s https://raw.githubusercontent.com/ory/ci/master/src/scripts/docs/cli.sh)" workflows: version: 2 @@ -124,7 +132,7 @@ workflows: only: /v.*/ branches: only: master - - docs/cli + - clidocs - docs/build: requires: - test diff --git a/go.mod b/go.mod index 1d6ec397a..d04037eb8 100644 --- a/go.mod +++ b/go.mod @@ -55,4 +55,155 @@ require ( google.golang.org/protobuf v1.26.0 ) +require ( + github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect + github.com/DataDog/datadog-go v4.0.0+incompatible // indirect + github.com/Masterminds/semver/v3 v3.1.1 // indirect + github.com/Microsoft/go-winio v0.4.14 // indirect + github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/armon/go-radix v1.0.0 // indirect + github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect + github.com/cenkalti/backoff/v4 v4.1.0 // indirect + github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/cockroachdb/cockroach-go/v2 v2.1.1 // indirect + github.com/containerd/containerd v1.4.3 // indirect + github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgraph-io/ristretto v0.1.0 // indirect + github.com/docker/distribution v2.7.1+incompatible // indirect + github.com/docker/docker v17.12.0-ce-rc1.0.20201201034508-7d75c1d40d88+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.4.0 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect + github.com/elastic/go-licenser v0.3.1 // indirect + github.com/elastic/go-sysinfo v1.1.1 // indirect + github.com/elastic/go-windows v1.0.0 // indirect + github.com/fatih/color v1.9.0 // indirect + github.com/fatih/structs v1.1.0 // indirect + github.com/fsnotify/fsnotify v1.4.9 // indirect + github.com/go-openapi/analysis v0.20.0 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.5 // indirect + github.com/go-openapi/loads v0.20.2 // indirect + github.com/go-openapi/spec v0.20.3 // indirect + github.com/go-sql-driver/mysql v1.5.0 // indirect + github.com/go-stack/stack v1.8.0 // indirect + github.com/gobuffalo/envy v1.9.0 // indirect + github.com/gobuffalo/fizz v1.13.1-0.20201104174146-3416f0e6618f // indirect + github.com/gobuffalo/flect v0.2.1 // indirect + github.com/gobuffalo/github_flavored_markdown v1.1.0 // indirect + github.com/gobuffalo/helpers v0.6.1 // indirect + github.com/gobuffalo/here v0.6.0 // indirect + github.com/gobuffalo/nulls v0.3.0 // indirect + github.com/gobuffalo/packd v1.0.0 // indirect + github.com/gobuffalo/plush/v4 v4.0.0 // indirect + github.com/gobuffalo/tags/v3 v3.1.0 // indirect + github.com/gobuffalo/validate/v3 v3.2.0 // indirect + github.com/gofrs/uuid/v3 v3.1.2 // indirect + github.com/gogo/protobuf v1.2.1 // indirect + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/uuid v1.2.0 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + github.com/hashicorp/go-cleanhttp v0.5.1 // indirect + github.com/hashicorp/go-retryablehttp v0.6.8 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/inhies/go-bytesize v0.0.0-20201103132853-d0aed0d254f8 // indirect + github.com/instana/go-sensor v1.29.0 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.8.1 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.0.6 // indirect + github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect + github.com/jackc/pgtype v1.7.0 // indirect + github.com/jackc/pgx/v4 v4.11.0 // indirect + github.com/jandelgado/gcov2lcov v1.0.4 // indirect + github.com/jcchavezs/porto v0.1.0 // indirect + github.com/jmoiron/sqlx v1.3.3 // indirect + github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect + github.com/joho/godotenv v1.3.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/knadh/koanf v1.0.0 // indirect + github.com/lib/pq v1.10.1 // indirect + github.com/looplab/fsm v0.1.0 // indirect + github.com/magiconair/properties v1.8.4 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/markbates/pkger v0.17.1 // indirect + github.com/mattn/go-colorable v0.1.6 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect + github.com/microcosm-cc/bluemonday v1.0.2 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect + github.com/opencontainers/go-digest v1.0.0-rc1 // indirect + github.com/opencontainers/image-spec v1.0.1 // indirect + github.com/opencontainers/runc v1.0.0-rc9 // indirect + github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 // indirect + github.com/openzipkin/zipkin-go v0.2.2 // indirect + github.com/ory/dockertest/v3 v3.6.5 // indirect + github.com/ory/go-acc v0.2.6 // indirect + github.com/ory/viper v1.7.5 // indirect + github.com/pborman/uuid v1.2.1 // indirect + github.com/philhofer/fwd v1.0.0 // indirect + github.com/pkg/profile v1.2.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/procfs v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.5.2 // indirect + github.com/russross/blackfriday/v2 v2.0.1 // indirect + github.com/santhosh-tekuri/jsonschema v1.2.4 // indirect + github.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 // indirect + github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect + github.com/sergi/go-diff v1.1.0 // indirect + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect + github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d // indirect + github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e // indirect + github.com/spf13/afero v1.5.1 // indirect + github.com/spf13/cast v1.3.2-0.20200723214538-8d17101741c8 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518 // indirect + github.com/subosito/gotenv v1.2.0 // indirect + github.com/tidwall/match v1.0.3 // indirect + github.com/tidwall/pretty v1.1.0 // indirect + github.com/tinylib/msgp v1.1.2 // indirect + github.com/uber/jaeger-client-go v2.22.1+incompatible // indirect + github.com/uber/jaeger-lib v2.2.0+incompatible // indirect + github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect + go.elastic.co/apm v1.13.0 // indirect + go.elastic.co/apm/module/apmhttp v1.13.0 // indirect + go.elastic.co/apm/module/apmot v1.13.0 // indirect + go.elastic.co/fastjson v1.1.0 // indirect + go.mongodb.org/mongo-driver v1.4.6 // indirect + go.opentelemetry.io/contrib v0.20.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.20.0 // indirect + go.opentelemetry.io/otel v0.20.0 // indirect + go.opentelemetry.io/otel/metric v0.20.0 // indirect + go.opentelemetry.io/otel/trace v0.20.0 // indirect + go.uber.org/atomic v1.6.0 // indirect + golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect + golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect + golang.org/x/mod v0.4.2 // indirect + golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 // indirect + golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 // indirect + golang.org/x/text v0.3.5 // indirect + golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect + golang.org/x/tools v0.1.0 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/genproto v0.0.0-20210503173045-b96a97608f20 // indirect + gopkg.in/DataDog/dd-trace-go.v1 v1.27.0 // indirect + gopkg.in/ini.v1 v1.62.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect +) + go 1.17 diff --git a/internal/relationtuple/definitions.go b/internal/relationtuple/definitions.go index 0048af266..0bed1fc4f 100644 --- a/internal/relationtuple/definitions.go +++ b/internal/relationtuple/definitions.go @@ -65,33 +65,6 @@ type RelationQuery struct { SubjectSet *SubjectSet `json:"subject_set,omitempty"` } -// swagger:model InternalRelationTuple -type relationTupleWithRequired struct { - // Namespace of the Relation Tuple - // - // required: true - Namespace string `json:"namespace"` - - // Object of the Relation Tuple - // - // required: true - Object string `json:"object"` - - // Relation of the Relation Tuple - // - // required: true - Relation string `json:"relation"` - - // SubjectID of the Relation Tuple - // - // Either SubjectSet or SubjectID are required. - SubjectID *string `json:"subject_id,omitempty"` - // SubjectSet of the Relation Tuple - // - // Either SubjectSet or SubjectID are required. - SubjectSet *SubjectSet `json:"subject_set,omitempty"` -} - // swagger:ignore type TupleData interface { // swagger:ignore @@ -162,20 +135,6 @@ const ( ActionDelete patchAction = "delete" ) -// The patch request payload -// -// swagger:parameters patchRelationTuples -// nolint:deadcode,unused -type patchPayload struct { - // in:body - Payload []*PatchDelta -} - -type PatchDelta struct { - Action patchAction `json:"action"` - RelationTuple *InternalRelationTuple `json:"relation_tuple"` -} - func SubjectFromString(s string) (Subject, error) { if strings.Contains(s, "#") { return (&SubjectSet{}).FromString(s) diff --git a/internal/relationtuple/swagger_definitions.go b/internal/relationtuple/swagger_definitions.go new file mode 100644 index 000000000..7c32e2a38 --- /dev/null +++ b/internal/relationtuple/swagger_definitions.go @@ -0,0 +1,43 @@ +package relationtuple + +// swagger:model InternalRelationTuple +// nolint:deadcode,unused +type relationTupleWithRequired struct { + // Namespace of the Relation Tuple + // + // required: true + Namespace string `json:"namespace"` + + // Object of the Relation Tuple + // + // required: true + Object string `json:"object"` + + // Relation of the Relation Tuple + // + // required: true + Relation string `json:"relation"` + + // SubjectID of the Relation Tuple + // + // Either SubjectSet or SubjectID are required. + SubjectID *string `json:"subject_id,omitempty"` + // SubjectSet of the Relation Tuple + // + // Either SubjectSet or SubjectID are required. + SubjectSet *SubjectSet `json:"subject_set,omitempty"` +} + +// The patch request payload +// +// swagger:parameters patchRelationTuples +// nolint:deadcode,unused +type patchPayload struct { + // in:body + Payload []*PatchDelta +} + +type PatchDelta struct { + Action patchAction `json:"action"` + RelationTuple *InternalRelationTuple `json:"relation_tuple"` +} From d9dc9f9d53077023858eefbf48e153b3856671c1 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Thu, 23 Sep 2021 09:28:34 +0000 Subject: [PATCH 5/9] autogen(docs): generate cli docs --- docs/docs/cli/keto-check.md | 9 ++++---- docs/docs/cli/keto-expand.md | 6 +++--- docs/docs/cli/keto-migrate-down.md | 12 +++++------ docs/docs/cli/keto-migrate-status.md | 10 ++++----- docs/docs/cli/keto-migrate-up.md | 10 ++++----- docs/docs/cli/keto-migrate.md | 16 +++++++------- docs/docs/cli/keto-namespace-validate.md | 13 ++++++------ docs/docs/cli/keto-namespace.md | 9 ++++---- docs/docs/cli/keto-relation-tuple-create.md | 13 ++++++------ docs/docs/cli/keto-relation-tuple-delete.md | 16 +++++++------- docs/docs/cli/keto-relation-tuple-get.md | 10 ++++----- docs/docs/cli/keto-relation-tuple-parse.md | 13 ++++++------ docs/docs/cli/keto-relation-tuple.md | 17 ++++++++------- docs/docs/cli/keto-serve.md | 11 +++++----- docs/docs/cli/keto-status.md | 9 ++++---- docs/docs/cli/keto-version.md | 6 +++--- docs/docs/cli/keto.md | 23 ++++++++++++--------- 17 files changed, 108 insertions(+), 95 deletions(-) diff --git a/docs/docs/cli/keto-check.md b/docs/docs/cli/keto-check.md index 610b734aa..01eaf616e 100644 --- a/docs/docs/cli/keto-check.md +++ b/docs/docs/cli/keto-check.md @@ -9,13 +9,15 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto check Check whether a subject has a relation on an object ### Synopsis -Check whether a subject has a relation on an object. This method resolves subject sets and subject set rewrites. +Check whether a subject has a relation on an object. This method resolves +subject sets and subject set rewrites. ``` keto check <subject> <relation> <namespace> <object> [flags] @@ -34,10 +36,9 @@ keto check <subject> <relation> <namespace> <object> [fl ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto](keto) - Global and consistent permission and authorization server - +- [keto](keto) - Global and consistent permission and authorization server diff --git a/docs/docs/cli/keto-expand.md b/docs/docs/cli/keto-expand.md index bcd2c6ffe..8f9ac06c1 100644 --- a/docs/docs/cli/keto-expand.md +++ b/docs/docs/cli/keto-expand.md @@ -9,6 +9,7 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto expand Expand a subject set @@ -35,10 +36,9 @@ keto expand <relation> <namespace> <object> [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto](keto) - Global and consistent permission and authorization server - +- [keto](keto) - Global and consistent permission and authorization server diff --git a/docs/docs/cli/keto-migrate-down.md b/docs/docs/cli/keto-migrate-down.md index d75edc5bf..466ca27fd 100644 --- a/docs/docs/cli/keto-migrate-down.md +++ b/docs/docs/cli/keto-migrate-down.md @@ -9,15 +9,16 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto migrate down Migrate the database down ### Synopsis -Migrate the database down a specific amount of steps. -Pass 0 steps to fully migrate down. -This does not affect namespaces. Use `keto namespace migrate down` for migrating namespaces. +Migrate the database down a specific amount of steps. Pass 0 steps to fully +migrate down. This does not affect namespaces. Use `keto namespace migrate down` +for migrating namespaces. ``` keto migrate down <steps> [flags] @@ -35,10 +36,9 @@ keto migrate down <steps> [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto migrate](keto-migrate) - Commands to migrate the database - +- [keto migrate](keto-migrate) - Commands to migrate the database diff --git a/docs/docs/cli/keto-migrate-status.md b/docs/docs/cli/keto-migrate-status.md index d5581b667..154640dd1 100644 --- a/docs/docs/cli/keto-migrate-status.md +++ b/docs/docs/cli/keto-migrate-status.md @@ -9,14 +9,15 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto migrate status Get the current migration status ### Synopsis -Get the current migration status. -This does not affect namespaces. Use `keto namespace migrate status` for migrating namespaces. +Get the current migration status. This does not affect namespaces. Use +`keto namespace migrate status` for migrating namespaces. ``` keto migrate status [flags] @@ -33,10 +34,9 @@ keto migrate status [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto migrate](keto-migrate) - Commands to migrate the database - +- [keto migrate](keto-migrate) - Commands to migrate the database diff --git a/docs/docs/cli/keto-migrate-up.md b/docs/docs/cli/keto-migrate-up.md index 082a7f611..0e90f2c4c 100644 --- a/docs/docs/cli/keto-migrate-up.md +++ b/docs/docs/cli/keto-migrate-up.md @@ -9,14 +9,15 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto migrate up Migrate the database up ### Synopsis -Migrate the database up. -This does not affect namespaces. Use `keto namespace migrate up` for migrating namespaces. +Migrate the database up. This does not affect namespaces. Use +`keto namespace migrate up` for migrating namespaces. ``` keto migrate up [flags] @@ -35,10 +36,9 @@ keto migrate up [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto migrate](keto-migrate) - Commands to migrate the database - +- [keto migrate](keto-migrate) - Commands to migrate the database diff --git a/docs/docs/cli/keto-migrate.md b/docs/docs/cli/keto-migrate.md index 6889bf96b..a06fac2de 100644 --- a/docs/docs/cli/keto-migrate.md +++ b/docs/docs/cli/keto-migrate.md @@ -9,14 +9,15 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto migrate Commands to migrate the database ### Synopsis -Commands to migrate the database. -This does not affect namespaces. Use `keto namespace migrate` for migrating namespaces. +Commands to migrate the database. This does not affect namespaces. Use +`keto namespace migrate` for migrating namespaces. ### Options @@ -27,13 +28,12 @@ This does not affect namespaces. Use `keto namespace migrate` for migrating name ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto](keto) - Global and consistent permission and authorization server -* [keto migrate down](keto-migrate-down) - Migrate the database down -* [keto migrate status](keto-migrate-status) - Get the current migration status -* [keto migrate up](keto-migrate-up) - Migrate the database up - +- [keto](keto) - Global and consistent permission and authorization server +- [keto migrate down](keto-migrate-down) - Migrate the database down +- [keto migrate status](keto-migrate-status) - Get the current migration status +- [keto migrate up](keto-migrate-up) - Migrate the database up diff --git a/docs/docs/cli/keto-namespace-validate.md b/docs/docs/cli/keto-namespace-validate.md index 52548ade3..d98d246c2 100644 --- a/docs/docs/cli/keto-namespace-validate.md +++ b/docs/docs/cli/keto-namespace-validate.md @@ -9,16 +9,16 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto namespace validate Validate namespace definitions ### Synopsis -validate -Validates namespace definitions. Parses namespace yaml files or configuration -files passed via the configuration flag. Returns human readable errors. Useful for -debugging. +validate Validates namespace definitions. Parses namespace yaml files or +configuration files passed via the configuration flag. Returns human readable +errors. Useful for debugging. ``` keto namespace validate <namespace.yml> [<namespace2.yml> ...] | validate -c <config.yaml> [flags] @@ -33,10 +33,9 @@ keto namespace validate <namespace.yml> [<namespace2.yml> ...] | val ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto namespace](keto-namespace) - Read and manipulate namespaces - +- [keto namespace](keto-namespace) - Read and manipulate namespaces diff --git a/docs/docs/cli/keto-namespace.md b/docs/docs/cli/keto-namespace.md index b2d85140e..5f7c6b20b 100644 --- a/docs/docs/cli/keto-namespace.md +++ b/docs/docs/cli/keto-namespace.md @@ -9,6 +9,7 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto namespace Read and manipulate namespaces @@ -22,11 +23,11 @@ Read and manipulate namespaces ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto](keto) - Global and consistent permission and authorization server -* [keto namespace validate](keto-namespace-validate) - Validate namespace definitions - +- [keto](keto) - Global and consistent permission and authorization server +- [keto namespace validate](keto-namespace-validate) - Validate namespace + definitions diff --git a/docs/docs/cli/keto-relation-tuple-create.md b/docs/docs/cli/keto-relation-tuple-create.md index c288e7e01..4c67d1754 100644 --- a/docs/docs/cli/keto-relation-tuple-create.md +++ b/docs/docs/cli/keto-relation-tuple-create.md @@ -9,15 +9,16 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto relation-tuple create Create relation tuples from JSON files ### Synopsis -Create relation tuples from JSON files. -A directory will be traversed and all relation tuples will be created. -Pass the special filename `-` to read from STD_IN. +Create relation tuples from JSON files. A directory will be traversed and all +relation tuples will be created. Pass the special filename `-` to read from +STD_IN. ``` keto relation-tuple create <relation-tuple.json> [<relation-tuple-dir>] [flags] @@ -36,10 +37,10 @@ keto relation-tuple create <relation-tuple.json> [<relation-tuple-dir&g ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation tuples - +- [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation + tuples diff --git a/docs/docs/cli/keto-relation-tuple-delete.md b/docs/docs/cli/keto-relation-tuple-delete.md index 3af8bf726..ac9c88677 100644 --- a/docs/docs/cli/keto-relation-tuple-delete.md +++ b/docs/docs/cli/keto-relation-tuple-delete.md @@ -1,7 +1,8 @@ --- id: keto-relation-tuple-delete title: keto relation-tuple delete -description: keto relation-tuple delete Delete relation tuples defined in JSON files +description: + keto relation-tuple delete Delete relation tuples defined in JSON files --- + ## keto relation-tuple delete Delete relation tuples defined in JSON files ### Synopsis -Delete relation tuples defined in the given JSON files. -A directory will be traversed and all relation tuples will be deleted. -Pass the special filename `-` to read from STD_IN. +Delete relation tuples defined in the given JSON files. A directory will be +traversed and all relation tuples will be deleted. Pass the special filename `-` +to read from STD_IN. ``` keto relation-tuple delete <relation-tuple.json> [<relation-tuple-dir>] [flags] @@ -36,10 +38,10 @@ keto relation-tuple delete <relation-tuple.json> [<relation-tuple-dir&g ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation tuples - +- [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation + tuples diff --git a/docs/docs/cli/keto-relation-tuple-get.md b/docs/docs/cli/keto-relation-tuple-get.md index 78c83202f..f09926ecb 100644 --- a/docs/docs/cli/keto-relation-tuple-get.md +++ b/docs/docs/cli/keto-relation-tuple-get.md @@ -9,14 +9,14 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto relation-tuple get Get relation tuples ### Synopsis -Get relation tuples matching the given partial tuple. -Returns paginated results. +Get relation tuples matching the given partial tuple. Returns paginated results. ``` keto relation-tuple get <namespace> [flags] @@ -41,10 +41,10 @@ keto relation-tuple get <namespace> [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation tuples - +- [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation + tuples diff --git a/docs/docs/cli/keto-relation-tuple-parse.md b/docs/docs/cli/keto-relation-tuple-parse.md index c9c1f1e68..7503679fc 100644 --- a/docs/docs/cli/keto-relation-tuple-parse.md +++ b/docs/docs/cli/keto-relation-tuple-parse.md @@ -9,15 +9,16 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto relation-tuple parse Parse human readable relation tuples ### Synopsis -Parse human readable relation tuples as used in the documentation. -Supports various output formats. Especially useful for piping into other commands by using `--format json`. -Ignores comments (starting with `//`) and blank lines. +Parse human readable relation tuples as used in the documentation. Supports +various output formats. Especially useful for piping into other commands by +using `--format json`. Ignores comments (starting with `//`) and blank lines. ``` keto relation-tuple parse [flags] @@ -34,10 +35,10 @@ keto relation-tuple parse [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation tuples - +- [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation + tuples diff --git a/docs/docs/cli/keto-relation-tuple.md b/docs/docs/cli/keto-relation-tuple.md index 6f32be3ac..dbd5e41a2 100644 --- a/docs/docs/cli/keto-relation-tuple.md +++ b/docs/docs/cli/keto-relation-tuple.md @@ -9,6 +9,7 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto relation-tuple Read and manipulate relation tuples @@ -22,14 +23,16 @@ Read and manipulate relation tuples ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto](keto) - Global and consistent permission and authorization server -* [keto relation-tuple create](keto-relation-tuple-create) - Create relation tuples from JSON files -* [keto relation-tuple delete](keto-relation-tuple-delete) - Delete relation tuples defined in JSON files -* [keto relation-tuple get](keto-relation-tuple-get) - Get relation tuples -* [keto relation-tuple parse](keto-relation-tuple-parse) - Parse human readable relation tuples - +- [keto](keto) - Global and consistent permission and authorization server +- [keto relation-tuple create](keto-relation-tuple-create) - Create relation + tuples from JSON files +- [keto relation-tuple delete](keto-relation-tuple-delete) - Delete relation + tuples defined in JSON files +- [keto relation-tuple get](keto-relation-tuple-get) - Get relation tuples +- [keto relation-tuple parse](keto-relation-tuple-parse) - Parse human readable + relation tuples diff --git a/docs/docs/cli/keto-serve.md b/docs/docs/cli/keto-serve.md index 7853495ed..dd37544e9 100644 --- a/docs/docs/cli/keto-serve.md +++ b/docs/docs/cli/keto-serve.md @@ -9,6 +9,7 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto serve Starts the server and serves the HTTP REST and gRPC APIs @@ -19,8 +20,9 @@ This command opens the network ports and listens to HTTP and gRPC API requests. ## Configuration -ORY Keto can be configured using environment variables as well as a configuration file. For more information -on configuration options, open the configuration documentation: +ORY Keto can be configured using environment variables as well as a +configuration file. For more information on configuration options, open the +configuration documentation: >> https://www.ory.sh/keto/docs/reference/configuration << @@ -39,10 +41,9 @@ keto serve [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto](keto) - Global and consistent permission and authorization server - +- [keto](keto) - Global and consistent permission and authorization server diff --git a/docs/docs/cli/keto-status.md b/docs/docs/cli/keto-status.md index e659431be..f50281060 100644 --- a/docs/docs/cli/keto-status.md +++ b/docs/docs/cli/keto-status.md @@ -9,13 +9,15 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto status Get the status of the upstream Keto instance ### Synopsis -Get a status report about the upstream Keto instance. Can also block until the service is healthy. +Get a status report about the upstream Keto instance. Can also block until the +service is healthy. ``` keto status [flags] @@ -35,10 +37,9 @@ keto status [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto](keto) - Global and consistent permission and authorization server - +- [keto](keto) - Global and consistent permission and authorization server diff --git a/docs/docs/cli/keto-version.md b/docs/docs/cli/keto-version.md index 5b223e8f9..99b146c84 100644 --- a/docs/docs/cli/keto-version.md +++ b/docs/docs/cli/keto-version.md @@ -9,6 +9,7 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto version Show the build version, build time, and git hash @@ -26,10 +27,9 @@ keto version [flags] ### Options inherited from parent commands ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) ``` ### SEE ALSO -* [keto](keto) - Global and consistent permission and authorization server - +- [keto](keto) - Global and consistent permission and authorization server diff --git a/docs/docs/cli/keto.md b/docs/docs/cli/keto.md index 895cd0203..071746245 100644 --- a/docs/docs/cli/keto.md +++ b/docs/docs/cli/keto.md @@ -9,6 +9,7 @@ This file is auto-generated. To improve this file please make your change against the appropriate "./cmd/*.go" file. --> + ## keto Global and consistent permission and authorization server @@ -16,18 +17,20 @@ Global and consistent permission and authorization server ### Options ``` - -c, --config strings Config files to load, overwriting in the order specified. (default [/home/patrik/keto.yml]) + -c, --config strings Config files to load, overwriting in the order specified. (default [/home/circleci/keto.yml]) -h, --help help for keto ``` ### SEE ALSO -* [keto check](keto-check) - Check whether a subject has a relation on an object -* [keto expand](keto-expand) - Expand a subject set -* [keto migrate](keto-migrate) - Commands to migrate the database -* [keto namespace](keto-namespace) - Read and manipulate namespaces -* [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation tuples -* [keto serve](keto-serve) - Starts the server and serves the HTTP REST and gRPC APIs -* [keto status](keto-status) - Get the status of the upstream Keto instance -* [keto version](keto-version) - Show the build version, build time, and git hash - +- [keto check](keto-check) - Check whether a subject has a relation on an object +- [keto expand](keto-expand) - Expand a subject set +- [keto migrate](keto-migrate) - Commands to migrate the database +- [keto namespace](keto-namespace) - Read and manipulate namespaces +- [keto relation-tuple](keto-relation-tuple) - Read and manipulate relation + tuples +- [keto serve](keto-serve) - Starts the server and serves the HTTP REST and gRPC + APIs +- [keto status](keto-status) - Get the status of the upstream Keto instance +- [keto version](keto-version) - Show the build version, build time, and git + hash From bb3cef58ca3685b7af23098c41781c873e058aaf Mon Sep 17 00:00:00 2001 From: zepatrik Date: Thu, 23 Sep 2021 12:18:42 +0200 Subject: [PATCH 6/9] fix: docs samples --- .../01-expand-beach/expected_output.txt | 34 ++++++++++++++----- .../01-expand-beach/index.js | 18 ++++++---- .../01-list-PM/cli.sh | 2 +- .../01-list-PM/curl.sh | 2 +- .../02-list-coffee-break/cli.sh | 2 +- .../02-list-coffee-break/curl.sh | 2 +- .../00-write-direct-access/curl.sh | 2 +- .../01-check-direct-access/curl.sh | 2 +- .../99-cleanup/cli.sh | 2 +- .../99-cleanup/curl.sh | 2 +- internal/expand/tree.go | 2 +- 11 files changed, 46 insertions(+), 24 deletions(-) diff --git a/contrib/docs-code-samples/expand-api-display-access/01-expand-beach/expected_output.txt b/contrib/docs-code-samples/expand-api-display-access/01-expand-beach/expected_output.txt index ac48d83b3..c0c2556b5 100644 --- a/contrib/docs-code-samples/expand-api-display-access/01-expand-beach/expected_output.txt +++ b/contrib/docs-code-samples/expand-api-display-access/01-expand-beach/expected_output.txt @@ -1,30 +1,46 @@ { "type": "union", - "subject": "files:/photos/beach.jpg#access", "children": [ { "type": "union", - "subject": "files:/photos/beach.jpg#owner", "children": [ { "type": "leaf", - "subject": "maureen" + "subject_id": "maureen" } - ] + ], + "subject_set": { + "namespace": "files", + "object": "/photos/beach.jpg", + "relation": "owner" + } }, { "type": "union", - "subject": "directories:/photos#access", "children": [ { "type": "leaf", - "subject": "directories:/photos#owner" + "subject_set": { + "namespace": "directories", + "object": "/photos", + "relation": "owner" + } }, { "type": "leaf", - "subject": "laura" + "subject_id": "laura" } - ] + ], + "subject_set": { + "namespace": "directories", + "object": "/photos", + "relation": "access" + } } - ] + ], + "subject_set": { + "namespace": "files", + "object": "/photos/beach.jpg", + "relation": "access" + } } diff --git a/contrib/docs-code-samples/expand-api-display-access/01-expand-beach/index.js b/contrib/docs-code-samples/expand-api-display-access/01-expand-beach/index.js index 2f8cf6ca1..a8b15b2cf 100644 --- a/contrib/docs-code-samples/expand-api-display-access/01-expand-beach/index.js +++ b/contrib/docs-code-samples/expand-api-display-access/01-expand-beach/index.js @@ -19,26 +19,32 @@ expandRequest.setSubject(sub) expandRequest.setMaxDepth(3) // helper to get a nice result -const subjectString = (subject) => { +const subjectJSON = (subject) => { if (subject.hasId()) { - return subject.getId() + return { subject_id: subject.getId() } } const set = subject.getSet() - return set.getNamespace() + ':' + set.getObject() + '#' + set.getRelation() + return { + subject_set: { + namespace: set.getNamespace(), + object: set.getObject(), + relation: set.getRelation() + } + } } // helper to get a nice result const prettyTree = (tree) => { const [nodeType, subject, children] = [ tree.getNodeType(), - subjectString(tree.getSubject()), + subjectJSON(tree.getSubject()), tree.getChildrenList() ] switch (nodeType) { case expand.NodeType.NODE_TYPE_LEAF: - return { type: 'leaf', subject } + return { type: 'leaf', ...subject } case expand.NodeType.NODE_TYPE_UNION: - return { type: 'union', subject, children: children.map(prettyTree) } + return { type: 'union', children: children.map(prettyTree), ...subject } } } diff --git a/contrib/docs-code-samples/list-api-display-objects/01-list-PM/cli.sh b/contrib/docs-code-samples/list-api-display-objects/01-list-PM/cli.sh index a0763cc68..0d593e79a 100755 --- a/contrib/docs-code-samples/list-api-display-objects/01-list-PM/cli.sh +++ b/contrib/docs-code-samples/list-api-display-objects/01-list-PM/cli.sh @@ -3,5 +3,5 @@ set -euo pipefail export KETO_READ_REMOTE="127.0.0.1:4466" -keto relation-tuple get chats --relation member --subject PM --format json | \ +keto relation-tuple get chats --relation member --subject-id PM --format json | \ jq ".relation_tuples[] | .object" -r diff --git a/contrib/docs-code-samples/list-api-display-objects/01-list-PM/curl.sh b/contrib/docs-code-samples/list-api-display-objects/01-list-PM/curl.sh index 60b566cb0..d936d8e0a 100755 --- a/contrib/docs-code-samples/list-api-display-objects/01-list-PM/curl.sh +++ b/contrib/docs-code-samples/list-api-display-objects/01-list-PM/curl.sh @@ -4,6 +4,6 @@ set -euo pipefail curl -G --silent \ --data-urlencode "namespace=chats" \ --data-urlencode "relation=member" \ - --data-urlencode "subject=PM" \ + --data-urlencode "subject_id=PM" \ http://127.0.0.1:4466/relation-tuples | \ jq ".relation_tuples[] | .object" -r diff --git a/contrib/docs-code-samples/list-api-display-objects/02-list-coffee-break/cli.sh b/contrib/docs-code-samples/list-api-display-objects/02-list-coffee-break/cli.sh index d189f8414..7b73f81f8 100755 --- a/contrib/docs-code-samples/list-api-display-objects/02-list-coffee-break/cli.sh +++ b/contrib/docs-code-samples/list-api-display-objects/02-list-coffee-break/cli.sh @@ -4,4 +4,4 @@ set -euo pipefail export KETO_READ_REMOTE="127.0.0.1:4466" keto relation-tuple get chats --object coffee-break --relation member --format json | \ - jq ".relation_tuples[] | .subject" -r + jq ".relation_tuples[] | .subject_id" -r diff --git a/contrib/docs-code-samples/list-api-display-objects/02-list-coffee-break/curl.sh b/contrib/docs-code-samples/list-api-display-objects/02-list-coffee-break/curl.sh index aa716aa05..5439bab62 100755 --- a/contrib/docs-code-samples/list-api-display-objects/02-list-coffee-break/curl.sh +++ b/contrib/docs-code-samples/list-api-display-objects/02-list-coffee-break/curl.sh @@ -6,4 +6,4 @@ curl -G --silent \ --data-urlencode "object=coffee-break" \ --data-urlencode "relation=member" \ http://127.0.0.1:4466/relation-tuples | \ - jq ".relation_tuples[] | .subject" -r + jq ".relation_tuples[] | .subject_id" -r diff --git a/contrib/docs-code-samples/simple-access-check-guide/00-write-direct-access/curl.sh b/contrib/docs-code-samples/simple-access-check-guide/00-write-direct-access/curl.sh index c7cfffe2a..8e5f8af1f 100755 --- a/contrib/docs-code-samples/simple-access-check-guide/00-write-direct-access/curl.sh +++ b/contrib/docs-code-samples/simple-access-check-guide/00-write-direct-access/curl.sh @@ -6,7 +6,7 @@ relationtuple=' "namespace": "messages", "object": "02y_15_4w350m3", "relation": "decypher", - "subject": "john" + "subject_id": "john" }' curl --fail --silent -X PUT \ diff --git a/contrib/docs-code-samples/simple-access-check-guide/01-check-direct-access/curl.sh b/contrib/docs-code-samples/simple-access-check-guide/01-check-direct-access/curl.sh index 89af3d84c..45f283d9c 100755 --- a/contrib/docs-code-samples/simple-access-check-guide/01-check-direct-access/curl.sh +++ b/contrib/docs-code-samples/simple-access-check-guide/01-check-direct-access/curl.sh @@ -2,7 +2,7 @@ set -euo pipefail curl -G --silent \ - --data-urlencode "subject=john" \ + --data-urlencode "subject_id=john" \ --data-urlencode "relation=decypher" \ --data-urlencode "namespace=messages" \ --data-urlencode "object=02y_15_4w350m3" \ diff --git a/contrib/docs-code-samples/simple-access-check-guide/99-cleanup/cli.sh b/contrib/docs-code-samples/simple-access-check-guide/99-cleanup/cli.sh index ba2856af2..1b05f69d8 100755 --- a/contrib/docs-code-samples/simple-access-check-guide/99-cleanup/cli.sh +++ b/contrib/docs-code-samples/simple-access-check-guide/99-cleanup/cli.sh @@ -8,7 +8,7 @@ relationtuple=' "namespace": "messages", "object": "02y_15_4w350m3", "relation": "decypher", - "subject": "john" + "subject_id": "john" }' keto relation-tuple delete <(echo "$relationtuple") -q > /dev/null diff --git a/contrib/docs-code-samples/simple-access-check-guide/99-cleanup/curl.sh b/contrib/docs-code-samples/simple-access-check-guide/99-cleanup/curl.sh index f0e4099d0..719f5bee9 100755 --- a/contrib/docs-code-samples/simple-access-check-guide/99-cleanup/curl.sh +++ b/contrib/docs-code-samples/simple-access-check-guide/99-cleanup/curl.sh @@ -2,7 +2,7 @@ set -euo pipefail curl -X DELETE -G --silent \ - --data-urlencode "subject=john" \ + --data-urlencode "subject_id=john" \ --data-urlencode "relation=decypher" \ --data-urlencode "namespace=messages" \ --data-urlencode "object=02y_15_4w350m3" \ diff --git a/internal/expand/tree.go b/internal/expand/tree.go index deeec891e..2528e1467 100644 --- a/internal/expand/tree.go +++ b/internal/expand/tree.go @@ -85,7 +85,7 @@ func NodeTypeFromProto(t acl.NodeType) NodeType { type node struct { // required: true Type NodeType `json:"type"` - Children []*node `json:"children"` + Children []*node `json:"children,omitempty"` SubjectID *string `json:"subject_id,omitempty"` SubjectSet *relationtuple.SubjectSet `json:"subject_set,omitempty"` } From ac3ac94231664e2b73e722f1208618bdb64441ad Mon Sep 17 00:00:00 2001 From: zepatrik Date: Thu, 23 Sep 2021 12:56:15 +0200 Subject: [PATCH 7/9] feat: add unsupported error to CLI --- cmd/relationtuple/get.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmd/relationtuple/get.go b/cmd/relationtuple/get.go index 045e3df7b..0ac23b534 100644 --- a/cmd/relationtuple/get.go +++ b/cmd/relationtuple/get.go @@ -20,6 +20,7 @@ import ( ) const ( + FlagSubject = "subject" FlagSubjectID = "subject-id" FlagSubjectSet = "subject-set" FlagRelation = "relation" @@ -33,6 +34,11 @@ func registerRelationTupleFlags(flags *pflag.FlagSet) { flags.String(FlagSubjectSet, "", `Set the requested subject set; format: "namespace:object#relation"`) flags.String(FlagRelation, "", "Set the requested relation") flags.String(FlagObject, "", "Set the requested object") + + flags.String(FlagSubject, "", "") + if err := flags.MarkHidden(FlagSubject); err != nil { + panic(err.Error()) + } } func readQueryFromFlags(cmd *cobra.Command, namespace string) (*acl.ListRelationTuplesRequest_Query, error) { @@ -71,6 +77,10 @@ func newGetCmd() *cobra.Command { "Returns paginated results.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + if cmd.Flags().Changed(FlagSubject) { + return fmt.Errorf("usage of --%s is not supported anymore, use --%s or --%s respectively", FlagSubject, FlagSubjectID, FlagSubjectSet) + } + conn, err := client.GetReadConn(cmd) if err != nil { return err From c6337b68f1ed2ebc07e15fb351edcd32c1f05bcb Mon Sep 17 00:00:00 2001 From: zepatrik Date: Thu, 23 Sep 2021 13:04:52 +0200 Subject: [PATCH 8/9] chore: cleanup --- internal/check/handler.go | 1 - spec/patches/subjects.yml | 4 ---- 2 files changed, 5 deletions(-) delete mode 100644 spec/patches/subjects.yml diff --git a/internal/check/handler.go b/internal/check/handler.go index 52750b3d7..ece7eb1b4 100644 --- a/internal/check/handler.go +++ b/internal/check/handler.go @@ -92,7 +92,6 @@ func (h *Handler) getCheck(w http.ResponseWriter, r *http.Request, _ httprouter. return } - h.d.Logger().Warn("checking against engine\n\n\n") allowed, err := h.d.PermissionEngine().SubjectIsAllowed(r.Context(), tuple) if err != nil { h.d.Writer().WriteError(w, r, err) diff --git a/spec/patches/subjects.yml b/spec/patches/subjects.yml deleted file mode 100644 index 73947c52d..000000000 --- a/spec/patches/subjects.yml +++ /dev/null @@ -1,4 +0,0 @@ -- op: replace - path: /definitions/subject - value: - oneOf: From b1ed9df68e2cfd04e0d82ec6f2688a4aa64a6c77 Mon Sep 17 00:00:00 2001 From: zepatrik Date: Fri, 24 Sep 2021 12:26:13 +0200 Subject: [PATCH 9/9] chore: go mod tidy --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 2a337b8da..e562002c8 100644 --- a/go.mod +++ b/go.mod @@ -129,7 +129,7 @@ require ( github.com/joho/godotenv v1.3.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/knadh/koanf v1.0.0 // indirect + github.com/knadh/koanf v1.2.2 // indirect github.com/lib/pq v1.10.1 // indirect github.com/looplab/fsm v0.1.0 // indirect github.com/magiconair/properties v1.8.4 // indirect