From 58eda95d93246f648cdbdf2b701d744c9ec5fccf Mon Sep 17 00:00:00 2001 From: gearnode Date: Mon, 10 Mar 2025 13:54:55 +0100 Subject: [PATCH] Rewrite the global id system to include tenant id The API should be aware of the tenant they are working on. Many solution is possible like passing a header, adding the tenant id in each function call, encode the tenant id in the GID. I consider the header as a hack it force the client to keep in mind to pass this header, having to returns an error in case of not defined header and add a non standard header make the API more harder to use. Passing the tenant id everywhere will be a good option but since Relay impose to have node(id: ID!) Node interface it is not possible or by hacking by wrapping node(id: ID!) Node in top query who getting the tenant_id. I finish by simpliy encode the tenant id directly in the object id, it what AWS do too, it allow to always have the information, and it ensure a right data isolation. Signed-off-by: gearnode --- go.sum | 72 +--- pkg/gid/gid.go | 103 +++-- pkg/gid/tenant_id.go | 173 +++++++++ .../coredata/control_state_transition.go | 1 + pkg/probo/coredata/entity_type_reg.go | 2 +- pkg/probo/coredata/organization.go | 14 +- pkg/probo/coredata/scope.go | 21 +- pkg/probo/coredata/task.go | 14 +- pkg/probo/create_control.go | 4 +- pkg/probo/create_evidence.go | 4 +- pkg/probo/create_framework.go | 2 +- pkg/probo/create_organization.go | 5 +- pkg/probo/create_people.go | 2 +- pkg/probo/create_task.go | 4 +- pkg/probo/create_vendor.go | 2 +- pkg/probo/policy_service.go | 2 +- pkg/probo/service.go | 3 +- pkg/probo/update_task_state.go | 2 +- pkg/server/api/console/v1/schema/schema.go | 351 ++++++++++++++---- pkg/server/api/console/v1/v1_resolver.go | 167 +++++---- .../coredata/migrations/20150310T133000Z.sql | 1 + pkg/usrmgr/coredata/user.go | 7 +- pkg/usrmgr/usrmgr.go | 19 +- 23 files changed, 676 insertions(+), 299 deletions(-) create mode 100644 pkg/gid/tenant_id.go create mode 100644 pkg/usrmgr/coredata/migrations/20150310T133000Z.sql diff --git a/go.sum b/go.sum index a854675bc..84b3aaa6e 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,9 @@ gearno.de/ref v0.0.0-20221013162104-a522beda40f4 h1:tS3oPI89+Y33vZRR/DwbNTewefycz1UzEeDYgMKNdzY= gearno.de/ref v0.0.0-20221013162104-a522beda40f4/go.mod h1:yMxgb+Im8XTCLBBgpy8p5xE6/OaWkKyfypNd3HPQpLw= -github.com/99designs/gqlgen v0.17.63 h1:HCdaYDPd9HqUXRchEvmE3EFzELRwLlaJ8DBuyC8Cqto= -github.com/99designs/gqlgen v0.17.63/go.mod h1:sVCM2iwIZisJjTI/DEC3fpH+HFgxY1496ZJ+jbT9IjA= github.com/99designs/gqlgen v0.17.66 h1:2/SRc+h3115fCOZeTtsqrB5R5gTGm+8qCAwcrZa+CXA= github.com/99designs/gqlgen v0.17.66/go.mod h1:gucrb5jK5pgCKzAGuOMMVU9C8PnReecHEHd2UxLQwCg= github.com/PuerkitoBio/goquery v1.9.3 h1:mpJr/ikUA9/GNJB/DBZcGeFDXUtosHRyRrwh7KGdTG0= github.com/PuerkitoBio/goquery v1.9.3/go.mod h1:1ndLHPdTz+DyQPICCWYlYQMPl0oXZj0G6D4LCYA6u4U= -github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY= -github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= @@ -20,8 +16,6 @@ github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38y github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14= -github.com/aws/aws-sdk-go-v2/credentials v1.17.61 h1:Hd/uX6Wo2iUW1JWII+rmyCD7MMhOe7ALwQXN6sKDd1o= -github.com/aws/aws-sdk-go-v2/credentials v1.17.61/go.mod h1:L7vaLkwHY1qgW0gG1zG0z/X0sQ5tpIY5iI13+j3qI80= github.com/aws/aws-sdk-go-v2/credentials v1.17.62 h1:fvtQY3zFzYJ9CfixuAQ96IxDrBajbBWGqjNTCa79ocU= github.com/aws/aws-sdk-go-v2/credentials v1.17.62/go.mod h1:ElETBxIQqcxej++Cs8GyPBbgMys5DgQPTwo7cUPDKt8= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= @@ -40,12 +34,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA= -github.com/aws/aws-sdk-go-v2/service/s3 v1.78.0 h1:EBm8lXevBWe+kK9VOU/IBeOI189WPRwPUc3LvJK9GOs= -github.com/aws/aws-sdk-go-v2/service/s3 v1.78.0/go.mod h1:4qzsZSzB/KiX2EzDjs9D7A8rI/WGJxZceVJIHqtJjIU= github.com/aws/aws-sdk-go-v2/service/s3 v1.78.1 h1:1M0gSbyP6q06gl3384wpoKPaH9G16NPqZFieEhLboSU= github.com/aws/aws-sdk-go-v2/service/s3 v1.78.1/go.mod h1:4qzsZSzB/KiX2EzDjs9D7A8rI/WGJxZceVJIHqtJjIU= -github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= -github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -61,8 +51,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= -github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0= -github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= @@ -77,14 +65,12 @@ github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlnd github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 h1:VD1gqscl4nYs1YxVuSdemTrSgTKrwOWDK0FVFMqm+Cg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0/go.mod h1:4EgsQoS4TOhJizV+JTFg40qx1Ofh3XmXEQNBpgvNT40= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -97,8 +83,6 @@ github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -111,8 +95,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= @@ -138,94 +120,56 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= -github.com/vektah/gqlparser/v2 v2.5.21 h1:Zw1rG2dr1pRR4wqwbVq4d6+xk2f4ut/yo+hwr4QjE08= -github.com/vektah/gqlparser/v2 v2.5.21/go.mod h1:xMl+ta8a5M1Yo1A1Iwt/k7gSpscwSnHZdw7tfhEGfTM= github.com/vektah/gqlparser/v2 v2.5.23 h1:PurJ9wpgEVB7tty1seRUwkIDa/QH5RzkzraiKIjKLfA= github.com/vektah/gqlparser/v2 v2.5.23/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= go.gearno.de/crypto/uuid v0.1.0 h1:94BYg7GYItJ6yYZ1GJayb3VYhI9/FjxuR1nFaduR4hE= go.gearno.de/crypto/uuid v0.1.0/go.mod h1:fnIIvKO9QnsyLO3ZJLJT3r8KZv/p0FOeT5eZKilYWXg= -go.gearno.de/kit v0.0.0-20250124134658-f0d0f3b7f7b3 h1:740uuHnKzIeWXxUBOHsojv+1K48ZOc4kZJ6Nd5fLa5w= -go.gearno.de/kit v0.0.0-20250124134658-f0d0f3b7f7b3/go.mod h1:RsqqVkwq+p4rmtOfYLX8NmQ+kIDi0tgyzAr2GsuLUnE= -go.gearno.de/kit v0.0.0-20250308195817-268b4b34992c h1:APb8N85kyvQZBQNnIWCC9J3eKvwxWN033VpTxACEgqk= -go.gearno.de/kit v0.0.0-20250308195817-268b4b34992c/go.mod h1:RsqqVkwq+p4rmtOfYLX8NmQ+kIDi0tgyzAr2GsuLUnE= -go.gearno.de/kit v0.0.0-20250308213944-095c1cb1316f h1:56SgklE7xrXeWz3oy6r7Xqi1d0iqSemTOePak5gF08k= -go.gearno.de/kit v0.0.0-20250308213944-095c1cb1316f/go.mod h1:RsqqVkwq+p4rmtOfYLX8NmQ+kIDi0tgyzAr2GsuLUnE= go.gearno.de/kit v0.0.0-20250308215532-082d0731efae h1:CrC8b2quMUZQ0ry16N40rvz7EktnEpOyBJrAId0Hdc8= go.gearno.de/kit v0.0.0-20250308215532-082d0731efae/go.mod h1:RsqqVkwq+p4rmtOfYLX8NmQ+kIDi0tgyzAr2GsuLUnE= go.gearno.de/x/panicf v0.1.1 h1:E3Cr9NB8Ry2EsvEG/1eHr7kplP3tEjTf5d56dTX64VQ= go.gearno.de/x/panicf v0.1.1/go.mod h1:VnB8oF0UefMZcYeD4v+Wk4U5Z1uza7PHLlhT2CbNEbU= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= -go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= -go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= -google.golang.org/genproto/googleapis/api v0.0.0-20250122153221-138b5a5a4fd4 h1://y4MHaM7tNLqTeWKyfBIeoAMxwKwRm/nODb5IKA3BE= -google.golang.org/genproto/googleapis/api v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250122153221-138b5a5a4fd4 h1:yrTuav+chrF0zF/joFGICKTzYv7mh/gr9AgEXrVU8ao= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= -google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= -google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= -google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= -google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/gid/gid.go b/pkg/gid/gid.go index fe0b3d837..471cc8e17 100644 --- a/pkg/gid/gid.go +++ b/pkg/gid/gid.go @@ -1,51 +1,40 @@ -// Copyright (c) 2025 Probo Inc . -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - package gid import ( + "crypto/rand" "database/sql/driver" "encoding/base64" "encoding/binary" "fmt" + "time" +) - "go.gearno.de/crypto/uuid" +const ( + GIDSize = 32 // 256 bits total ) type ( - GID uuid.UUID + GID [GIDSize]byte + TenantID [16]byte // 128-bit tenant ID ) var ( - Nil = GID(uuid.Nil) + Nil = GID{} ) // ParseGID parses a string representation of a GID func ParseGID(encoded string) (GID, error) { gid := GID{} - err := gid.UnmarshalText([]byte(encoded)) if err != nil { return Nil, err } - return gid, nil } -// New creates a new GID with a default entity type of 0 -func New() GID { - id, err := NewGID(0) +// New creates a new GID with default entity type and nil tenant ID +func New(tenantID TenantID, entityType uint16) GID { + id, err := NewGID(tenantID, entityType) if err != nil { // This should never happen with a valid random source panic(fmt.Sprintf("failed to generate GID: %v", err)) @@ -53,26 +42,58 @@ func New() GID { return id } -// NewGID creates a new GID with the specified entity type -func NewGID(et uint32) (GID, error) { - id, err := uuid.NewV7() +// NewGID creates a new GID with the specified entity type and tenant ID +// Structure: +// - Bytes 0-15: Tenant ID (full 16 bytes) +// - Bytes 16-17: Entity Type (uint16) +// - Bytes 18-25: Timestamp (milliseconds since epoch) +// - Bytes 26-31: Random data for uniqueness +func NewGID(tenantID TenantID, entityType uint16) (GID, error) { + var id GID + + // Write full tenant ID (16 bytes) + copy(id[0:16], tenantID[:]) + + // Write entity type (2 bytes) + binary.BigEndian.PutUint16(id[16:18], entityType) + + // Get current timestamp (milliseconds) and write it (8 bytes) + now := time.Now().UnixMilli() + binary.BigEndian.PutUint64(id[18:26], uint64(now)) + + // Fill the rest with random data (6 bytes) + _, err := rand.Read(id[26:32]) if err != nil { - return Nil, err + return Nil, fmt.Errorf("failed to generate random bytes: %v", err) } - binary.BigEndian.PutUint32(id[10:14], et) - - return GID(id), nil + return id, nil } +// Value implements the database/sql/driver.Valuer interface func (gid GID) Value() (driver.Value, error) { return gid.String(), nil } -func (gid GID) EntityType() uint32 { - return binary.BigEndian.Uint32(gid[10:14]) +// TenantID extracts the tenant ID from the GID +func (gid GID) TenantID() TenantID { + var tenantID TenantID + copy(tenantID[:], gid[0:16]) + return tenantID } +// EntityType extracts the entity type from the GID +func (gid GID) EntityType() uint16 { + return binary.BigEndian.Uint16(gid[16:18]) +} + +// Timestamp extracts the timestamp from the GID +func (gid GID) Timestamp() time.Time { + millis := binary.BigEndian.Uint64(gid[18:26]) + return time.UnixMilli(int64(millis)) +} + +// Scan implements the database/sql/driver.Scanner interface func (gid *GID) Scan(value interface{}) error { switch v := value.(type) { case string: @@ -82,33 +103,43 @@ func (gid *GID) Scan(value interface{}) error { return err } - *gid = GID(id) + if len(id) != GIDSize { + return fmt.Errorf("invalid length for GID: got %d, want %d", len(id), GIDSize) + } + + copy((*gid)[:], id) default: return fmt.Errorf("invalid type for GID: expected string, got %T", value) } return nil } +// String returns the base64url encoded representation of the GID func (gid GID) String() string { return base64.RawURLEncoding.EncodeToString(gid[:]) } +// MarshalText returns the base64url encoded representation of the GID func (gid GID) MarshalText() ([]byte, error) { enc := base64.RawURLEncoding - buf := make([]byte, enc.EncodedLen(len(gid))) enc.Encode(buf, gid[:]) - return buf, nil } +// UnmarshalText decodes a base64url encoded GID func (gid *GID) UnmarshalText(encoded []byte) error { enc := base64.RawURLEncoding - - _, err := enc.Decode(gid[:], encoded) + dst := make([]byte, enc.DecodedLen(len(encoded))) + n, err := enc.Decode(dst, encoded) if err != nil { return err } + if n != GIDSize { + return fmt.Errorf("invalid length for GID: got %d, want %d", n, GIDSize) + } + + copy((*gid)[:], dst) return nil } diff --git a/pkg/gid/tenant_id.go b/pkg/gid/tenant_id.go new file mode 100644 index 000000000..4fcb50b70 --- /dev/null +++ b/pkg/gid/tenant_id.go @@ -0,0 +1,173 @@ +package gid + +import ( + "crypto/rand" + "database/sql/driver" + "encoding/base64" + "encoding/binary" + "fmt" + "os" + "sync/atomic" + "time" +) + +var ( + // NilTenant represents an empty tenant ID + NilTenant = TenantID{} + + // Global generation singleton + defaultTenantGenerator = newTenantGenerator() +) + +// TenantGenerator handles creation of unique tenant IDs +type tenantGenerator struct { + // Process-specific values + machineID [6]byte // 48 bits for machine identifier + processID uint16 // 16 bits for process + counter uint32 // Counter for the sequence +} + +// NewTenantID generates a new globally unique tenant ID +func NewTenantID() TenantID { + return defaultTenantGenerator.NewTenantID() +} + +// ParseTenantID parses a string representation into a TenantID +func ParseTenantID(s string) (TenantID, error) { + var id TenantID + decoded, err := base64.RawURLEncoding.DecodeString(s) + if err != nil { + return NilTenant, fmt.Errorf("invalid tenant ID encoding: %w", err) + } + + if len(decoded) != len(id) { + return NilTenant, fmt.Errorf("invalid tenant ID length: got %d, want %d", len(decoded), len(id)) + } + + copy(id[:], decoded) + return id, nil +} + +// newTenantGenerator creates a new generator with machine-specific components +func newTenantGenerator() *tenantGenerator { + g := &tenantGenerator{ + counter: 0, + } + + // Generate machine ID component + if _, err := rand.Read(g.machineID[:]); err != nil { + // Fallback if random source fails + hostname, _ := os.Hostname() + copy(g.machineID[:], []byte(hostname)) + + // Pad with timestamp bits if hostname is short + if len(hostname) < len(g.machineID) { + ts := time.Now().UnixNano() + binary.BigEndian.PutUint32(g.machineID[len(hostname):], uint32(ts)) + } + } + + // Set process ID from OS PID + g.processID = uint16(os.Getpid() & 0xFFFF) + + return g +} + +// NewTenantID generates a new 128-bit tenant ID with the structure: +// - 48 bits: Machine ID (random, unique per machine) +// - 16 bits: Process ID (unique per process on machine) +// - 48 bits: Timestamp (milliseconds, sequential) +// - 16 bits: Counter (increments per ID) +func (g *tenantGenerator) NewTenantID() TenantID { + // Create new ID + var id TenantID + + // 1. Get timestamp (48 bits = milliseconds since epoch) + now := time.Now().UnixMilli() + + // 2. Increment counter atomically (16 bits used) + count := atomic.AddUint32(&g.counter, 1) & 0xFFFF + + // 3. Assemble the ID + // First 6 bytes: Machine ID + copy(id[0:6], g.machineID[:]) + + // Next 2 bytes: Process ID + binary.BigEndian.PutUint16(id[6:8], g.processID) + + // Next 6 bytes: Timestamp (48 bits) + id[8] = byte(now >> 40) + id[9] = byte(now >> 32) + id[10] = byte(now >> 24) + id[11] = byte(now >> 16) + id[12] = byte(now >> 8) + id[13] = byte(now) + + // Last 2 bytes: Counter + binary.BigEndian.PutUint16(id[14:16], uint16(count)) + + return id +} + +// Value implements the database/sql/driver.Valuer interface +func (id TenantID) Value() (driver.Value, error) { + return id.String(), nil +} + +// Scan implements the database/sql.Scanner interface +func (id *TenantID) Scan(value interface{}) error { + switch v := value.(type) { + case string: + decoded, err := base64.RawURLEncoding.DecodeString(v) + if err != nil { + return err + } + + if len(decoded) != len(*id) { + return fmt.Errorf("invalid tenant ID length: got %d, want %d", len(decoded), len(*id)) + } + + copy((*id)[:], decoded) + return nil + default: + return fmt.Errorf("invalid type for TenantID: expected string, got %T", value) + } +} + +// String returns the base64 representation of the TenantID +func (id TenantID) String() string { + return base64.RawURLEncoding.EncodeToString(id[:]) +} + +// MarshalText returns the base64 representation for JSON encoding +func (id TenantID) MarshalText() ([]byte, error) { + encoded := base64.RawURLEncoding.EncodeToString(id[:]) + return []byte(encoded), nil +} + +// UnmarshalText parses the base64 representation for JSON decoding +func (id *TenantID) UnmarshalText(text []byte) error { + decoded, err := base64.RawURLEncoding.DecodeString(string(text)) + if err != nil { + return err + } + + if len(decoded) != len(*id) { + return fmt.Errorf("invalid tenant ID length: got %d, want %d", len(decoded), len(*id)) + } + + copy((*id)[:], decoded) + return nil +} + +// IsValid returns true if the tenant ID is not nil +func (id TenantID) IsValid() bool { + return id != NilTenant +} + +// Timestamp extracts the timestamp from the TenantID +func (id TenantID) Timestamp() time.Time { + millis := int64(id[8])<<40 | int64(id[9])<<32 | int64(id[10])<<24 | + int64(id[11])<<16 | int64(id[12])<<8 | int64(id[13]) + return time.UnixMilli(millis) +} diff --git a/pkg/probo/coredata/control_state_transition.go b/pkg/probo/coredata/control_state_transition.go index c69c924e4..1533ff1c4 100644 --- a/pkg/probo/coredata/control_state_transition.go +++ b/pkg/probo/coredata/control_state_transition.go @@ -92,6 +92,7 @@ func (cst *ControlStateTransitions) LoadByControlID( q := ` SELECT id, + tenant_id, control_id, from_state, to_state, diff --git a/pkg/probo/coredata/entity_type_reg.go b/pkg/probo/coredata/entity_type_reg.go index b88c691c4..7f803948d 100644 --- a/pkg/probo/coredata/entity_type_reg.go +++ b/pkg/probo/coredata/entity_type_reg.go @@ -15,7 +15,7 @@ package coredata const ( - OrganizationEntityType uint32 = iota + OrganizationEntityType uint16 = iota FrameworkEntityType ControlEntityType TaskEntityType diff --git a/pkg/probo/coredata/organization.go b/pkg/probo/coredata/organization.go index 5b1b6f572..47da0d8a3 100644 --- a/pkg/probo/coredata/organization.go +++ b/pkg/probo/coredata/organization.go @@ -27,11 +27,12 @@ import ( type ( Organization struct { - ID gid.GID `db:"id"` - Name string `db:"name"` - LogoURL string `db:"logo_url"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` + ID gid.GID `db:"id"` + TenantID gid.TenantID `db:"tenant_id"` + Name string `db:"name"` + LogoURL string `db:"logo_url"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` } ) @@ -43,6 +44,7 @@ func (o *Organization) LoadByID( ) error { q := ` SELECT + tenant_id, id, name, logo_url, @@ -92,7 +94,7 @@ INSERT INTO organizations ( ` args := pgx.StrictNamedArgs{ - "tenant_id": o.ID, // We use the organization ID as tenant ID + "tenant_id": o.TenantID, "id": o.ID, "name": o.Name, "logo_url": o.LogoURL, diff --git a/pkg/probo/coredata/scope.go b/pkg/probo/coredata/scope.go index b2bc1abe3..24015e7a2 100644 --- a/pkg/probo/coredata/scope.go +++ b/pkg/probo/coredata/scope.go @@ -15,6 +15,9 @@ package coredata import ( + "fmt" + + "github.com/getprobo/probo/pkg/gid" "github.com/jackc/pgx/v5" ) @@ -22,13 +25,13 @@ type ( Scoper interface { SQLArguments() pgx.StrictNamedArgs SQLFragment() string - GetTenantID() *string + GetTenantID() gid.TenantID } NoScope struct{} Scope struct { - TenantID string + tenantID gid.TenantID } ) @@ -49,19 +52,19 @@ func (*NoScope) SQLFragment() string { return "TRUE" } -func (*NoScope) GetTenantID() *string { - return nil +func (*NoScope) GetTenantID() gid.TenantID { + panic(fmt.Errorf("cannot get tenant id from no scope")) } -func NewScope(tenantID string) *Scope { +func NewScope(tenantID gid.TenantID) *Scope { return &Scope{ - TenantID: tenantID, + tenantID: tenantID, } } func (s *Scope) SQLArguments() pgx.StrictNamedArgs { return pgx.StrictNamedArgs{ - "tenant_id": s.TenantID, + "tenant_id": s.tenantID, } } @@ -69,6 +72,6 @@ func (*Scope) SQLFragment() string { return "tenant_id = @tenant_id" } -func (s *Scope) GetTenantID() *string { - return &s.TenantID +func (s *Scope) GetTenantID() gid.TenantID { + return s.tenantID } diff --git a/pkg/probo/coredata/task.go b/pkg/probo/coredata/task.go index 31baa7c01..b346874fc 100644 --- a/pkg/probo/coredata/task.go +++ b/pkg/probo/coredata/task.go @@ -69,7 +69,7 @@ WITH controls_tasks ct ON ct.task_id = t.id WHERE - %s + t.tenant_id = @tenant_id AND id = @task_id ), task_states AS ( @@ -104,9 +104,7 @@ WHERE LIMIT 1; ` - q = fmt.Sprintf(q, scope.SQLFragment()) - - args := pgx.StrictNamedArgs{"task_id": taskID} + args := pgx.StrictNamedArgs{"tenant_id": scope.GetTenantID(), "task_id": taskID} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) @@ -153,11 +151,13 @@ WITH task_insert AS ( ) INSERT INTO controls_tasks ( task_id, + tenant_id, control_id, created_at ) VALUES ( (SELECT id FROM task_insert), + @tenant_id, @control_id, @created_at ); @@ -202,7 +202,7 @@ WITH ct.task_id = t.id AND ct.control_id = @control_id WHERE - %s + t.tenant_id = @tenant_id ), task_states AS ( SELECT @@ -233,9 +233,9 @@ WHERE AND %s ` - q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) + q = fmt.Sprintf(q, cursor.SQLFragment()) - args := pgx.StrictNamedArgs{"control_id": controlID} + args := pgx.StrictNamedArgs{"tenant_id": scope.GetTenantID(), "control_id": controlID} maps.Copy(args, scope.SQLArguments()) maps.Copy(args, cursor.SQLArguments()) diff --git a/pkg/probo/create_control.go b/pkg/probo/create_control.go index c7dcdcbb3..550b76987 100644 --- a/pkg/probo/create_control.go +++ b/pkg/probo/create_control.go @@ -40,11 +40,11 @@ func (s Service) CreateControl( req CreateControlRequest, ) (*coredata.Control, error) { now := time.Now() - controlID, err := gid.NewGID(coredata.ControlEntityType) + controlID, err := gid.NewGID(s.scope.GetTenantID(), coredata.ControlEntityType) if err != nil { return nil, fmt.Errorf("cannot create control global id: %w", err) } - controlStateTransitionID, err := gid.NewGID(coredata.ControlStateTransitionEntityType) + controlStateTransitionID, err := gid.NewGID(s.scope.GetTenantID(), coredata.ControlStateTransitionEntityType) if err != nil { return nil, fmt.Errorf("cannot create control state transition global id: %w", err) } diff --git a/pkg/probo/create_evidence.go b/pkg/probo/create_evidence.go index 536d2f48a..118613a38 100644 --- a/pkg/probo/create_evidence.go +++ b/pkg/probo/create_evidence.go @@ -44,11 +44,11 @@ func (s Service) CreateEvidence( req CreateEvidenceRequest, ) (*coredata.Evidence, error) { now := time.Now() - evidenceID, err := gid.NewGID(coredata.EvidenceEntityType) + evidenceID, err := gid.NewGID(s.scope.GetTenantID(), coredata.EvidenceEntityType) if err != nil { return nil, fmt.Errorf("cannot create evidence global id: %w", err) } - evidenceStateTransitionID, err := gid.NewGID(coredata.EvidenceStateTransitionEntityType) + evidenceStateTransitionID, err := gid.NewGID(s.scope.GetTenantID(), coredata.EvidenceStateTransitionEntityType) if err != nil { return nil, fmt.Errorf("cannot create evidence state transition: %w", err) } diff --git a/pkg/probo/create_framework.go b/pkg/probo/create_framework.go index 74fd5291a..176ab1866 100644 --- a/pkg/probo/create_framework.go +++ b/pkg/probo/create_framework.go @@ -38,7 +38,7 @@ func (s Service) CreateFramework( req CreateFrameworkRequest, ) (*coredata.Framework, error) { now := time.Now() - frameworkID, err := gid.NewGID(coredata.FrameworkEntityType) + frameworkID, err := gid.NewGID(s.scope.GetTenantID(), coredata.FrameworkEntityType) if err != nil { return nil, fmt.Errorf("cannot create global id: %w", err) } diff --git a/pkg/probo/create_organization.go b/pkg/probo/create_organization.go index ae062571a..d28ade916 100644 --- a/pkg/probo/create_organization.go +++ b/pkg/probo/create_organization.go @@ -34,14 +34,17 @@ func (s Service) CreateOrganization( ctx context.Context, req CreateOrganizationRequest, ) (*coredata.Organization, error) { + tenantID := gid.NewTenantID() + now := time.Now() - organizationID, err := gid.NewGID(coredata.OrganizationEntityType) + organizationID, err := gid.NewGID(tenantID, coredata.OrganizationEntityType) if err != nil { return nil, fmt.Errorf("cannot create organization global id: %w", err) } organization := &coredata.Organization{ ID: organizationID, + TenantID: tenantID, Name: req.Name, CreatedAt: now, UpdatedAt: now, diff --git a/pkg/probo/create_people.go b/pkg/probo/create_people.go index b96a26a2d..f5e4e83da 100644 --- a/pkg/probo/create_people.go +++ b/pkg/probo/create_people.go @@ -39,7 +39,7 @@ func (s Service) CreatePeople( req CreatePeopleRequest, ) (*coredata.People, error) { now := time.Now() - peopleID, err := gid.NewGID(coredata.PeopleEntityType) + peopleID, err := gid.NewGID(s.scope.GetTenantID(), coredata.PeopleEntityType) if err != nil { return nil, fmt.Errorf("cannot create people global id: %w", err) } diff --git a/pkg/probo/create_task.go b/pkg/probo/create_task.go index 7f4b430f5..89edae03e 100644 --- a/pkg/probo/create_task.go +++ b/pkg/probo/create_task.go @@ -39,11 +39,11 @@ func (s Service) CreateTask( req CreateTaskRequest, ) (*coredata.Task, error) { now := time.Now() - taskID, err := gid.NewGID(coredata.TaskEntityType) + taskID, err := gid.NewGID(s.scope.GetTenantID(), coredata.TaskEntityType) if err != nil { return nil, fmt.Errorf("cannot create task global id: %w", err) } - taskStateTransitionID, err := gid.NewGID(coredata.TaskStateTransitionEntityType) + taskStateTransitionID, err := gid.NewGID(s.scope.GetTenantID(), coredata.TaskStateTransitionEntityType) if err != nil { return nil, fmt.Errorf("cannot create task state transition global id: %w", err) } diff --git a/pkg/probo/create_vendor.go b/pkg/probo/create_vendor.go index 2efb93b3f..123018ee5 100644 --- a/pkg/probo/create_vendor.go +++ b/pkg/probo/create_vendor.go @@ -44,7 +44,7 @@ func (s Service) CreateVendor( req CreateVendorRequest, ) (*coredata.Vendor, error) { now := time.Now() - vendorID, err := gid.NewGID(coredata.VendorEntityType) + vendorID, err := gid.NewGID(s.scope.GetTenantID(), coredata.VendorEntityType) if err != nil { return nil, fmt.Errorf("cannot create vendor global id: %w", err) } diff --git a/pkg/probo/policy_service.go b/pkg/probo/policy_service.go index 5b5e768de..55654ef1e 100644 --- a/pkg/probo/policy_service.go +++ b/pkg/probo/policy_service.go @@ -61,7 +61,7 @@ func (s *PolicyService) Create( req CreatePolicyRequest, ) (*coredata.Policy, error) { now := time.Now() - policyID, err := gid.NewGID(coredata.PolicyEntityType) + policyID, err := gid.NewGID(s.svc.scope.GetTenantID(), coredata.PolicyEntityType) if err != nil { return nil, fmt.Errorf("cannot create policy global id: %w", err) } diff --git a/pkg/probo/service.go b/pkg/probo/service.go index 94dc8d803..7f6ed58b3 100644 --- a/pkg/probo/service.go +++ b/pkg/probo/service.go @@ -19,6 +19,7 @@ import ( "fmt" "github.com/aws/aws-sdk-go-v2/service/s3" + "github.com/getprobo/probo/pkg/gid" "github.com/getprobo/probo/pkg/probo/coredata" "go.gearno.de/kit/migrator" "go.gearno.de/kit/pg" @@ -62,7 +63,7 @@ func NewService( return svc, nil } -func (s *Service) WithTenant(tenantID string) *Service { +func (s *Service) WithTenant(tenantID gid.TenantID) *Service { newSvc := &Service{ pg: s.pg, s3: s.s3, diff --git a/pkg/probo/update_task_state.go b/pkg/probo/update_task_state.go index db31fabce..fd8412826 100644 --- a/pkg/probo/update_task_state.go +++ b/pkg/probo/update_task_state.go @@ -46,7 +46,7 @@ func (s Service) UpdateTaskState( return task, nil } - taskStateTransitionID, err := gid.NewGID(coredata.TaskStateTransitionEntityType) + taskStateTransitionID, err := gid.NewGID(s.scope.GetTenantID(), coredata.TaskStateTransitionEntityType) if err != nil { return nil, fmt.Errorf("cannot create task state transition global id: %w", err) } diff --git a/pkg/server/api/console/v1/schema/schema.go b/pkg/server/api/console/v1/schema/schema.go index 3c6f0244c..40c3fabd9 100644 --- a/pkg/server/api/console/v1/schema/schema.go +++ b/pkg/server/api/console/v1/schema/schema.go @@ -4166,6 +4166,52 @@ func (ec *executionContext) field_User_organizations_argsBefore( return zeroVal, nil } +func (ec *executionContext) field___Directive_args_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field___Directive_args_argsIncludeDeprecated(ctx, rawArgs) + if err != nil { + return nil, err + } + args["includeDeprecated"] = arg0 + return args, nil +} +func (ec *executionContext) field___Directive_args_argsIncludeDeprecated( + ctx context.Context, + rawArgs map[string]any, +) (*bool, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) + if tmp, ok := rawArgs["includeDeprecated"]; ok { + return ec.unmarshalOBoolean2ᚖbool(ctx, tmp) + } + + var zeroVal *bool + return zeroVal, nil +} + +func (ec *executionContext) field___Field_args_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field___Field_args_argsIncludeDeprecated(ctx, rawArgs) + if err != nil { + return nil, err + } + args["includeDeprecated"] = arg0 + return args, nil +} +func (ec *executionContext) field___Field_args_argsIncludeDeprecated( + ctx context.Context, + rawArgs map[string]any, +) (*bool, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) + if tmp, ok := rawArgs["includeDeprecated"]; ok { + return ec.unmarshalOBoolean2ᚖbool(ctx, tmp) + } + + var zeroVal *bool + return zeroVal, nil +} + func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -9984,6 +10030,8 @@ func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -9996,8 +10044,8 @@ func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) + case "isOneOf": + return ec.fieldContext___Type_isOneOf(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) }, @@ -12435,6 +12483,44 @@ func (ec *executionContext) fieldContext___Directive_description(_ context.Conte return fc, nil } +func (ec *executionContext) ___Directive_isRepeatable(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Directive_isRepeatable(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsRepeatable, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Directive_isRepeatable(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Directive", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) ___Directive_locations(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { fc, err := ec.fieldContext___Directive_locations(ctx, field) if err != nil { @@ -12498,7 +12584,7 @@ func (ec *executionContext) ___Directive_args(ctx context.Context, field graphql return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Directive_args(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Directive_args(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Directive", Field: field, @@ -12514,47 +12600,18 @@ func (ec *executionContext) fieldContext___Directive_args(_ context.Context, fie return ec.fieldContext___InputValue_type(ctx, field) case "defaultValue": return ec.fieldContext___InputValue_defaultValue(ctx, field) + case "isDeprecated": + return ec.fieldContext___InputValue_isDeprecated(ctx, field) + case "deprecationReason": + return ec.fieldContext___InputValue_deprecationReason(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type __InputValue", field.Name) }, } - return fc, nil -} - -func (ec *executionContext) ___Directive_isRepeatable(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Directive_isRepeatable(ctx, field) - if err != nil { - return graphql.Null - } ctx = graphql.WithFieldContext(ctx, fc) - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { - ctx = rctx // use context from middleware stack in children - return obj.IsRepeatable, nil - }) - if err != nil { + if fc.Args, err = ec.field___Directive_args_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(bool) - fc.Result = res - return ec.marshalNBoolean2bool(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext___Directive_isRepeatable(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "__Directive", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") - }, + return fc, err } return fc, nil } @@ -12803,7 +12860,7 @@ func (ec *executionContext) ___Field_args(ctx context.Context, field graphql.Col return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Field_args(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Field_args(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Field", Field: field, @@ -12819,10 +12876,19 @@ func (ec *executionContext) fieldContext___Field_args(_ context.Context, field g return ec.fieldContext___InputValue_type(ctx, field) case "defaultValue": return ec.fieldContext___InputValue_defaultValue(ctx, field) + case "isDeprecated": + return ec.fieldContext___InputValue_isDeprecated(ctx, field) + case "deprecationReason": + return ec.fieldContext___InputValue_deprecationReason(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type __InputValue", field.Name) }, } + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field___Field_args_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } return fc, nil } @@ -12865,6 +12931,8 @@ func (ec *executionContext) fieldContext___Field_type(_ context.Context, field g return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -12877,8 +12945,8 @@ func (ec *executionContext) fieldContext___Field_type(_ context.Context, field g return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) + case "isOneOf": + return ec.fieldContext___Type_isOneOf(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) }, @@ -13071,6 +13139,8 @@ func (ec *executionContext) fieldContext___InputValue_type(_ context.Context, fi return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -13083,8 +13153,8 @@ func (ec *executionContext) fieldContext___InputValue_type(_ context.Context, fi return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) + case "isOneOf": + return ec.fieldContext___Type_isOneOf(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) }, @@ -13127,6 +13197,79 @@ func (ec *executionContext) fieldContext___InputValue_defaultValue(_ context.Con return fc, nil } +func (ec *executionContext) ___InputValue_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___InputValue_isDeprecated(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsDeprecated(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___InputValue_isDeprecated(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__InputValue", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___InputValue_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___InputValue_deprecationReason(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.DeprecationReason(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___InputValue_deprecationReason(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__InputValue", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) ___Schema_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { fc, err := ec.fieldContext___Schema_description(ctx, field) if err != nil { @@ -13201,6 +13344,8 @@ func (ec *executionContext) fieldContext___Schema_types(_ context.Context, field return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -13213,8 +13358,8 @@ func (ec *executionContext) fieldContext___Schema_types(_ context.Context, field return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) + case "isOneOf": + return ec.fieldContext___Type_isOneOf(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) }, @@ -13261,6 +13406,8 @@ func (ec *executionContext) fieldContext___Schema_queryType(_ context.Context, f return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -13273,8 +13420,8 @@ func (ec *executionContext) fieldContext___Schema_queryType(_ context.Context, f return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) + case "isOneOf": + return ec.fieldContext___Type_isOneOf(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) }, @@ -13318,6 +13465,8 @@ func (ec *executionContext) fieldContext___Schema_mutationType(_ context.Context return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -13330,8 +13479,8 @@ func (ec *executionContext) fieldContext___Schema_mutationType(_ context.Context return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) + case "isOneOf": + return ec.fieldContext___Type_isOneOf(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) }, @@ -13375,6 +13524,8 @@ func (ec *executionContext) fieldContext___Schema_subscriptionType(_ context.Con return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -13387,8 +13538,8 @@ func (ec *executionContext) fieldContext___Schema_subscriptionType(_ context.Con return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) + case "isOneOf": + return ec.fieldContext___Type_isOneOf(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) }, @@ -13433,12 +13584,12 @@ func (ec *executionContext) fieldContext___Schema_directives(_ context.Context, return ec.fieldContext___Directive_name(ctx, field) case "description": return ec.fieldContext___Directive_description(ctx, field) + case "isRepeatable": + return ec.fieldContext___Directive_isRepeatable(ctx, field) case "locations": return ec.fieldContext___Directive_locations(ctx, field) case "args": return ec.fieldContext___Directive_args(ctx, field) - case "isRepeatable": - return ec.fieldContext___Directive_isRepeatable(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type __Directive", field.Name) }, @@ -13554,6 +13705,41 @@ func (ec *executionContext) fieldContext___Type_description(_ context.Context, f return fc, nil } +func (ec *executionContext) ___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_specifiedByURL(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.SpecifiedByURL(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_specifiedByURL(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) ___Type_fields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { fc, err := ec.fieldContext___Type_fields(ctx, field) if err != nil { @@ -13644,6 +13830,8 @@ func (ec *executionContext) fieldContext___Type_interfaces(_ context.Context, fi return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -13656,8 +13844,8 @@ func (ec *executionContext) fieldContext___Type_interfaces(_ context.Context, fi return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) + case "isOneOf": + return ec.fieldContext___Type_isOneOf(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) }, @@ -13701,6 +13889,8 @@ func (ec *executionContext) fieldContext___Type_possibleTypes(_ context.Context, return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -13713,8 +13903,8 @@ func (ec *executionContext) fieldContext___Type_possibleTypes(_ context.Context, return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) + case "isOneOf": + return ec.fieldContext___Type_isOneOf(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) }, @@ -13810,6 +14000,10 @@ func (ec *executionContext) fieldContext___Type_inputFields(_ context.Context, f return ec.fieldContext___InputValue_type(ctx, field) case "defaultValue": return ec.fieldContext___InputValue_defaultValue(ctx, field) + case "isDeprecated": + return ec.fieldContext___InputValue_isDeprecated(ctx, field) + case "deprecationReason": + return ec.fieldContext___InputValue_deprecationReason(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type __InputValue", field.Name) }, @@ -13853,6 +14047,8 @@ func (ec *executionContext) fieldContext___Type_ofType(_ context.Context, field return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -13865,8 +14061,8 @@ func (ec *executionContext) fieldContext___Type_ofType(_ context.Context, field return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) + case "isOneOf": + return ec.fieldContext___Type_isOneOf(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) }, @@ -13874,15 +14070,15 @@ func (ec *executionContext) fieldContext___Type_ofType(_ context.Context, field return fc, nil } -func (ec *executionContext) ___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Type_specifiedByURL(ctx, field) +func (ec *executionContext) ___Type_isOneOf(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_isOneOf(ctx, field) if err != nil { return graphql.Null } ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { ctx = rctx // use context from middleware stack in children - return obj.SpecifiedByURL(), nil + return obj.IsOneOf(), nil }) if err != nil { ec.Error(ctx, err) @@ -13891,19 +14087,19 @@ func (ec *executionContext) ___Type_specifiedByURL(ctx context.Context, field gr if resTmp == nil { return graphql.Null } - res := resTmp.(*string) + res := resTmp.(bool) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalOBoolean2bool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_specifiedByURL(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Type_isOneOf(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Type", Field: field, IsMethod: true, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type Boolean does not have child fields") }, } return fc, nil @@ -18254,6 +18450,11 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS } case "description": out.Values[i] = ec.___Directive_description(ctx, field, obj) + case "isRepeatable": + out.Values[i] = ec.___Directive_isRepeatable(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "locations": out.Values[i] = ec.___Directive_locations(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -18264,11 +18465,6 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS if out.Values[i] == graphql.Null { out.Invalids++ } - case "isRepeatable": - out.Values[i] = ec.___Directive_isRepeatable(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -18423,6 +18619,13 @@ func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.Selection } case "defaultValue": out.Values[i] = ec.___InputValue_defaultValue(ctx, field, obj) + case "isDeprecated": + out.Values[i] = ec.___InputValue_isDeprecated(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "deprecationReason": + out.Values[i] = ec.___InputValue_deprecationReason(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -18521,6 +18724,8 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o out.Values[i] = ec.___Type_name(ctx, field, obj) case "description": out.Values[i] = ec.___Type_description(ctx, field, obj) + case "specifiedByURL": + out.Values[i] = ec.___Type_specifiedByURL(ctx, field, obj) case "fields": out.Values[i] = ec.___Type_fields(ctx, field, obj) case "interfaces": @@ -18533,8 +18738,8 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o out.Values[i] = ec.___Type_inputFields(ctx, field, obj) case "ofType": out.Values[i] = ec.___Type_ofType(ctx, field, obj) - case "specifiedByURL": - out.Values[i] = ec.___Type_specifiedByURL(ctx, field, obj) + case "isOneOf": + out.Values[i] = ec.___Type_isOneOf(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index 381594176..f4a120a95 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -2,7 +2,7 @@ package console_v1 // This file will be automatically regenerated based on the schema, any resolver implementations // will be copied through when generating and any unknown code will be moved to the end. -// Code generated by github.com/99designs/gqlgen version v0.17.63 +// Code generated by github.com/99designs/gqlgen version v0.17.66 import ( "context" @@ -20,9 +20,10 @@ import ( // StateTransisions is the resolver for the stateTransisions field. func (r *controlResolver) StateTransisions(ctx context.Context, obj *types.Control, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.ControlStateTransitionConnection, error) { + svc := r.proboSvc.WithTenant(obj.ID.TenantID()) cursor := types.NewCursor(first, after, last, before) - page, err := r.proboSvc.ListControlStateTransitions(ctx, obj.ID, cursor) + page, err := svc.ListControlStateTransitions(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list control tasks: %w", err) } @@ -32,9 +33,10 @@ func (r *controlResolver) StateTransisions(ctx context.Context, obj *types.Contr // Tasks is the resolver for the tasks field. func (r *controlResolver) Tasks(ctx context.Context, obj *types.Control, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.TaskConnection, error) { + svc := r.proboSvc.WithTenant(obj.ID.TenantID()) cursor := types.NewCursor(first, after, last, before) - page, err := r.proboSvc.ListControlTasks(ctx, obj.ID, cursor) + page, err := svc.ListControlTasks(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list control tasks: %w", err) } @@ -44,7 +46,9 @@ func (r *controlResolver) Tasks(ctx context.Context, obj *types.Control, first * // FileURL is the resolver for the fileUrl field. func (r *evidenceResolver) FileURL(ctx context.Context, obj *types.Evidence) (string, error) { - fileURL, err := r.proboSvc.GetEvidenceFileURL(ctx, obj.ID, 15*time.Minute) + svc := r.proboSvc.WithTenant(obj.ID.TenantID()) + + fileURL, err := svc.GetEvidenceFileURL(ctx, obj.ID, 15*time.Minute) if err != nil { return "", fmt.Errorf("cannot generate file URL: %w", err) } @@ -54,9 +58,10 @@ func (r *evidenceResolver) FileURL(ctx context.Context, obj *types.Evidence) (st // StateTransisions is the resolver for the stateTransisions field. func (r *evidenceResolver) StateTransisions(ctx context.Context, obj *types.Evidence, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.EvidenceStateTransitionConnection, error) { + svc := r.proboSvc.WithTenant(obj.ID.TenantID()) cursor := types.NewCursor(first, after, last, before) - page, err := r.proboSvc.ListEvidenceStateTransitions(ctx, obj.ID, cursor) + page, err := svc.ListEvidenceStateTransitions(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list evidence state transitions: %w", err) } @@ -66,9 +71,10 @@ func (r *evidenceResolver) StateTransisions(ctx context.Context, obj *types.Evid // Controls is the resolver for the controls field. func (r *frameworkResolver) Controls(ctx context.Context, obj *types.Framework, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.ControlConnection, error) { + svc := r.proboSvc.WithTenant(obj.ID.TenantID()) cursor := types.NewCursor(first, after, last, before) - page, err := r.proboSvc.ListFrameworkControls(ctx, obj.ID, cursor) + page, err := svc.ListFrameworkControls(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list framework controls: %w", err) } @@ -78,7 +84,9 @@ func (r *frameworkResolver) Controls(ctx context.Context, obj *types.Framework, // CreateVendor is the resolver for the createVendor field. func (r *mutationResolver) CreateVendor(ctx context.Context, input types.CreateVendorInput) (*types.CreateVendorPayload, error) { - vendor, err := r.proboSvc.CreateVendor(ctx, probo.CreateVendorRequest{ + svc := r.proboSvc.WithTenant(input.OrganizationID.TenantID()) + + vendor, err := svc.CreateVendor(ctx, probo.CreateVendorRequest{ OrganizationID: input.OrganizationID, Name: input.Name, Description: input.Description, @@ -100,7 +108,9 @@ func (r *mutationResolver) CreateVendor(ctx context.Context, input types.CreateV // UpdateVendor is the resolver for the updateVendor field. func (r *mutationResolver) UpdateVendor(ctx context.Context, input types.UpdateVendorInput) (*types.UpdateVendorPayload, error) { - vendor, err := r.proboSvc.UpdateVendor(ctx, probo.UpdateVendorRequest{ + svc := r.proboSvc.WithTenant(input.ID.TenantID()) + + vendor, err := svc.UpdateVendor(ctx, probo.UpdateVendorRequest{ ID: input.ID, ExpectedVersion: input.ExpectedVersion, Name: input.Name, @@ -124,7 +134,9 @@ func (r *mutationResolver) UpdateVendor(ctx context.Context, input types.UpdateV // DeleteVendor is the resolver for the deleteVendor field. func (r *mutationResolver) DeleteVendor(ctx context.Context, input types.DeleteVendorInput) (*types.DeleteVendorPayload, error) { - err := r.proboSvc.DeleteVendor(ctx, input.VendorID) + svc := r.proboSvc.WithTenant(input.VendorID.TenantID()) + + err := svc.DeleteVendor(ctx, input.VendorID) if err != nil { return nil, fmt.Errorf("cannot delete vendor: %w", err) } @@ -136,7 +148,9 @@ func (r *mutationResolver) DeleteVendor(ctx context.Context, input types.DeleteV // CreatePeople is the resolver for the createPeople field. func (r *mutationResolver) CreatePeople(ctx context.Context, input types.CreatePeopleInput) (*types.CreatePeoplePayload, error) { - people, err := r.proboSvc.CreatePeople(ctx, probo.CreatePeopleRequest{ + svc := r.proboSvc.WithTenant(input.OrganizationID.TenantID()) + + people, err := svc.CreatePeople(ctx, probo.CreatePeopleRequest{ OrganizationID: input.OrganizationID, FullName: input.FullName, PrimaryEmailAddress: input.PrimaryEmailAddress, @@ -155,7 +169,9 @@ func (r *mutationResolver) CreatePeople(ctx context.Context, input types.CreateP // UpdatePeople is the resolver for the updatePeople field. func (r *mutationResolver) UpdatePeople(ctx context.Context, input types.UpdatePeopleInput) (*types.UpdatePeoplePayload, error) { - people, err := r.proboSvc.UpdatePeople(ctx, probo.UpdatePeopleRequest{ + svc := r.proboSvc.WithTenant(input.ID.TenantID()) + + people, err := svc.UpdatePeople(ctx, probo.UpdatePeopleRequest{ ID: input.ID, ExpectedVersion: input.ExpectedVersion, FullName: input.FullName, @@ -174,7 +190,9 @@ func (r *mutationResolver) UpdatePeople(ctx context.Context, input types.UpdateP // DeletePeople is the resolver for the deletePeople field. func (r *mutationResolver) DeletePeople(ctx context.Context, input types.DeletePeopleInput) (*types.DeletePeoplePayload, error) { - err := r.proboSvc.DeletePeople(ctx, input.PeopleID) + svc := r.proboSvc.WithTenant(input.PeopleID.TenantID()) + + err := svc.DeletePeople(ctx, input.PeopleID) if err != nil { return nil, fmt.Errorf("cannot delete people: %w", err) } @@ -210,7 +228,9 @@ func (r *mutationResolver) DeleteOrganization(ctx context.Context, input types.D // UpdateTaskState is the resolver for the updateTaskState field. func (r *mutationResolver) UpdateTaskState(ctx context.Context, input types.UpdateTaskStateInput) (*types.UpdateTaskStatePayload, error) { - task, err := r.proboSvc.UpdateTaskState(ctx, probo.UpdateTaskStateRequest{ + svc := r.proboSvc.WithTenant(input.TaskID.TenantID()) + + task, err := svc.UpdateTaskState(ctx, probo.UpdateTaskStateRequest{ TaskID: input.TaskID, State: input.State, Reason: nil, @@ -226,7 +246,9 @@ func (r *mutationResolver) UpdateTaskState(ctx context.Context, input types.Upda // CreateTask is the resolver for the createTask field. func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTaskInput) (*types.CreateTaskPayload, error) { - task, err := r.proboSvc.CreateTask(ctx, probo.CreateTaskRequest{ + svc := r.proboSvc.WithTenant(input.ControlID.TenantID()) + + task, err := svc.CreateTask(ctx, probo.CreateTaskRequest{ ControlID: input.ControlID, Name: input.Name, Description: input.Description, @@ -242,7 +264,9 @@ func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTas // DeleteTask is the resolver for the deleteTask field. func (r *mutationResolver) DeleteTask(ctx context.Context, input types.DeleteTaskInput) (*types.DeleteTaskPayload, error) { - err := r.proboSvc.DeleteTask(ctx, input.TaskID) + svc := r.proboSvc.WithTenant(input.TaskID.TenantID()) + + err := svc.DeleteTask(ctx, input.TaskID) if err != nil { return nil, fmt.Errorf("cannot delete task: %w", err) } @@ -254,7 +278,9 @@ func (r *mutationResolver) DeleteTask(ctx context.Context, input types.DeleteTas // CreateFramework is the resolver for the createFramework field. func (r *mutationResolver) CreateFramework(ctx context.Context, input types.CreateFrameworkInput) (*types.CreateFrameworkPayload, error) { - framework, err := r.proboSvc.CreateFramework(ctx, probo.CreateFrameworkRequest{ + svc := r.proboSvc.WithTenant(input.OrganizationID.TenantID()) + + framework, err := svc.CreateFramework(ctx, probo.CreateFrameworkRequest{ OrganizationID: input.OrganizationID, Name: input.Name, Description: input.Description, @@ -270,7 +296,9 @@ func (r *mutationResolver) CreateFramework(ctx context.Context, input types.Crea // CreateControl is the resolver for the createControl field. func (r *mutationResolver) CreateControl(ctx context.Context, input types.CreateControlInput) (*types.CreateControlPayload, error) { - control, err := r.proboSvc.CreateControl(ctx, probo.CreateControlRequest{ + svc := r.proboSvc.WithTenant(input.FrameworkID.TenantID()) + + control, err := svc.CreateControl(ctx, probo.CreateControlRequest{ FrameworkID: input.FrameworkID, Name: input.Name, Description: input.Description, @@ -287,19 +315,13 @@ func (r *mutationResolver) CreateControl(ctx context.Context, input types.Create // UpdateFramework is the resolver for the updateFramework field. func (r *mutationResolver) UpdateFramework(ctx context.Context, input types.UpdateFrameworkInput) (*types.UpdateFrameworkPayload, error) { - var name, description *string - if input.Name != nil { - name = input.Name - } - if input.Description != nil { - description = input.Description - } + svc := r.proboSvc.WithTenant(input.ID.TenantID()) - framework, err := r.proboSvc.UpdateFramework(ctx, probo.UpdateFrameworkRequest{ + framework, err := svc.UpdateFramework(ctx, probo.UpdateFrameworkRequest{ ID: input.ID, ExpectedVersion: input.ExpectedVersion, - Name: name, - Description: description, + Name: input.Name, + Description: input.Description, }) if err != nil { return nil, fmt.Errorf("cannot update framework: %w", err) @@ -312,29 +334,15 @@ func (r *mutationResolver) UpdateFramework(ctx context.Context, input types.Upda // UpdateControl is the resolver for the updateControl field. func (r *mutationResolver) UpdateControl(ctx context.Context, input types.UpdateControlInput) (*types.UpdateControlPayload, error) { - var name, description, category *string - var state *coredata.ControlState + svc := r.proboSvc.WithTenant(input.ID.TenantID()) - if input.Name != nil { - name = input.Name - } - if input.Description != nil { - description = input.Description - } - if input.Category != nil { - category = input.Category - } - if input.State != nil { - state = input.State - } - - control, err := r.proboSvc.UpdateControl(ctx, probo.UpdateControlRequest{ + control, err := svc.UpdateControl(ctx, probo.UpdateControlRequest{ ID: input.ID, ExpectedVersion: input.ExpectedVersion, - Name: name, - Description: description, - Category: category, - State: state, + Name: input.Name, + Description: input.Description, + Category: input.Category, + State: input.State, }) if err != nil { return nil, fmt.Errorf("cannot update control: %w", err) @@ -347,13 +355,15 @@ func (r *mutationResolver) UpdateControl(ctx context.Context, input types.Update // UploadEvidence is the resolver for the uploadEvidence field. func (r *mutationResolver) UploadEvidence(ctx context.Context, input types.UploadEvidenceInput) (*types.UploadEvidencePayload, error) { + svc := r.proboSvc.WithTenant(input.TaskID.TenantID()) + req := probo.CreateEvidenceRequest{ TaskID: input.TaskID, Name: input.Name, File: input.File.File, } - evidence, err := r.proboSvc.CreateEvidence(ctx, req) + evidence, err := svc.CreateEvidence(ctx, req) if err != nil { return nil, fmt.Errorf("failed to create evidence: %w", err) } @@ -365,7 +375,9 @@ func (r *mutationResolver) UploadEvidence(ctx context.Context, input types.Uploa // DeleteEvidence is the resolver for the deleteEvidence field. func (r *mutationResolver) DeleteEvidence(ctx context.Context, input types.DeleteEvidenceInput) (*types.DeleteEvidencePayload, error) { - err := r.proboSvc.DeleteEvidence(ctx, input.EvidenceID) + svc := r.proboSvc.WithTenant(input.EvidenceID.TenantID()) + + err := svc.DeleteEvidence(ctx, input.EvidenceID) if err != nil { return nil, fmt.Errorf("failed to delete evidence: %w", err) } @@ -377,7 +389,9 @@ func (r *mutationResolver) DeleteEvidence(ctx context.Context, input types.Delet // CreatePolicy is the resolver for the createPolicy field. func (r *mutationResolver) CreatePolicy(ctx context.Context, input types.CreatePolicyInput) (*types.CreatePolicyPayload, error) { - policy, err := r.proboSvc.Policies.Create(ctx, probo.CreatePolicyRequest{ + svc := r.proboSvc.WithTenant(input.OrganizationID.TenantID()) + + policy, err := svc.Policies.Create(ctx, probo.CreatePolicyRequest{ OrganizationID: input.OrganizationID, Name: input.Name, Content: input.Content, @@ -396,7 +410,9 @@ func (r *mutationResolver) CreatePolicy(ctx context.Context, input types.CreateP // UpdatePolicy is the resolver for the updatePolicy field. func (r *mutationResolver) UpdatePolicy(ctx context.Context, input types.UpdatePolicyInput) (*types.UpdatePolicyPayload, error) { - policy, err := r.proboSvc.Policies.Update(ctx, probo.UpdatePolicyRequest{ + svc := r.proboSvc.WithTenant(input.ID.TenantID()) + + policy, err := svc.Policies.Update(ctx, probo.UpdatePolicyRequest{ ID: input.ID, ExpectedVersion: input.ExpectedVersion, Name: input.Name, @@ -416,7 +432,9 @@ func (r *mutationResolver) UpdatePolicy(ctx context.Context, input types.UpdateP // DeletePolicy is the resolver for the deletePolicy field. func (r *mutationResolver) DeletePolicy(ctx context.Context, input types.DeletePolicyInput) (*types.DeletePolicyPayload, error) { - err := r.proboSvc.Policies.Delete(ctx, input.PolicyID) + svc := r.proboSvc.WithTenant(input.PolicyID.TenantID()) + + err := svc.Policies.Delete(ctx, input.PolicyID) if err != nil { return nil, fmt.Errorf("cannot delete policy: %w", err) } @@ -428,9 +446,11 @@ func (r *mutationResolver) DeletePolicy(ctx context.Context, input types.DeleteP // Frameworks is the resolver for the frameworks field. func (r *organizationResolver) Frameworks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.FrameworkConnection, error) { + svc := r.proboSvc.WithTenant(obj.ID.TenantID()) + cursor := types.NewCursor(first, after, last, before) - page, err := r.proboSvc.ListOrganizationFrameworks(ctx, obj.ID, cursor) + page, err := svc.ListOrganizationFrameworks(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list organization frameworks: %w", err) } @@ -440,9 +460,11 @@ func (r *organizationResolver) Frameworks(ctx context.Context, obj *types.Organi // Vendors is the resolver for the vendors field. func (r *organizationResolver) Vendors(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.VendorConnection, error) { + svc := r.proboSvc.WithTenant(obj.ID.TenantID()) + cursor := types.NewCursor(first, after, last, before) - page, err := r.proboSvc.ListOrganizationVendors(ctx, obj.ID, cursor) + page, err := svc.ListOrganizationVendors(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list organization vendors: %w", err) } @@ -452,9 +474,11 @@ func (r *organizationResolver) Vendors(ctx context.Context, obj *types.Organizat // Peoples is the resolver for the peoples field. func (r *organizationResolver) Peoples(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.PeopleConnection, error) { + svc := r.proboSvc.WithTenant(obj.ID.TenantID()) + cursor := types.NewCursor(first, after, last, before) - page, err := r.proboSvc.ListOrganizationPeoples(ctx, obj.ID, cursor) + page, err := svc.ListOrganizationPeoples(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list organization peoples: %w", err) } @@ -464,9 +488,10 @@ func (r *organizationResolver) Peoples(ctx context.Context, obj *types.Organizat // Policies is the resolver for the policies field. func (r *organizationResolver) Policies(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.PolicyConnection, error) { + svc := r.proboSvc.WithTenant(obj.ID.TenantID()) cursor := types.NewCursor(first, after, last, before) - page, err := r.proboSvc.Policies.ListByOrganization(ctx, obj.ID, cursor) + page, err := svc.Policies.ListByOrganization(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list organization policies: %w", err) } @@ -476,13 +501,15 @@ func (r *organizationResolver) Policies(ctx context.Context, obj *types.Organiza // Owner is the resolver for the owner field. func (r *policyResolver) Owner(ctx context.Context, obj *types.Policy) (*types.People, error) { - policy, err := r.proboSvc.Policies.Get(ctx, obj.ID) + svc := r.proboSvc.WithTenant(obj.ID.TenantID()) + + policy, err := svc.Policies.Get(ctx, obj.ID) if err != nil { return nil, fmt.Errorf("cannot get policy: %w", err) } // Get the owner - owner, err := r.proboSvc.GetPeople(ctx, policy.OwnerID) + owner, err := svc.GetPeople(ctx, policy.OwnerID) if err != nil { return nil, fmt.Errorf("cannot get owner: %w", err) } @@ -492,58 +519,60 @@ func (r *policyResolver) Owner(ctx context.Context, obj *types.Policy) (*types.P // Node is the resolver for the node field. func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) { + svc := r.proboSvc.WithTenant(id.TenantID()) + switch id.EntityType() { case coredata.OrganizationEntityType: - organization, err := r.proboSvc.GetOrganization(ctx, id) + organization, err := svc.GetOrganization(ctx, id) if err != nil { return nil, err } return types.NewOrganization(organization), nil case coredata.PeopleEntityType: - people, err := r.proboSvc.GetPeople(ctx, id) + people, err := svc.GetPeople(ctx, id) if err != nil { return nil, err } return types.NewPeople(people), nil case coredata.VendorEntityType: - vendor, err := r.proboSvc.GetVendor(ctx, id) + vendor, err := svc.GetVendor(ctx, id) if err != nil { return nil, err } return types.NewVendor(vendor), nil case coredata.FrameworkEntityType: - framework, err := r.proboSvc.GetFramework(ctx, id) + framework, err := svc.GetFramework(ctx, id) if err != nil { return nil, err } return types.NewFramework(framework), nil case coredata.ControlEntityType: - control, err := r.proboSvc.GetControl(ctx, id) + control, err := svc.GetControl(ctx, id) if err != nil { return nil, err } return types.NewControl(control), nil case coredata.TaskEntityType: - task, err := r.proboSvc.GetTask(ctx, id) + task, err := svc.GetTask(ctx, id) if err != nil { return nil, err } return types.NewTask(task), nil case coredata.EvidenceEntityType: - evidence, err := r.proboSvc.GetEvidence(ctx, id) + evidence, err := svc.GetEvidence(ctx, id) if err != nil { return nil, err } return types.NewEvidence(evidence), nil case coredata.PolicyEntityType: - policy, err := r.proboSvc.Policies.Get(ctx, id) + policy, err := svc.Policies.Get(ctx, id) if err != nil { return nil, err } @@ -562,9 +591,10 @@ func (r *queryResolver) Viewer(ctx context.Context) (*types.User, error) { // StateTransisions is the resolver for the stateTransisions field. func (r *taskResolver) StateTransisions(ctx context.Context, obj *types.Task, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.TaskStateTransitionConnection, error) { + svc := r.proboSvc.WithTenant(obj.ID.TenantID()) cursor := types.NewCursor(first, after, last, before) - page, err := r.proboSvc.ListTaskStateTransitions(ctx, obj.ID, cursor) + page, err := svc.ListTaskStateTransitions(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list control tasks: %w", err) } @@ -574,9 +604,10 @@ func (r *taskResolver) StateTransisions(ctx context.Context, obj *types.Task, fi // Evidences is the resolver for the evidences field. func (r *taskResolver) Evidences(ctx context.Context, obj *types.Task, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.EvidenceConnection, error) { + svc := r.proboSvc.WithTenant(obj.ID.TenantID()) cursor := types.NewCursor(first, after, last, before) - page, err := r.proboSvc.ListTaskEvidences(ctx, obj.ID, cursor) + page, err := svc.ListTaskEvidences(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list organization frameworks: %w", err) } diff --git a/pkg/usrmgr/coredata/migrations/20150310T133000Z.sql b/pkg/usrmgr/coredata/migrations/20150310T133000Z.sql new file mode 100644 index 000000000..428b3f958 --- /dev/null +++ b/pkg/usrmgr/coredata/migrations/20150310T133000Z.sql @@ -0,0 +1 @@ +ALTER TABLE usrmgr_users DROP COLUMN organization_id; \ No newline at end of file diff --git a/pkg/usrmgr/coredata/user.go b/pkg/usrmgr/coredata/user.go index f3d7abec0..53b1662da 100644 --- a/pkg/usrmgr/coredata/user.go +++ b/pkg/usrmgr/coredata/user.go @@ -31,7 +31,6 @@ type ( EmailAddress string `db:"email_address"` HashedPassword []byte `db:"hashed_password"` FullName string `db:"fullname"` - OrganizationID gid.GID `db:"organization_id"` CreatedAt time.Time `db:"created_at"` UpdatedAt time.Time `db:"updated_at"` } @@ -52,7 +51,6 @@ SELECT email_address, hashed_password, fullname, - organization_id, created_at, updated_at FROM @@ -90,7 +88,6 @@ SELECT email_address, hashed_password, fullname, - organization_id, created_at, updated_at FROM @@ -123,13 +120,12 @@ func (u *User) Insert( ) error { q := ` INSERT INTO - usrmgr_users (id, email_address, hashed_password, fullname, organization_id, created_at, updated_at) + usrmgr_users (id, email_address, hashed_password, fullname, created_at, updated_at) VALUES ( @user_id, @email_address, @hashed_password, @fullname, - @organization_id, @created_at, @updated_at ) @@ -140,7 +136,6 @@ VALUES ( "email_address": u.EmailAddress, "hashed_password": u.HashedPassword, "fullname": u.FullName, - "organization_id": "AZSfP_xAcAC5IAAAAAAltA", "created_at": u.CreatedAt, "updated_at": u.UpdatedAt, } diff --git a/pkg/usrmgr/usrmgr.go b/pkg/usrmgr/usrmgr.go index 58b059c7c..569ac2dbe 100644 --- a/pkg/usrmgr/usrmgr.go +++ b/pkg/usrmgr/usrmgr.go @@ -109,7 +109,7 @@ func (s Service) RegisterUser( now := time.Now() user := &coredata.User{ - ID: gid.New(), + ID: gid.New(gid.NilTenant, 0), EmailAddress: params.Email, HashedPassword: hashedPassword, FullName: params.FullName, @@ -151,8 +151,8 @@ func (s Service) Login( now := time.Now() user := &coredata.User{} session := &coredata.Session{ - ID: gid.New(), - UserID: gid.GID{}, // Will be set after user is loaded + ID: gid.New(gid.NilTenant, 0), + UserID: gid.Nil, ExpiredAt: now.Add(24 * time.Hour), CreatedAt: now, UpdatedAt: now, @@ -310,19 +310,6 @@ func (s Service) GetUserBySession( return s.GetUserByID(ctx, session.UserID) } -// GetUserOrganization gets the organization ID for a user -func (s Service) GetUserOrganization( - ctx context.Context, - userID gid.GID, -) (gid.GID, error) { - user, err := s.GetUserByID(ctx, userID) - if err != nil { - return gid.GID{}, err - } - - return user.OrganizationID, nil -} - // GetUserOrganizations gets all organizations for a user func (s Service) GetUserOrganizations( ctx context.Context,