From e0e9e008348c6315ed78b6f2fb2f836780e65e02 Mon Sep 17 00:00:00 2001 From: Mike Mason Date: Fri, 16 Jun 2023 07:58:26 -0500 Subject: [PATCH] Update urn gidx (#107) * convert to gidx PrefixID Signed-off-by: Mike Mason * update documentation to no longer reference urns Documentation has been updated to no longer reference urns except where dependent libraries use it. Signed-off-by: Mike Mason --------- Signed-off-by: Mike Mason --- README.md | 18 +++---- docs/resource_management.md | 16 +++--- go.mod | 21 ++++---- go.sum | 43 +++++++++------- internal/api/assignments.go | 20 +++---- internal/api/permissions.go | 20 +++---- internal/api/relationships.go | 35 ++++++------- internal/api/roles.go | 22 ++++---- internal/api/router.go | 14 ++--- internal/api/types.go | 20 +++---- internal/iapl/policy.go | 3 +- internal/pubsub/subscriber.go | 26 +++++----- internal/pubsub/subscriber_test.go | 8 +-- internal/query/mock/mock.go | 18 +++---- internal/query/roles.go | 11 +++- internal/query/service.go | 5 +- internal/query/tenants.go | 83 ++++++++++++++++-------------- internal/query/tenants_test.go | 41 +++++++-------- internal/types/types.go | 9 ++-- pkg/client/v1/auth.go | 6 +-- 20 files changed, 220 insertions(+), 219 deletions(-) diff --git a/README.md b/README.md index c2a346e6..a119f667 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ The concepts necessary to accomplish this are described in this section. ### Resource -A resource is any uniquely identifiable thing in the Infratographer ecosystem. Resources have types and are identified using URNs in permissions-api. For example, the URN `urn:infratographer:loadbalancer:a69e79ce-ac05-4a3e-b9b3-f371255e8e99` corresponds to a resource of type `loadbalancer` and ID `a69e79ce-ac05-4a3e-b9b3-f371255e8e99`. +A resource is any uniquely identifiable thing in the Infratographer ecosystem. Resources have types and are identified using Prefixed IDs in permissions-api. For example, the Prefixed ID `loadbal-hWV_xTSoYqIkXXWyK6eco` corresponds to a resource of type `loadbalancer`. ### Subject @@ -68,7 +68,7 @@ $ ./permissions-api server --config permissions-api.example.yaml ### Generating access tokens -permissions-api requests are authenticated using JWT access tokens. If you are using the provided [dev container](#development), permissions-api is already configured to accept JWTs from the included [mock-oauth2-server][mock-oauth2-server] service. A UI to manually create access tokens is available at http://localhost:8081/default/debugger. Tokens must be configured with a "scope" value in the UI set to `openid permissions-api` (which maps to an audience in the JWT of `permissions-api`) and a subject value of `urn:infratographer:subject:$SOME_UUID`. +permissions-api requests are authenticated using JWT access tokens. If you are using the provided [dev container](#development), permissions-api is already configured to accept JWTs from the included [mock-oauth2-server][mock-oauth2-server] service. A UI to manually create access tokens is available at http://localhost:8081/default/debugger. Tokens must be configured with a "scope" value in the UI set to `openid permissions-api` (which maps to an audience in the JWT of `permissions-api`) and a Prefixed ID (ex: `idntusr-0xqwVtYKHjjuLfjSItHLU`). [mock-oauth2-server]: https://github.com/navikt/mock-oauth2-server @@ -78,8 +78,8 @@ Resources are defined in terms of their relationships to other resources using t ``` $ curl --oauth2-bearer "$AUTH_TOKEN" \ - -d '{"relationships": [{"relation": "tenant", "subject_urn": "urn:infratographer:tenant:075b8c8c-1214-49ac-a7ed-ec102f165568"}]}' \ - http://localhost:7602/api/v1/resources/urn:infratographer:tenant:3fc4e4e0-6030-4e36-83d6-09ae2d58fee8/relationships + -d '{"relationships": [{"relation": "tenant", "subject_id": "tnntten-OJrD-JdCFThZiRgqk6vs6"}]}' \ + http://localhost:7602/api/v1/resources/tnntten-MCR3xIIMWfVpVM22w82NZ/relationships ``` ### Creating roles @@ -89,17 +89,17 @@ Roles are created using the `/roles` API endpoint. For example, the following cu ``` $ curl --oauth2-bearer "$AUTH_TOKEN" \ -d '{"actions": ["loadbalancer_create"]}' \ - http://localhost:7602/api/v1/resources/urn:infratographer:tenant:3fc4e4e0-6030-4e36-83d6-09ae2d58fee8/roles + http://localhost:7602/api/v1/resources/tnntten-MCR3xIIMWfVpVM22w82NZ/roles ``` ### Assigning roles to subjects -Roles are assigned to subjects using the `/assignments` API endpoint. The curl command below will assign the subject with the given URN to the given role: +Roles are assigned to subjects using the `/assignments` API endpoint. The curl command below will assign the subject with the given ID to the given role: ``` $ curl --oauth2-bearer "$AUTH_TOKEN" \ - -d '{"subject_urn": "urn:infratographer:user:e0a97b60-af68-4376-828c-78c6c2ab04a9"}' \ - http://localhost:7602/api/v1/roles/7a5ccbfb-6838-478e-9d61-cffd38ceb5a3/assignments + -d '{"subject_id": "idntusr-0xqwVtYKHjjuLfjSItHLU"}' \ + http://localhost:7602/api/v1/roles/permrol-XqGKCT8L5CikBuIpbFQEt/assignments ``` ### Checking permissions @@ -108,7 +108,7 @@ The `/has` API endpoint is used to check whether the authenticated subject in th ``` $ curl --oauth2-bearer "$AUTH_TOKEN" \ - http://localhost:7602/api/v1/has/loadbalancer_create/on/urn:infratographer:tenant:3fc4e4e0-6030-4e36-83d6-09ae2d58fee8 + http://localhost:7602/api/v1/has/loadbalancer_create/on/tnntten-MCR3xIIMWfVpVM22w82NZ ``` ## Development diff --git a/docs/resource_management.md b/docs/resource_management.md index 55f6a94f..ac4d6191 100644 --- a/docs/resource_management.md +++ b/docs/resource_management.md @@ -16,9 +16,9 @@ permissions-api consumes resource lifecycle events over [NATS][nats]. This secti permission-api expects lifecycle event messages in the format described in [`go.infratographer.com/x/pubsubx`][pubsubx], and interprets the message fields as follows: -* `subject_urn`: The URN of the resource the event is about +* `subject_id`: The Prefixed ID of the resource the event is about * `event_type`: The type of lifecyle event for the resouce. Must match one of the defined lifecycle events -* `fields`: Information related to the resource. A field's value will be persisted in permissions-api if the field is of the form `{foo}_urn` and a defined relationship exists on the resource with relation `{foo}` +* `fields`: Information related to the resource. A field's value will be persisted in permissions-api if the field is of the form `{foo}_id` and a defined relationship exists on the resource with relation `{foo}` [pubsubx]: https://github.com/infratographer/x/blob/v0.0.7/pubsubx/message.go @@ -48,18 +48,18 @@ As an example, consider the following lifecycle event for a resource of type `lo ```json { - "subject_urn": "urn:infratographer:loadbalancer:0e919c70-6d04-4050-a474-073ab8b58ffe", + "subject_urn": "loadbal-1vzGV0jqpeKlbMBZzq3uf", "event_type": "create", "additional_subjects": [ - "urn:infratographer:tenant:42f0e8f2-4b81-4e5a-86f2-62d78ed35dca", - "urn:infratographer:loadbalancerport:db25eabd-30eb-4654-9bb6-a22c140eac97", - "urn:infratographer:loadbalancerassignment:44cadf84-c626-4428-8910-3a699a78b898" + "tnntten-RNsrsfboJb_r6OyXHxBzN", + "loadprt-j3UjvKqoRyMUicC7pWatJ", + "loadpvd-tvedEoZ2d_vkoTjjdLK76" ], - "actor_urn": "urn:infratographer:user:35464f0b-a7b4-47db-b446-01e61987db6c", + "actor_urn": "idntusr-xbNJrq0updKVkgiaDOxtY", "source": "loadbalancer-api", "timestamp": "2023-05-06T17:30:00Z", "fields": { - "tenant_urn": "urn:infratographer:tenant:42f0e8f2-4b81-4e5a-86f2-62d78ed35dca" + "tenant_id": "tnntten-RNsrsfboJb_r6OyXHxBzN" }, "additional_data": {} } diff --git a/go.mod b/go.mod index b635de56..86a6c27d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.20 require ( github.com/authzed/authzed-go v0.8.0 github.com/authzed/grpcutil v0.0.0-20230524151342-4caf7fd1108a - github.com/google/uuid v1.3.0 github.com/labstack/echo/v4 v4.10.2 github.com/nats-io/nats-server/v2 v2.9.17 github.com/nats-io/nats.go v1.24.0 @@ -13,7 +12,7 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.3 - go.infratographer.com/x v0.0.8 + go.infratographer.com/x v0.0.15 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 go.opentelemetry.io/otel v1.14.0 @@ -23,7 +22,7 @@ require ( ) require ( - github.com/MicahParks/keyfunc v1.9.0 // indirect + github.com/MicahParks/keyfunc/v2 v2.0.3 // indirect github.com/benbjohnson/clock v1.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect @@ -36,21 +35,22 @@ require ( github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.14.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jaevor/go-nanoid v1.3.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/jzelinskie/stringz v0.0.0-20210414224931-d6a8ce844a70 // indirect github.com/klauspost/compress v1.16.5 // indirect github.com/labstack/echo-contrib v0.14.1 // indirect - github.com/labstack/echo-jwt/v4 v4.1.0 // indirect + github.com/labstack/echo-jwt/v4 v4.2.0 // indirect github.com/labstack/gommon v0.4.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -85,10 +85,11 @@ require ( go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.8.0 // indirect - golang.org/x/net v0.9.0 // indirect - golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sys v0.7.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index 976fe562..b27b2728 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID3+o= -github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw= +github.com/MicahParks/keyfunc/v2 v2.0.3 h1:uKUEOc+knRO0UoucONisgNPiT85V2s/W5c0FQYsd9kc= +github.com/MicahParks/keyfunc/v2 v2.0.3/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/authzed/authzed-go v0.8.0 h1:gb4X+7RxVqXSCFReAnKmSda68TBIqRdc47W2spLqoEc= @@ -109,9 +109,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= @@ -174,7 +173,6 @@ github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -192,6 +190,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jaevor/go-nanoid v1.3.0 h1:nD+iepesZS6pr3uOVf20vR9GdGgJW1HPaR46gtrxzkg= +github.com/jaevor/go-nanoid v1.3.0/go.mod h1:SI+jFaPuddYkqkVQoNGHs81navCtH388TcrH0RqFKgY= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -211,8 +211,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/labstack/echo-contrib v0.14.1 h1:oNUSCeXQOlCGt3eWafzu0mkXjIh3SINnYgE/UR2kYXQ= github.com/labstack/echo-contrib v0.14.1/go.mod h1:6jgpHPjGRk0qrysPCfv3SCau6kewjQtYzOk1fLZGMeQ= -github.com/labstack/echo-jwt/v4 v4.1.0 h1:eYGBxauPkyzBM78KJbR5OSz5uhKMDkhJZhTTIuoH6Pg= -github.com/labstack/echo-jwt/v4 v4.1.0/go.mod h1:DHSSaL6cTgczdPXjf8qrTHRbrau2flcddV7CPMs2U/Y= +github.com/labstack/echo-jwt/v4 v4.2.0 h1:odSISV9JgcSCuhgQSV/6Io3i7nUmfM/QkBeR5GVJj5c= +github.com/labstack/echo-jwt/v4 v4.2.0/go.mod h1:MA2RqdXdEn4/uEglx0HcUOgQSyBaTh5JcaHIan3biwU= github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M= github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= @@ -224,8 +224,8 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= @@ -310,8 +310,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.infratographer.com/x v0.0.8 h1:DyoWXk/EjIV+STlBHKd5pBMmxN2g6eitbFV9Fz9JJ8Y= -go.infratographer.com/x v0.0.8/go.mod h1:MQYfeEyp7CAFZI6PJRWfvNvts74yQ+AGUioSNOkIowg= +go.infratographer.com/x v0.0.15 h1:swN19UZt7OR192ZKeNIyPUGoC9DWbwluT5kvZ9/Yqek= +go.infratographer.com/x v0.0.15/go.mod h1:nsF/EbfcFnKPbTrkARjRrvFKy1w4Vr9cd6jAMDyUaXI= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -366,8 +366,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -378,6 +378,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -433,8 +435,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -445,8 +447,8 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -501,8 +503,9 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/internal/api/assignments.go b/internal/api/assignments.go index e7638ea1..ac2f7c1e 100644 --- a/internal/api/assignments.go +++ b/internal/api/assignments.go @@ -4,10 +4,9 @@ import ( "net/http" "go.infratographer.com/permissions-api/internal/types" + "go.infratographer.com/x/gidx" - "github.com/google/uuid" "github.com/labstack/echo/v4" - "go.infratographer.com/x/urnx" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) @@ -15,7 +14,7 @@ import ( func (r *Router) assignmentCreate(c echo.Context) error { roleIDStr := c.Param("role_id") - roleID, err := uuid.Parse(roleIDStr) + roleID, err := gidx.Parse(roleIDStr) if err != nil { return echo.ErrNotFound } @@ -30,12 +29,12 @@ func (r *Router) assignmentCreate(c echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, "error parsing request body").SetInternal(err) } - subjURN, err := urnx.Parse(reqBody.SubjectURN) + subjID, err := gidx.Parse(reqBody.SubjectID) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "error parsing subject URN").SetInternal(err) + return echo.NewHTTPError(http.StatusBadRequest, "error parsing subject ID").SetInternal(err) } - subjResource, err := r.engine.NewResourceFromURN(subjURN) + subjResource, err := r.engine.NewResourceFromID(subjID) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, "error creating resource").SetInternal(err) } @@ -59,7 +58,7 @@ func (r *Router) assignmentCreate(c echo.Context) error { func (r *Router) assignmentsList(c echo.Context) error { roleIDStr := c.Param("role_id") - roleID, err := uuid.Parse(roleIDStr) + roleID, err := gidx.Parse(roleIDStr) if err != nil { return echo.ErrNotFound } @@ -79,13 +78,8 @@ func (r *Router) assignmentsList(c echo.Context) error { items := make([]assignmentItem, len(assignments)) for i, res := range assignments { - subjURN, err := r.engine.NewURNFromResource(res) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "error listing assignments").SetInternal(err) - } - item := assignmentItem{ - SubjectURN: subjURN.String(), + SubjectID: res.ID.String(), } items[i] = item diff --git a/internal/api/permissions.go b/internal/api/permissions.go index ccf73cb7..b0811baa 100644 --- a/internal/api/permissions.go +++ b/internal/api/permissions.go @@ -7,7 +7,7 @@ import ( "github.com/labstack/echo/v4" "go.infratographer.com/permissions-api/internal/query" - "go.infratographer.com/x/urnx" + "go.infratographer.com/x/gidx" ) // checkAction will check if a subject is allowed to perform an action on a resource. @@ -19,7 +19,7 @@ import ( // contain the subject of the request in the "sub" claim. // // The following query parameters are required: -// - resource: the resource URN to check +// - resource: the resource ID to check // - action: the action to check func (r *Router) checkAction(c echo.Context) error { ctx, span := tracer.Start(c.Request().Context(), "api.checkAction") @@ -31,20 +31,20 @@ func (r *Router) checkAction(c echo.Context) error { } // Optional query parameters - resourceURNStr, hasResourceParam := getParam(c, "resource") + resourceIDStr, hasResourceParam := getParam(c, "resource") if !hasResourceParam { return echo.NewHTTPError(http.StatusBadRequest, "missing resource query parameter") } // Query parameter validation - resourceURN, err := urnx.Parse(resourceURNStr) + resourceID, err := gidx.Parse(resourceIDStr) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "error processing resource URN").SetInternal(err) + return echo.NewHTTPError(http.StatusBadRequest, "error processing resource ID").SetInternal(err) } - resource, err := r.engine.NewResourceFromURN(resourceURN) + resource, err := r.engine.NewResourceFromID(resourceID) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "error processing tenant resource URN").SetInternal(err) + return echo.NewHTTPError(http.StatusBadRequest, "error processing tenant resource ID").SetInternal(err) } // Subject validation @@ -53,16 +53,16 @@ func (r *Router) checkAction(c echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, "failed to get the subject").SetInternal(err) } - subjectResource, err := r.engine.NewResourceFromURN(subject) + subjectResource, err := r.engine.NewResourceFromID(subject) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "error processing subject URN").SetInternal(err) + return echo.NewHTTPError(http.StatusBadRequest, "error processing subject ID").SetInternal(err) } // Check the permissions err = r.engine.SubjectHasPermission(ctx, subjectResource, action, resource, "") if err != nil && errors.Is(err, query.ErrActionNotAssigned) { msg := fmt.Sprintf("subject '%s' does not have permission to perform action '%s' on resource '%s'", - subject, action, resourceURNStr) + subject, action, resourceIDStr) return echo.NewHTTPError(http.StatusForbidden, msg).SetInternal(err) } else if err != nil { diff --git a/internal/api/relationships.go b/internal/api/relationships.go index c342cff2..175be23c 100644 --- a/internal/api/relationships.go +++ b/internal/api/relationships.go @@ -6,18 +6,18 @@ import ( "go.infratographer.com/permissions-api/internal/types" "github.com/labstack/echo/v4" - "go.infratographer.com/x/urnx" + "go.infratographer.com/x/gidx" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) func (r *Router) buildRelationship(resource types.Resource, item createRelationshipItem) (types.Relationship, error) { - itemURN, err := urnx.Parse(item.SubjectURN) + itemID, err := gidx.Parse(item.SubjectID) if err != nil { return types.Relationship{}, err } - itemResource, err := r.engine.NewResourceFromURN(itemURN) + itemResource, err := r.engine.NewResourceFromID(itemID) if err != nil { return types.Relationship{}, err } @@ -47,14 +47,14 @@ func (r *Router) buildRelationships(subjResource types.Resource, items []createR } func (r *Router) relationshipsCreate(c echo.Context) error { - resourceURNStr := c.Param("urn") + resourceIDStr := c.Param("id") - ctx, span := tracer.Start(c.Request().Context(), "api.relationshipsCreate", trace.WithAttributes(attribute.String("urn", resourceURNStr))) + ctx, span := tracer.Start(c.Request().Context(), "api.relationshipsCreate", trace.WithAttributes(attribute.String("id", resourceIDStr))) defer span.End() - resourceURN, err := urnx.Parse(resourceURNStr) + resourceID, err := gidx.Parse(resourceIDStr) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "error parsing resource URN").SetInternal(err) + return echo.NewHTTPError(http.StatusBadRequest, "error parsing resource ID").SetInternal(err) } var reqBody createRelationshipsRequest @@ -64,7 +64,7 @@ func (r *Router) relationshipsCreate(c echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, "error parsing request body").SetInternal(err) } - resource, err := r.engine.NewResourceFromURN(resourceURN) + resource, err := r.engine.NewResourceFromID(resourceID) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, "error creating relationships").SetInternal(err) } @@ -87,17 +87,17 @@ func (r *Router) relationshipsCreate(c echo.Context) error { } func (r *Router) relationshipsList(c echo.Context) error { - resourceURNStr := c.Param("urn") + resourceIDStr := c.Param("id") - ctx, span := tracer.Start(c.Request().Context(), "api.relationshipsList", trace.WithAttributes(attribute.String("urn", resourceURNStr))) + ctx, span := tracer.Start(c.Request().Context(), "api.relationshipsList", trace.WithAttributes(attribute.String("id", resourceIDStr))) defer span.End() - resourceURN, err := urnx.Parse(resourceURNStr) + resourceID, err := gidx.Parse(resourceIDStr) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "error parsing resource URN").SetInternal(err) + return echo.NewHTTPError(http.StatusBadRequest, "error parsing resource ID").SetInternal(err) } - resource, err := r.engine.NewResourceFromURN(resourceURN) + resource, err := r.engine.NewResourceFromID(resourceID) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, "error listing relationships").SetInternal(err) } @@ -110,14 +110,9 @@ func (r *Router) relationshipsList(c echo.Context) error { items := make([]relationshipItem, len(rels)) for i, rel := range rels { - subjURN, err := r.engine.NewURNFromResource(rel.Subject) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "error listing relationships").SetInternal(err) - } - item := relationshipItem{ - Relation: rel.Relation, - SubjectURN: subjURN.String(), + Relation: rel.Relation, + SubjectID: rel.Subject.ID.String(), } items[i] = item diff --git a/internal/api/roles.go b/internal/api/roles.go index 1169703b..92f5f559 100644 --- a/internal/api/roles.go +++ b/internal/api/roles.go @@ -4,20 +4,20 @@ import ( "net/http" "github.com/labstack/echo/v4" - "go.infratographer.com/x/urnx" + "go.infratographer.com/x/gidx" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) func (r *Router) roleCreate(c echo.Context) error { - resourceURNStr := c.Param("urn") + resourceIDStr := c.Param("id") - ctx, span := tracer.Start(c.Request().Context(), "api.roleCreate", trace.WithAttributes(attribute.String("urn", resourceURNStr))) + ctx, span := tracer.Start(c.Request().Context(), "api.roleCreate", trace.WithAttributes(attribute.String("id", resourceIDStr))) defer span.End() - resourceURN, err := urnx.Parse(resourceURNStr) + resourceID, err := gidx.Parse(resourceIDStr) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "error parsing resource URN").SetInternal(err) + return echo.NewHTTPError(http.StatusBadRequest, "error parsing resource ID").SetInternal(err) } var reqBody createRoleRequest @@ -27,7 +27,7 @@ func (r *Router) roleCreate(c echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, "error parsing request body").SetInternal(err) } - resource, err := r.engine.NewResourceFromURN(resourceURN) + resource, err := r.engine.NewResourceFromID(resourceID) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, "error creating resource").SetInternal(err) } @@ -46,17 +46,17 @@ func (r *Router) roleCreate(c echo.Context) error { } func (r *Router) rolesList(c echo.Context) error { - resourceURNStr := c.Param("urn") + resourceIDStr := c.Param("id") - ctx, span := tracer.Start(c.Request().Context(), "api.roleGet", trace.WithAttributes(attribute.String("urn", resourceURNStr))) + ctx, span := tracer.Start(c.Request().Context(), "api.roleGet", trace.WithAttributes(attribute.String("id", resourceIDStr))) defer span.End() - resourceURN, err := urnx.Parse(resourceURNStr) + resourceID, err := gidx.Parse(resourceIDStr) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "error parsing resource URN").SetInternal(err) + return echo.NewHTTPError(http.StatusBadRequest, "error parsing resource ID").SetInternal(err) } - resource, err := r.engine.NewResourceFromURN(resourceURN) + resource, err := r.engine.NewResourceFromID(resourceID) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, "error creating resource").SetInternal(err) } diff --git a/internal/api/router.go b/internal/api/router.go index 6155c102..34e536ff 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -7,7 +7,7 @@ import ( "go.infratographer.com/permissions-api/internal/query" "go.infratographer.com/x/echojwtx" "go.infratographer.com/x/echox" - "go.infratographer.com/x/urnx" + "go.infratographer.com/x/gidx" "go.opentelemetry.io/otel" "go.uber.org/zap" ) @@ -46,10 +46,10 @@ func (r *Router) Routes(rg *echo.Group) { { v1.Use(r.authMW) - v1.POST("/resources/:urn/roles", r.roleCreate) - v1.GET("/resources/:urn/roles", r.rolesList) - v1.POST("/resources/:urn/relationships", r.relationshipsCreate) - v1.GET("/resources/:urn/relationships", r.relationshipsList) + v1.POST("/resources/:id/roles", r.roleCreate) + v1.GET("/resources/:id/roles", r.rolesList) + v1.POST("/resources/:id/relationships", r.relationshipsCreate) + v1.GET("/resources/:id/relationships", r.relationshipsList) v1.POST("/roles/:role_id/assignments", r.assignmentCreate) v1.GET("/roles/:role_id/assignments", r.assignmentsList) @@ -58,8 +58,8 @@ func (r *Router) Routes(rg *echo.Group) { } } -func currentSubject(c echo.Context) (*urnx.URN, error) { +func currentSubject(c echo.Context) (gidx.PrefixedID, error) { subject := echojwtx.Actor(c) - return urnx.Parse(subject) + return gidx.Parse(subject) } diff --git a/internal/api/types.go b/internal/api/types.go index d1fe7fd2..75714921 100644 --- a/internal/api/types.go +++ b/internal/api/types.go @@ -1,14 +1,16 @@ package api -import "github.com/google/uuid" +import ( + "go.infratographer.com/x/gidx" +) type createRoleRequest struct { Actions []string `json:"actions" binding:"required"` } type roleResponse struct { - ID uuid.UUID `json:"id"` - Actions []string `json:"actions"` + ID gidx.PrefixedID `json:"id"` + Actions []string `json:"actions"` } type listRolesResponse struct { @@ -16,8 +18,8 @@ type listRolesResponse struct { } type createRelationshipItem struct { - Relation string `json:"relation" binding:"required"` - SubjectURN string `json:"subject_urn" binding:"required"` + Relation string `json:"relation" binding:"required"` + SubjectID string `json:"subject_id" binding:"required"` } type createRelationshipsRequest struct { @@ -29,8 +31,8 @@ type createRelationshipsResponse struct { } type relationshipItem struct { - Relation string `json:"relation"` - SubjectURN string `json:"subject_urn"` + Relation string `json:"relation"` + SubjectID string `json:"subject_id"` } type listRelationshipsResponse struct { @@ -38,7 +40,7 @@ type listRelationshipsResponse struct { } type createAssignmentRequest struct { - SubjectURN string `json:"subject_urn" binding:"required"` + SubjectID string `json:"subject_id" binding:"required"` } type createAssignmentResponse struct { @@ -46,7 +48,7 @@ type createAssignmentResponse struct { } type assignmentItem struct { - SubjectURN string `json:"subject_urn"` + SubjectID string `json:"subject_id"` } type listAssignmentsResponse struct { diff --git a/internal/iapl/policy.go b/internal/iapl/policy.go index 7e895e14..35cbeaf3 100644 --- a/internal/iapl/policy.go +++ b/internal/iapl/policy.go @@ -279,7 +279,8 @@ func (v *policy) Schema() []types.ResourceType { for n, rt := range v.rt { out := types.ResourceType{ - Name: rt.Name, + Name: rt.Name, + IDPrefix: rt.IDPrefix, } for _, rel := range rt.Relationships { diff --git a/internal/pubsub/subscriber.go b/internal/pubsub/subscriber.go index b7f9c4e8..57f109a5 100644 --- a/internal/pubsub/subscriber.go +++ b/internal/pubsub/subscriber.go @@ -10,8 +10,8 @@ import ( "go.infratographer.com/permissions-api/internal/query" "go.infratographer.com/permissions-api/internal/types" + "go.infratographer.com/x/gidx" "go.infratographer.com/x/pubsubx" - "go.infratographer.com/x/urnx" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" @@ -31,7 +31,7 @@ const ( eventTypeUpdate = "update" eventTypeDelete = "delete" - fieldRelationshipSuffix = "_urn" + fieldRelationshipSuffix = "_id" ) // Client represents a NATS JetStream client listening for resource lifecycle events. @@ -214,13 +214,13 @@ func (c *Client) ackWithError(msg *nats.Msg, err error) { } } -func (c *Client) newResourceFromString(urnStr string) (types.Resource, error) { - urn, err := urnx.Parse(urnStr) +func (c *Client) newResourceFromString(idStr string) (types.Resource, error) { + id, err := gidx.Parse(idStr) if err != nil { return types.Resource{}, err } - return c.qe.NewResourceFromURN(urn) + return c.qe.NewResourceFromID(id) } func (c *Client) createRelationships(ctx context.Context, msg *nats.Msg, resource types.Resource, fields map[string]string) error { @@ -269,10 +269,10 @@ func (c *Client) deleteRelationships(ctx context.Context, msg *nats.Msg, resourc } func (c *Client) handleCreateEvent(ctx context.Context, msg *nats.Msg, payload pubsubx.Message) error { - // Attempt to create a valid resource from the URN string. If this fails, reject the message + // Attempt to create a valid resource from the ID string. If this fails, reject the message resource, err := c.newResourceFromString(payload.SubjectURN) if err != nil { - c.logger.Warnw("error parsing subject URN - will not reprocess", "event_type", eventTypeCreate, "error", err.Error()) + c.logger.Warnw("error parsing subject ID - will not reprocess", "event_type", eventTypeCreate, "error", err.Error()) return nil } @@ -280,10 +280,10 @@ func (c *Client) handleCreateEvent(ctx context.Context, msg *nats.Msg, payload p } func (c *Client) handleDeleteEvent(ctx context.Context, msg *nats.Msg, payload pubsubx.Message) error { - // Attempt to create a valid resource from the URN string. If this fails, reject the message + // Attempt to create a valid resource from the ID string. If this fails, reject the message resource, err := c.newResourceFromString(payload.SubjectURN) if err != nil { - c.logger.Warnw("error parsing subject URN - will not reprocess", "event_type", eventTypeDelete, "error", err.Error()) + c.logger.Warnw("error parsing subject ID - will not reprocess", "event_type", eventTypeDelete, "error", err.Error()) return nil } @@ -291,10 +291,10 @@ func (c *Client) handleDeleteEvent(ctx context.Context, msg *nats.Msg, payload p } func (c *Client) handleUpdateEvent(ctx context.Context, msg *nats.Msg, payload pubsubx.Message) error { - // Attempt to create a valid resource from the URN string. If this fails, reject the message + // Attempt to create a valid resource from the ID string. If this fails, reject the message resource, err := c.newResourceFromString(payload.SubjectURN) if err != nil { - c.logger.Warnw("error parsing subject URN - will not reprocess", "event_type", eventTypeUpdate, "error", err.Error()) + c.logger.Warnw("error parsing subject ID - will not reprocess", "event_type", eventTypeUpdate, "error", err.Error()) return nil } @@ -324,13 +324,13 @@ func (c *Client) receiveMsg(msg *nats.Msg) { return } - resourceURN, err := urnx.Parse(payload.SubjectURN) + resourceID, err := gidx.Parse(payload.SubjectURN) if err != nil { c.ackWithError(msg, err) return } - resource, err := c.qe.NewResourceFromURN(resourceURN) + resource, err := c.qe.NewResourceFromID(resourceID) if err != nil { c.ackWithError(msg, err) return diff --git a/internal/pubsub/subscriber_test.go b/internal/pubsub/subscriber_test.go index 8900fa26..63237453 100644 --- a/internal/pubsub/subscriber_test.go +++ b/internal/pubsub/subscriber_test.go @@ -198,10 +198,10 @@ func TestNATS(t *testing.T) { msgBytes []byte } - createBytes := []byte(`{"subject_urn": "urn:infratographer:loadbalancer:fc065394-5486-4731-93b4-2726ca7e669f", "event_type": "create", "fields": {"tenant_urn": "urn:infratographer:tenant:75c8ec25-86e8-4fa7-93e2-6167684c3fb6"}}`) - updateBytes := []byte(`{"subject_urn": "urn:infratographer:loadbalancer:fc065394-5486-4731-93b4-2726ca7e669f", "event_type": "update", "fields": {"tenant_urn": "urn:infratographer:tenant:75c8ec25-86e8-4fa7-93e2-6167684c3fb6"}}`) - deleteBytes := []byte(`{"subject_urn": "urn:infratographer:loadbalancer:fc065394-5486-4731-93b4-2726ca7e669f", "event_type": "delete"}`) - unknownResourceBytes := []byte(`{"subject_urn": "urn:infratographer:badresource:fc065394-5486-4731-93b4-2726ca7e669f", "event_type": "create"}`) + createBytes := []byte(`{"subject_id": "loadbal-UCN7pxJO57BV_5pNiV95B", "event_type": "create", "fields": {"tenant_id": "tnntten-gd6RExwAz353UqHLzjC1n"}}`) + updateBytes := []byte(`{"subject_id": "loadbal-UCN7pxJO57BV_5pNiV95B", "event_type": "update", "fields": {"tenant_id": "tnntten-gd6RExwAz353UqHLzjC1n"}}`) + deleteBytes := []byte(`{"subject_id": "loadbal-UCN7pxJO57BV_5pNiV95B", "event_type": "delete"}`) + unknownResourceBytes := []byte(`{"subject_id": "baddres-BfqAzfYxtFNlpKPGYLmra", "event_type": "create"}`) // Each of these tests works as follows: // - A publisher NATS connection is created diff --git a/internal/query/mock/mock.go b/internal/query/mock/mock.go index 66a07bd0..2a8d5007 100644 --- a/internal/query/mock/mock.go +++ b/internal/query/mock/mock.go @@ -6,9 +6,8 @@ import ( "go.infratographer.com/permissions-api/internal/query" "go.infratographer.com/permissions-api/internal/types" - "github.com/google/uuid" "github.com/stretchr/testify/mock" - "go.infratographer.com/x/urnx" + "go.infratographer.com/x/gidx" ) var ( @@ -41,7 +40,7 @@ func (e *Engine) CreateRole(ctx context.Context, res types.Resource, actions []s copy(outActions, actions) role := types.Role{ - ID: uuid.New(), + ID: gidx.MustNewID(query.ApplicationPrefix), Actions: outActions, } @@ -70,21 +69,16 @@ func (e *Engine) DeleteRelationships(ctx context.Context, resource types.Resourc return args.String(0), args.Error(1) } -// NewResourceFromURN creates a new resource object based on the given URN. -func (e *Engine) NewResourceFromURN(urn *urnx.URN) (types.Resource, error) { +// NewResourceFromID creates a new resource object based on the given ID. +func (e *Engine) NewResourceFromID(id gidx.PrefixedID) (types.Resource, error) { out := types.Resource{ - Type: urn.ResourceType, - ID: urn.ResourceID, + Type: id.Prefix(), + ID: id, } return out, nil } -// NewURNFromResource creates a new URN from the given resource. -func (e *Engine) NewURNFromResource(res types.Resource) (*urnx.URN, error) { - return urnx.Build(e.Namespace, res.Type, res.ID) -} - // SubjectHasPermission returns nil to satisfy the Engine interface. func (e *Engine) SubjectHasPermission(ctx context.Context, subject types.Resource, action string, resource types.Resource, queryToken string) error { e.Called() diff --git a/internal/query/roles.go b/internal/query/roles.go index c8ed5663..13ae969d 100644 --- a/internal/query/roles.go +++ b/internal/query/roles.go @@ -1,13 +1,20 @@ package query import ( - "github.com/google/uuid" "go.infratographer.com/permissions-api/internal/types" + "go.infratographer.com/x/gidx" +) + +const ( + // ApplicationPrefix is the prefix for all application IDs owned by permissions-api + ApplicationPrefix string = "perm" + // RolePrefix is the prefix for roles + RolePrefix string = ApplicationPrefix + "rol" ) func newRole(actions []string) types.Role { return types.Role{ - ID: uuid.New(), + ID: gidx.MustNewID(RolePrefix), Actions: actions, } } diff --git a/internal/query/service.go b/internal/query/service.go index 0d1a57f8..df656439 100644 --- a/internal/query/service.go +++ b/internal/query/service.go @@ -4,7 +4,7 @@ import ( "context" "github.com/authzed/authzed-go/v1" - "go.infratographer.com/x/urnx" + "go.infratographer.com/x/gidx" "go.infratographer.com/permissions-api/internal/iapl" "go.infratographer.com/permissions-api/internal/types" @@ -19,8 +19,7 @@ type Engine interface { ListRelationships(ctx context.Context, resource types.Resource, queryToken string) ([]types.Relationship, error) ListRoles(ctx context.Context, resource types.Resource, queryToken string) ([]types.Role, error) DeleteRelationships(ctx context.Context, resource types.Resource) (string, error) - NewResourceFromURN(urn *urnx.URN) (types.Resource, error) - NewURNFromResource(res types.Resource) (*urnx.URN, error) + NewResourceFromID(id gidx.PrefixedID) (types.Resource, error) SubjectHasPermission(ctx context.Context, subject types.Resource, action string, resource types.Resource, queryToken string) error } diff --git a/internal/query/tenants.go b/internal/query/tenants.go index 066c36f8..f72714d2 100644 --- a/internal/query/tenants.go +++ b/internal/query/tenants.go @@ -7,9 +7,8 @@ import ( "strings" pb "github.com/authzed/authzed-go/proto/authzed/api/v1" - "github.com/google/uuid" "go.infratographer.com/permissions-api/internal/types" - "go.infratographer.com/x/urnx" + "go.infratographer.com/x/gidx" ) var roleSubjectRelation = "subject" @@ -64,24 +63,6 @@ func resourceToSpiceDBRef(namespace string, r types.Resource) *pb.ObjectReferenc } } -func spiceDBRefToResource(namespace string, ref *pb.ObjectReference) (types.Resource, error) { - sep := namespace + "/" - before, typeName, found := strings.Cut(ref.ObjectType, sep) - - if !found || before != "" { - return types.Resource{}, ErrInvalidReference - } - - resUUID := uuid.MustParse(ref.ObjectId) - - out := types.Resource{ - Type: typeName, - ID: resUUID, - } - - return out, nil -} - // SubjectHasPermission checks if the given subject can do the given action on the given resource func (e *engine) SubjectHasPermission(ctx context.Context, subject types.Resource, action string, resource types.Resource, queryToken string) error { req := &pb.CheckPermissionRequest{ @@ -128,12 +109,17 @@ func (e *engine) ListAssignments(ctx context.Context, role types.Role, queryToke out := make([]types.Resource, len(relationships)) for i, rel := range relationships { - subjRes, err := spiceDBRefToResource(e.namespace, rel.Subject.Object) + id, err := gidx.Parse(rel.Subject.Object.ObjectId) + if err != nil { + return nil, err + } + + res, err := e.NewResourceFromID(id) if err != nil { return nil, err } - out[i] = subjRes + out[i] = res } return out, nil @@ -229,9 +215,9 @@ func relationToAction(relation string) string { func (e *engine) roleRelationships(role types.Role, resource types.Resource) []*pb.RelationshipUpdate { var rels []*pb.RelationshipUpdate - roleResource := types.Resource{ - Type: "role", - ID: role.ID, + roleResource, err := e.NewResourceFromID(role.ID) + if err != nil { + panic(err) } resourceRef := resourceToSpiceDBRef(e.namespace, resource) @@ -342,13 +328,18 @@ func (e *engine) deleteRelationships(ctx context.Context, filter *pb.Relationshi } func relationshipsToRoles(rels []*pb.Relationship) []types.Role { - var roleIDs []uuid.UUID + var roleIDs []gidx.PrefixedID - roleMap := make(map[uuid.UUID]*types.Role) + roleMap := make(map[gidx.PrefixedID]*types.Role) for _, rel := range rels { roleIDStr := rel.Subject.Object.ObjectId - roleID := uuid.MustParse(roleIDStr) + + roleID, err := gidx.Parse(roleIDStr) + if err != nil { + panic(err) + } + action := relationToAction(rel.Relation) _, ok := roleMap[roleID] @@ -379,7 +370,12 @@ func (e *engine) relationshipsToNonRoles(rels []*pb.Relationship, res types.Reso continue } - subjRes, err := spiceDBRefToResource(e.namespace, rel.Subject.Object) + id, err := gidx.Parse(rel.Subject.Object.ObjectId) + if err != nil { + return nil, err + } + + subj, err := e.NewResourceFromID(id) if err != nil { return nil, err } @@ -387,7 +383,7 @@ func (e *engine) relationshipsToNonRoles(rels []*pb.Relationship, res types.Reso item := types.Relationship{ Resource: res, Relation: rel.Relation, - Subject: subjRes, + Subject: subj, } out = append(out, item) @@ -439,21 +435,28 @@ func (e *engine) ListRoles(ctx context.Context, resource types.Resource, queryTo return out, nil } -// NewResourceFromURN returns a new resource struct from a given urn -func (e *engine) NewResourceFromURN(urn *urnx.URN) (types.Resource, error) { - if urn.Namespace != e.namespace { +// NewResourceFromID returns a new resource struct from a given id +func (e *engine) NewResourceFromID(id gidx.PrefixedID) (types.Resource, error) { + prefix := id.Prefix() + + var rType *types.ResourceType + + for _, resourceType := range e.schema { + if resourceType.IDPrefix == prefix { + rType = &resourceType + + break + } + } + + if rType == nil { return types.Resource{}, errorInvalidNamespace } out := types.Resource{ - Type: urn.ResourceType, - ID: urn.ResourceID, + Type: rType.Name, + ID: id, } return out, nil } - -// NewURNFromResource creates a new URN namespaced to the given engine from the given resource. -func (e *engine) NewURNFromResource(res types.Resource) (*urnx.URN, error) { - return urnx.Build(e.namespace, res.Type, res.ID) -} diff --git a/internal/query/tenants_test.go b/internal/query/tenants_test.go index 4474a0c8..b276a54d 100644 --- a/internal/query/tenants_test.go +++ b/internal/query/tenants_test.go @@ -6,14 +6,13 @@ import ( pb "github.com/authzed/authzed-go/proto/authzed/api/v1" "github.com/authzed/authzed-go/v1" - "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.infratographer.com/permissions-api/internal/spicedbx" "go.infratographer.com/permissions-api/internal/testingx" "go.infratographer.com/permissions-api/internal/types" - "go.infratographer.com/x/urnx" + "go.infratographer.com/x/gidx" ) func testEngine(ctx context.Context, t *testing.T, namespace string) Engine { @@ -87,9 +86,9 @@ func TestRoles(t *testing.T) { } testFn := func(ctx context.Context, actions []string) testingx.TestResult[[]types.Role] { - tenURN, err := urnx.Build(namespace, "tenant", uuid.New()) + tenID, err := gidx.NewID("tnntten") require.NoError(t, err) - tenRes, err := e.NewResourceFromURN(tenURN) + tenRes, err := e.NewResourceFromID(tenID) require.NoError(t, err) _, queryToken, err := e.CreateRole(ctx, tenRes, actions) @@ -115,13 +114,13 @@ func TestAssignments(t *testing.T) { ctx := context.Background() e := testEngine(ctx, t, namespace) - tenURN, err := urnx.Build(namespace, "tenant", uuid.New()) + tenID, err := gidx.NewID("tnntten") require.NoError(t, err) - tenRes, err := e.NewResourceFromURN(tenURN) + tenRes, err := e.NewResourceFromID(tenID) require.NoError(t, err) - subjURN, err := urnx.Build(namespace, "user", uuid.New()) + subjID, err := gidx.NewID("idntusr") require.NoError(t, err) - subjRes, err := e.NewResourceFromURN(subjURN) + subjRes, err := e.NewResourceFromID(subjID) require.NoError(t, err) role, _, err := e.CreateRole( ctx, @@ -171,13 +170,13 @@ func TestRelationships(t *testing.T) { ctx := context.Background() e := testEngine(ctx, t, namespace) - parentURN, err := urnx.Build(namespace, "tenant", uuid.New()) + parentID, err := gidx.NewID("tnntten") require.NoError(t, err) - parentRes, err := e.NewResourceFromURN(parentURN) + parentRes, err := e.NewResourceFromID(parentID) require.NoError(t, err) - childURN, err := urnx.Build(namespace, "tenant", uuid.New()) + childID, err := gidx.NewID("tnntten") require.NoError(t, err) - childRes, err := e.NewResourceFromURN(childURN) + childRes, err := e.NewResourceFromID(childID) require.NoError(t, err) testCases := []testingx.TestCase[[]types.Relationship, []types.Relationship]{ @@ -191,7 +190,7 @@ func TestRelationships(t *testing.T) { }, }, CheckFn: func(ctx context.Context, t *testing.T, res testingx.TestResult[[]types.Relationship]) { - assert.ErrorIs(t, errorInvalidRelationship, res.Err) + assert.ErrorIs(t, res.Err, errorInvalidRelationship) }, }, { @@ -242,17 +241,17 @@ func TestSubjectActions(t *testing.T) { ctx := context.Background() e := testEngine(ctx, t, namespace) - tenURN, err := urnx.Build(namespace, "tenant", uuid.New()) + tenID, err := gidx.NewID("tnntten") require.NoError(t, err) - tenRes, err := e.NewResourceFromURN(tenURN) + tenRes, err := e.NewResourceFromID(tenID) require.NoError(t, err) - otherURN, err := urnx.Build(namespace, "tenant", uuid.New()) + otherID, err := gidx.NewID("tnntten") require.NoError(t, err) - otherRes, err := e.NewResourceFromURN(otherURN) + otherRes, err := e.NewResourceFromID(otherID) require.NoError(t, err) - subjURN, err := urnx.Build(namespace, "user", uuid.New()) + subjID, err := gidx.NewID("idntusr") require.NoError(t, err) - subjRes, err := e.NewResourceFromURN(subjURN) + subjRes, err := e.NewResourceFromID(subjID) require.NoError(t, err) role, _, err := e.CreateRole( ctx, @@ -278,7 +277,7 @@ func TestSubjectActions(t *testing.T) { action: "loadbalancer_update", }, CheckFn: func(ctx context.Context, t *testing.T, res testingx.TestResult[any]) { - assert.ErrorIs(t, ErrActionNotAssigned, res.Err) + assert.ErrorIs(t, res.Err, ErrActionNotAssigned) }, }, { @@ -288,7 +287,7 @@ func TestSubjectActions(t *testing.T) { action: "loadbalancer_delete", }, CheckFn: func(ctx context.Context, t *testing.T, res testingx.TestResult[any]) { - assert.ErrorIs(t, ErrActionNotAssigned, res.Err) + assert.ErrorIs(t, res.Err, ErrActionNotAssigned) }, }, { diff --git a/internal/types/types.go b/internal/types/types.go index bd1548cf..443f55c9 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -1,11 +1,13 @@ // Package types exposes domain types for permissions-api. package types -import "github.com/google/uuid" +import ( + "go.infratographer.com/x/gidx" +) // Role is a collection of permissions. type Role struct { - ID uuid.UUID + ID gidx.PrefixedID Actions []string } @@ -40,6 +42,7 @@ type Action struct { // ResourceType defines a type of resource managed by the api type ResourceType struct { Name string + IDPrefix string Relationships []ResourceTypeRelationship Actions []Action } @@ -47,7 +50,7 @@ type ResourceType struct { // Resource is the object to be acted upon by an subject type Resource struct { Type string - ID uuid.UUID + ID gidx.PrefixedID } // Relationship represents a named association between a resource and a subject. diff --git a/pkg/client/v1/auth.go b/pkg/client/v1/auth.go index db6b2447..29b3a25e 100644 --- a/pkg/client/v1/auth.go +++ b/pkg/client/v1/auth.go @@ -58,16 +58,16 @@ func New(url string, doerClient Doer) (*Client, error) { } // Allowed checks if the client subject is permitted exec the action on the resource -func (c *Client) Allowed(ctx context.Context, action string, resourceURN string) (bool, error) { +func (c *Client) Allowed(ctx context.Context, action string, resourceID string) (bool, error) { ctx, span := tracer.Start(ctx, "SubjectHasAction", trace.WithAttributes( attribute.String("action", action), - attribute.String("resource", resourceURN), + attribute.String("resource", resourceID), )) defer span.End() values := url.Values{} values.Add("action", action) - values.Add("resource", resourceURN) + values.Add("resource", resourceID) err := c.get(ctx, "/allow", values, map[string]string{}) if err != nil {