diff options
| author | mo khan <mo@mokhan.ca> | 2025-05-23 17:26:45 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-05-23 17:26:45 -0600 |
| commit | 2bb5b3ce0e618ab652159b986df252990f3d2f12 (patch) | |
| tree | abecef9153bce0d0787346cb04e373dc107689ad | |
| parent | 15b1699b0ad27c9fc3aa4498d4085b7338bec95a (diff) | |
feat: delegate to the remote authzd to check if the permission is granted
30 files changed, 3354 insertions, 10 deletions
@@ -14,6 +14,7 @@ require ( github.com/stretchr/testify v1.10.0 github.com/testcontainers/testcontainers-go v0.37.0 github.com/xlgmokha/x v0.0.0-20250523153843-ded39aa54bc5 + gitlab.com/gitlab-org/software-supply-chain-security/authorization/authzd.git v0.0.0-20250523230007-aebf1323e470 golang.org/x/oauth2 v0.30.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 google.golang.org/grpc v1.72.1 @@ -35,7 +36,7 @@ require ( github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect github.com/cpuguy83/dockercfg v0.3.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deckarep/golang-set/v2 v2.8.0 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/go-units v0.5.0 // indirect @@ -78,7 +79,7 @@ require ( github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect github.com/shirou/gopsutil/v4 v4.25.4 // indirect @@ -86,6 +87,7 @@ require ( github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect github.com/tklauser/numcpus v0.10.0 // indirect + github.com/twitchtv/twirp v8.1.3+incompatible // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/zeebo/errs v1.4.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect @@ -34,8 +34,9 @@ github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHf github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ= github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= @@ -153,8 +154,9 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25 h1:S1hI5J github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25/go.mod h1:ZQntvDG8TkPgljxtA0R9frDoND4QORU1VXz015N5Ks4= github.com/playwright-community/playwright-go v0.5200.0 h1:z/5LGuX2tBrg3ug1HupMXLjIG93f1d2MWdDsNhkMQ9c= github.com/playwright-community/playwright-go v0.5200.0/go.mod h1:UnnyQZaqUOO5ywAZu60+N4EiWReUqX1MQBBA3Oofvf8= -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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= @@ -183,6 +185,8 @@ github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8O github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= +github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU= +github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= github.com/xlgmokha/x v0.0.0-20250523153843-ded39aa54bc5 h1:GtohREIY3aFl1+emxYz6iRihfoobcauTXGXKcU2U+rc= github.com/xlgmokha/x v0.0.0-20250523153843-ded39aa54bc5/go.mod h1:zhU3cB9VYGt4IlTIaLVw3wsXIS5UzXiYmCB+q+DjfHs= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -192,6 +196,8 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= +gitlab.com/gitlab-org/software-supply-chain-security/authorization/authzd.git v0.0.0-20250523230007-aebf1323e470 h1:NaTcIgKhqQf8BZ7JO9pLU80RDer04BO3cxVq54OhbfE= +gitlab.com/gitlab-org/software-supply-chain-security/authorization/authzd.git v0.0.0-20250523230007-aebf1323e470/go.mod h1:yb1ydXPo0WdNzp7+q37wiVwUuzeCZTQeG7utFjBdmnk= 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/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= diff --git a/pkg/authz/check_service.go b/pkg/authz/check_service.go index 3c4426a..0d5567a 100644 --- a/pkg/authz/check_service.go +++ b/pkg/authz/check_service.go @@ -10,6 +10,7 @@ import ( types "github.com/envoyproxy/go-control-plane/envoy/type/v3" "github.com/xlgmokha/x/pkg/log" "github.com/xlgmokha/x/pkg/x" + "gitlab.com/gitlab-org/software-supply-chain-security/authorization/authzd.git/pkg/rpc" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/pls" status "google.golang.org/genproto/googleapis/rpc/status" "google.golang.org/grpc/codes" @@ -17,6 +18,13 @@ import ( type CheckService struct { auth.UnimplementedAuthorizationServer + ability rpc.Ability +} + +func NewCheckService(ability rpc.Ability) *CheckService { + return &CheckService{ + ability: ability, + } } func (svc *CheckService) Check(ctx context.Context, request *auth.CheckRequest) (*auth.CheckResponse, error) { @@ -56,7 +64,7 @@ func (svc *CheckService) isAllowed(ctx context.Context, r *auth.CheckRequest) bo } log.WithFields(ctx, svc.fieldsFor(r)) - return svc.isLoggedIn(ctx, r) || svc.isPublic(ctx, r) + return svc.isPublic(ctx, r) || svc.isLoggedIn(ctx, r) } func (svc *CheckService) validRequest(ctx context.Context, r *auth.CheckRequest) bool { @@ -90,7 +98,20 @@ func (svc *CheckService) isLoggedIn(ctx context.Context, r *auth.CheckRequest) b pls.LogError(ctx, err) return false } - return x.IsPresent(idToken) + if x.IsZero(idToken) { + return false + } + + reply, err := svc.ability.Allowed(ctx, &rpc.AllowRequest{ + Subject: idToken.Subject, + Permission: r.Attributes.Request.Http.Method, + Resource: r.Attributes.Request.Http.Path, + }) + if err != nil { + pls.LogError(ctx, err) + return false + } + return reply.Result } return false } diff --git a/pkg/authz/id_token.go b/pkg/authz/id_token.go index b647161..ccc96de 100644 --- a/pkg/authz/id_token.go +++ b/pkg/authz/id_token.go @@ -9,7 +9,7 @@ import ( ) type IDToken struct { - Audience []string `json:"aud"` + // Audience []string `json:"aud"` Email string `json:"email"` EmailVerified bool `json:"email_verified"` ExpiredAt int64 `json:"exp"` diff --git a/pkg/authz/server.go b/pkg/authz/server.go index 49bcd3d..b890387 100644 --- a/pkg/authz/server.go +++ b/pkg/authz/server.go @@ -2,11 +2,15 @@ package authz import ( "context" + "net/http" auth "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" + "github.com/xlgmokha/x/pkg/env" "github.com/xlgmokha/x/pkg/log" "github.com/xlgmokha/x/pkg/x" + "gitlab.com/gitlab-org/software-supply-chain-security/authorization/authzd.git/pkg/rpc" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/pls" + "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/web" "google.golang.org/grpc" "google.golang.org/grpc/reflection" ) @@ -17,12 +21,16 @@ type Server struct { func New(ctx context.Context, options ...grpc.ServerOption) *Server { logger := log.From(ctx) + server := grpc.NewServer(x.Prepend( options, grpc.UnaryInterceptor(pls.LogGRPC(logger)), grpc.StreamInterceptor(pls.LogGRPCStream(logger)), )...) - auth.RegisterAuthorizationServer(server, &CheckService{}) + auth.RegisterAuthorizationServer(server, NewCheckService(rpc.NewAbilityProtobufClient( + env.Fetch("AUTHZD_HOST", "https://authzd.staging.runway.gitlab.net"), + &http.Client{Transport: &web.Transport{Logger: logger}}, + ))) reflection.Register(server) return &Server{ diff --git a/vendor/github.com/twitchtv/twirp/.gitignore b/vendor/github.com/twitchtv/twirp/.gitignore new file mode 100644 index 0000000..6865b76 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/.gitignore @@ -0,0 +1,6 @@ +*.test +npm-debug.log + +/bin +/release +/build diff --git a/vendor/github.com/twitchtv/twirp/.travis.yml b/vendor/github.com/twitchtv/twirp/.travis.yml new file mode 100644 index 0000000..718489e --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/.travis.yml @@ -0,0 +1,10 @@ +sudo: false +language: go +go: +- 1.13.x +- 1.14.x +- 1.15.x +- tip +script: +- go install ./... +- go test -race ./... diff --git a/vendor/github.com/twitchtv/twirp/CONTRIBUTING.md b/vendor/github.com/twitchtv/twirp/CONTRIBUTING.md new file mode 100644 index 0000000..e80854b --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/CONTRIBUTING.md @@ -0,0 +1,92 @@ +# Contributing + +Thanks for helping make Twirp better! This is great! + +## Twirp Design Principles + +Contributions to Twirp should align with the project’s design principles: + +- Maintain backwards compatibility. Twirp has been in production at Twitch since 2016 and released to the public in January 2018. It is currently used by many companies and individuals with a variety of needs. There must be a compelling use-case and solid reasoning behind a major version upgrade. +- Simple wire protocol and minimal public API. Fewer things in the core means fewer things to break. In addition, it ensures lower friction updates and easier to maintain implementations in other languages. +- Avoid surprising behavior. For instance, mechanisms that can alter a program’s control flow in a surprising way (such as middleware or observability hooks) should be treated with caution. +- Prefer pragmatism over bleeding-edge. Users should be able to deploy and accept updates to Twirp even if they are conservative on updating its dependencies. This includes Go, the protobuf compiler and runtime libraries, and the HTTP protocol. +- Keep configuration to a minimum. For example: avoid adding flags to code generation commands, so that generated code is predictable across versions and platforms. +- Limit dependencies where possible, so that they are easier to integrate and upgrade. +- Prefer generated code over shared libraries between services and clients, so that it is easier to implement changes without forcing a lock-step upgrade across the ecosystem. + +Examples of contributions that should be addressed with high priority: + +- Security updates. +- Performance improvements. +- Supporting new versions of key dependencies such as Go and Protobuf. +- Documentation. +- Making Twirp easier to integrate with other tools. + +## Report an Issue + +If you have run into a bug or want to discuss a new feature, please [file an issue](https://github.com/twitchtv/twirp/issues). If you'd rather not publicly discuss the issue, please email security@twitch.tv. + +## Contributing Code with Pull Requests + +Twirp uses github pull requests. Fork, hack away at your changes and submit. Most pull requests will go through a few iterations before they get merged. Different contributors will sometimes have different opinions, and often patches will need to be revised before they can get merged. + +### Requirements + +- Add tests that cover your contribution. Overall code coverage should not decrease. +- Twirp officially supports the last 3 releases of Go. +- Protobuf version 3.x.x to generate code with the protoc command. +- For linters and other tools, we use [retool](https://github.com/twitchtv/retool). If `make setup` is not able to install it, you can install it in your path with `go get github.com/twitchtv/retool` and then install tools with `retool build`. + +### Running tests + +Generally you want to make changes and run `make`, which will install all +dependencies we know about, build the core, and run tests. A few notes: + +- Clone the repo on `$GOPATH/src/github.com/twitchtv/twirp` (go modules not supported yet). +- Run Go unit tests with `make test`. +- Most tests of the Go server are in `internal/twirptest/service_test.go`. +- Integration tests running the full stack in Go are in the [clientcompat](./clientcompat) directory. + +## Contributing Documentation + +Twirp's docs are generated with [Docusaurus](https://docusaurus.io/). You can safely edit anything inside the [docs](./docs) directory, adding new pages or editing them. You can edit the sidebar by editing [website/sidebars.json](./website/sidebars.json). + +To render and review your changes, run docusaurus's local server. See [Install docusaurus on your machine](https://docusaurus.io/docs/en/installation.html). + +1. `cd website` +2. `npm install` +3. `npm start` +4. Navigate to http://localhost:3000/twirp to see how it looks. + +Publish the new docs on the `gh-pages` branch. See [this guide](https://docusaurus.io/docs/en/tutorial-publish-site) for details. + +``` +GIT_USER=<your-github-username> CURRENT_BRANCH=gh-pages USE_SSH=true npm run publish-gh-pages +``` + +## Making a New Release + +Releasing versions is the responsibility of the core maintainers. Most people +can skip this section. + +Twirp uses Github releases. To make a new release: + +1. Merge all changes that should be included in the release into the main branch. +2. Update the version constant in `internal/gen/version.go`. Please respect [semantic versioning](http://semver.org/): `v<major>.<minor>.<patch>`. +3. Run `make test_all` to re-generate code and run tests. Check that generated test files include the new version in the header comment. +4. Add a new commit to main with a message like "Version vX.X.X release" and push. +5. Tag the commit you just made: `git tag vX.X.X` and `git push origin --tags`. +6. Go to Github https://github.com/twitchtv/twirp/releases and "Draft a new release". +7. Make sure that all new functionality is properly documented, on code comments, PR description, and include links and/or upgrade instructions on the release. For example the [v7 release](https://github.com/twitchtv/twirp/releases/tag/v7.0.0). Minor releases can just include a link to the PR/PRs that were merged included into the release. + +## Code of Conduct + +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +opensource-codeofconduct@amazon.com with any additional questions or comments. + +## Licensing + +See the [LICENSE](https://github.com/twitchtv/twirp/blob/main/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. + +We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. diff --git a/vendor/github.com/twitchtv/twirp/Gopkg.lock b/vendor/github.com/twitchtv/twirp/Gopkg.lock new file mode 100644 index 0000000..90c1382 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/Gopkg.lock @@ -0,0 +1,96 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" + name = "github.com/davecgh/go-spew" + packages = ["spew"] + pruneopts = "NUT" + revision = "346938d642f2ec3594ed81d874461961cd0faa76" + version = "v1.1.0" + +[[projects]] + digest = "1:5cf3f025cbee5951a4ee961de067c8a89fc95a5adabead774f82822efabab121" + name = "github.com/pkg/errors" + packages = ["."] + pruneopts = "NUT" + revision = "645ef00459ed84a119197bfb8d8205042c6df63d" + version = "v0.8.0" + +[[projects]] + digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + pruneopts = "NUT" + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + +[[projects]] + digest = "1:be7d615463b5c9c5fd732a3cc028038e8643543def6e55344b52a3a4dbb667a3" + name = "github.com/stretchr/testify" + packages = [ + "assert", + "require", + ] + pruneopts = "NUT" + revision = "b91bfb9ebec76498946beb6af7c0230c7cc7ba6c" + version = "v1.2.0" + +[[projects]] + digest = "1:acaa96db5d83347d36134c23782eda554b73fb1ac5915bbcb39c67d9a168db00" + name = "google.golang.org/protobuf" + packages = [ + "encoding/protojson", + "encoding/prototext", + "encoding/protowire", + "internal/descfmt", + "internal/descopts", + "internal/detrand", + "internal/encoding/defval", + "internal/encoding/json", + "internal/encoding/messageset", + "internal/encoding/tag", + "internal/encoding/text", + "internal/errors", + "internal/filedesc", + "internal/filetype", + "internal/flags", + "internal/genid", + "internal/impl", + "internal/order", + "internal/pragma", + "internal/set", + "internal/strs", + "internal/version", + "proto", + "reflect/protoreflect", + "reflect/protoregistry", + "runtime/protoiface", + "runtime/protoimpl", + "types/descriptorpb", + "types/known/emptypb", + "types/known/wrapperspb", + "types/pluginpb", + ] + pruneopts = "NUT" + revision = "f2d1f6cbe10b90d22296ea09a7217081c2798009" + version = "v1.26.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [ + "github.com/pkg/errors", + "github.com/stretchr/testify/assert", + "github.com/stretchr/testify/require", + "google.golang.org/protobuf/encoding/protojson", + "google.golang.org/protobuf/proto", + "google.golang.org/protobuf/reflect/protoreflect", + "google.golang.org/protobuf/runtime/protoimpl", + "google.golang.org/protobuf/types/descriptorpb", + "google.golang.org/protobuf/types/known/emptypb", + "google.golang.org/protobuf/types/known/wrapperspb", + "google.golang.org/protobuf/types/pluginpb", + ] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/vendor/github.com/twitchtv/twirp/Gopkg.toml b/vendor/github.com/twitchtv/twirp/Gopkg.toml new file mode 100644 index 0000000..0e6be4a --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/Gopkg.toml @@ -0,0 +1,25 @@ +# The importable parts of Twirp have no external dependencies. They just use the +# standard library. But both protoc-gen-twirp and Twirp's tests *do* have some +# dependencies, and this manifest lists them. +# +# All dependencies are specified as overrides, not constraints, so that dep +# doesn't get confused if someone imports Twirp. We don't want to falsely +# require any particular versions of these libraries - they are *only* for +# tests and building main packages. + +[[override]] + name = "google.golang.org/protobuf" + version = "1.26.0" + +[[override]] + name = "github.com/pkg/errors" + version = "0.8.0" + +[[override]] + name = "github.com/stretchr/testify" + version = "1.2.0" + +[prune] + unused-packages = true + go-tests = true + non-go = true diff --git a/vendor/github.com/twitchtv/twirp/LICENSE b/vendor/github.com/twitchtv/twirp/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/twitchtv/twirp/Makefile b/vendor/github.com/twitchtv/twirp/Makefile new file mode 100644 index 0000000..e3e6b4b --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/Makefile @@ -0,0 +1,28 @@ +PATH := ${PWD}/_tools/bin:${PWD}/bin:${PATH} +export GO111MODULE=off + +all: setup test_all + +.PHONY: setup generate test_all test test_clientcompat + +setup: + ./check_protoc_version.sh + GOPATH="$$PWD/_tools" GOBIN="$$PWD/_tools/bin" go get github.com/twitchtv/retool + ./_tools/bin/retool build + +generate: + # Recompile and install generator + GOBIN="$$PWD/bin" go install -v ./protoc-gen-twirp + # Generate code from go:generate comments + go generate ./... + +test_all: setup test test_clientcompat + +test: generate + ./_tools/bin/errcheck ./internal/twirptest + go test -race ./... + +test_clientcompat: generate + GOBIN="$$PWD/bin" go install ./clientcompat + GOBIN="$$PWD/bin" go install ./clientcompat/gocompat + ./bin/clientcompat -client ./bin/gocompat diff --git a/vendor/github.com/twitchtv/twirp/NOTICE b/vendor/github.com/twitchtv/twirp/NOTICE new file mode 100644 index 0000000..de04251 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/NOTICE @@ -0,0 +1,2 @@ +Twirp +Copyright 2018 Twitch Interactive, Inc. All Rights Reserved. diff --git a/vendor/github.com/twitchtv/twirp/PROTOCOL.md b/vendor/github.com/twitchtv/twirp/PROTOCOL.md new file mode 100644 index 0000000..8398c96 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/PROTOCOL.md @@ -0,0 +1,215 @@ +# Twirp Wire Protocol + +This document defines the Twirp wire protocol over HTTP. The +current protocol version is v7. + +## Overview + +The Twirp wire protocol is a simple RPC protocol based on HTTP and +Protocol Buffers (proto). The protocol uses HTTP URLs to specify the +RPC endpoints, and sends/receives proto messages as HTTP +request/response bodies. + +To use Twirp, developers first define their APIs using proto files, +then use Twirp tools to generate the client and the server libraries. +The generated libraries implement the Twirp wire protocol, using the +standard HTTP library provided by the programming language runtime or +the operating system. Once the client and the server are implemented, +the client can communicate with the server by making RPC calls. + +The Twirp wire protocol supports both binary and JSON encodings of +proto messages, and works with any HTTP client and any HTTP version. + +### URLs + +In [ABNF syntax](https://tools.ietf.org/html/rfc5234), Twirp's URLs +have the following format: + +```abnf +URL ::= Base-URL [ Prefix ] "/" [ Package "." ] Service "/" Method +``` + +The Twirp wire protocol uses HTTP URLs to specify the RPC +endpoints on the server for sending the requests. Such direct mapping +makes the request routing simple and efficient. The Twirp URLs have +the following components. + +* **Base-URL** is the virtual location of a Twirp API server, which is + typically published via API documentation or service discovery. + Currently, it should only contain URL `scheme` and `authority`. For + example, "https://example.com". +* **Prefix** is usually "/twirp", but it could be empty "", or an + arbitrary path like "/my/custom/prefix". +* **Package** is the proto `package` name for an API, which is often + considered as an API version. For example, + `example.calendar.v1`. This component is omitted if the API + definition doesn't have a package name. +* **Service** is the proto `service` name for an API. For example, + `CalendarService`. +* **Method** is the proto `rpc` name for an API method. For example, + `CreateEvent`. + +### Requests + +Twirp always uses HTTP POST method to send requests, because it +closely matches the semantics of RPC methods. + +The **Request-Headers** are normal HTTP headers. The Twirp wire +protocol uses the following headers. + +* **Content-Type** header indicates the proto message encoding, which + should be one of "application/protobuf", "application/json". The + server uses this value to decide how to parse the request body, + and encode the response body. + +The **Request-Body** is the encoded request message, contained in the +HTTP request body. The encoding is specified by the `Content-Type` +header. + +### Responses + +The **Response-Headers** are just normal HTTP response headers. The +Twirp wire protocol uses the following headers. + +* **Content-Type** The value should be either "application/protobuf" + or "application/json" to indicate the encoding of the response + message. It must match the "Content-Type" header in the request. + +The **Request-Body** is the encoded response message contained in the +HTTP response body. The encoding is specified by the `Content-Type` +header. + +### Example + +The following example shows a simple Echo API definition and its +corresponding wire payloads. + +The example assumes the server base URL is "https://example.com". + +```proto +syntax = "proto3"; + +package example.echoer; + +service Echo { + rpc Hello(HelloRequest) returns (HelloResponse); +} + +message HelloRequest { + string message; +} + +message HelloResponse { + string message; +} +``` + +**Proto Request** + +``` +POST /twirp/example.echoer.Echo/Hello HTTP/1.1 +Host: example.com +Content-Type: application/protobuf +Content-Length: 15 + +<encoded HelloRequest> +``` + +**JSON Request** + +``` +POST /twirp/example.echoer.Echo/Hello HTTP/1.1 +Host: example.com +Content-Type: application/json +Content-Length: 27 + +{"message":"Hello, World!"} +``` + +**Proto Response** + +``` +HTTP/1.1 200 OK +Content-Type: application/protobuf +Content-Length: 15 + +<encoded HelloResponse> +``` + +**JSON Response** + +``` +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 27 + +{"message":"Hello, World!"} +``` + + +## Errors + +Twirp error responses are always JSON-encoded, regardless of +the request's Content-Type, with a corresponding +`Content-Type: application/json` header. This ensures that +the errors are human-readable in any setting. + +Twirp errors are a JSON object with the keys: + +* **code**: One of the Twirp error codes as a string. +* **msg**: A human-readable message describing the error + as a string. +* **meta**: (optional) An object with string values holding + arbitrary additional metadata describing the error. + +Example: + +```json +{ + "code": "internal", + "msg": "Something went wrong" +} +``` + +Example with metadata: + +```json +{ + "code": "permission_denied", + "msg": "Thou shall not pass", + "meta": { + "target": "Balrog", + "power": "999" + } +} +``` + +### Error Codes + +Twirp errors always include an error code. This code is represented +as a string and must be one of a fixed set of codes, listed in the +table below. Each code has an associated HTTP Status Code. When a +server responds with the given error code, it must set the +corresponding HTTP Status Code for the response. + +| Twirp Error Code | HTTP Status | Description +| ------------------- | ----------- | ----------- +| canceled | 408 | The operation was cancelled. +| unknown | 500 | An unknown error occurred. For example, this can be used when handling errors raised by APIs that do not return any error information. +| invalid_argument | 400 | The client specified an invalid argument. This indicates arguments that are invalid regardless of the state of the system (i.e. a malformed file name, required argument, number out of range, etc.). +| malformed | 400 | The client sent a message which could not be decoded. This may mean that the message was encoded improperly or that the client and server have incompatible message definitions. +| deadline_exceeded | 408 | Operation expired before completion. For operations that change the state of the system, this error may be returned even if the operation has completed successfully (timeout). +| not_found | 404 | Some requested entity was not found. +| bad_route | 404 | The requested URL path wasn't routable to a Twirp service and method. This is returned by generated server code and should not be returned by application code (use "not_found" or "unimplemented" instead). +| already_exists | 409 | An attempt to create an entity failed because one already exists. +| permission_denied | 403 | The caller does not have permission to execute the specified operation. It must not be used if the caller cannot be identified (use "unauthenticated" instead). +| unauthenticated | 401 | The request does not have valid authentication credentials for the operation. +| resource_exhausted | 429 | Some resource has been exhausted or rate-limited, perhaps a per-user quota, or perhaps the entire file system is out of space. +| failed_precondition | 412 | The operation was rejected because the system is not in a state required for the operation's execution. For example, doing an rmdir operation on a directory that is non-empty, or on a non-directory object, or when having conflicting read-modify-write on the same resource. +| aborted | 409 | The operation was aborted, typically due to a concurrency issue like sequencer check failures, transaction aborts, etc. +| out_of_range | 400 | The operation was attempted past the valid range. For example, seeking or reading past end of a paginated collection. Unlike "invalid_argument", this error indicates a problem that may be fixed if the system state changes (i.e. adding more items to the collection). There is a fair bit of overlap between "failed_precondition" and "out_of_range". We recommend using "out_of_range" (the more specific error) when it applies so that callers who are iterating through a space can easily look for an "out_of_range" error to detect when they are done. +| unimplemented | 501 | The operation is not implemented or not supported/enabled in this service. +| internal | 500 | When some invariants expected by the underlying system have been broken. In other words, something bad happened in the library or backend service. Twirp specific issues like wire and serialization problems are also reported as "internal" errors. +| unavailable | 503 | The service is currently unavailable. This is most likely a transient condition and may be corrected by retrying with a backoff. +| data_loss | 500 | The operation resulted in unrecoverable data loss or corruption. + diff --git a/vendor/github.com/twitchtv/twirp/README.md b/vendor/github.com/twitchtv/twirp/README.md new file mode 100644 index 0000000..9400995 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/README.md @@ -0,0 +1,74 @@ + [](https://travis-ci.org/twitchtv/twirp) [](https://goreportcard.com/report/github.com/twitchtv/twirp) [](https://godoc.org/github.com/twitchtv/twirp) + +--- + +Twirp is a framework for service-to-service communication emphasizing simplicity +and minimalism. It generates routing and serialization from API definition files +and lets you focus on your application's logic instead of thinking about +folderol like HTTP methods and paths and JSON. + +Twirp is similar to [gRPC](http://www.grpc.io/), but without the custom +HTTP server and transport implementations: it runs on the standard library's +extremely-well-tested-and-high-performance `net/http` Server. It can run on HTTP +1.1, not just http/2, and supports JSON serialization for easy debugging. + +Along the way, you get autogenerated clients and a simple, smart framework for +passing error messages. Nice! + +Read more about the motivation behind on the [announcement blog post](https://blog.twitch.tv/en/2018/01/16/twirp-a-sweet-new-rpc-framework-for-go-5f2febbf35f/). + +### Documentation + + * [Getting Started](https://twitchtv.github.io/twirp/docs/intro.html) + * [Usage Example](https://twitchtv.github.io/twirp/docs/example.html) + * [Errors](https://twitchtv.github.io/twirp/docs/errors.html) + * More: https://twitchtv.github.io/twirp/ + +### Implementations in other languages + +This repo contains the generator and runtime library for the Go implementation. + +Here is a list of some third-party implementations in other languages. + +| Language | Clients | Servers | Repository | +|----------------|---------|---------|------------| +| **Python3** | ✓ | ✓ | [github.com/verloop/twirpy](https://github.com/verloop/twirpy) +| **Java** | ✓ | ✓ | [github.com/fajran/protoc-gen-twirp_java_jaxrs](https://github.com/fajran/protoc-gen-twirp_java_jaxrs) +| **Java** | | ✓ | [github.com/devork/flit](https://github.com/devork/flit) +| **JavaScript** | ✓ | | [github.com/thechriswalker/protoc-gen-twirp_js](https://github.com/thechriswalker/protoc-gen-twirp_js) +| **JavaScript** | ✓ | | [github.com/Xe/twirp-codegens/cmd/protoc-gen-twirp_jsbrowser](https://github.com/Xe/twirp-codegens) +| **JavaScript** | ✓ | ✓ | [github.com/tatethurston/TwirpScript](https://github.com/tatethurston/TwirpScript) +| **Typescript** | ✓ | ✓ | [github.com/hopin-team/twirp-ts](https://github.com/hopin-team/twirp-ts) +| **Typescript** | ✓ | | [github.com/larrymyers/protoc-gen-twirp_typescript](https://github.com/larrymyers/protoc-gen-twirp_typescript) +| **Typescript** | ✓ | ✓ | [github.com/tatethurston/TwirpScript](https://github.com/tatethurston/TwirpScript) +| **Typescript** | ✓ | ✓ | [github.com/timostamm/protobuf-ts](https://github.com/timostamm/protobuf-ts) +| **Ruby** | ✓ | ✓ | [github.com/twitchtv/twirp-ruby](https://github.com/twitchtv/twirp-ruby) +| **Rust** | ✓ | ✓ | [github.com/cretz/prost-twirp](https://github.com/cretz/prost-twirp) +| **Scala** | ✓ | ✓ | [github.com/soundcloud/twinagle](https://github.com/soundcloud/twinagle) +| **Swagger** | ✓ | ✓ | [github.com/go-bridget/twirp-swagger-gen](https://github.com/go-bridget/twirp-swagger-gen) +| **PHP** | ✓ | ✓ | [github.com/twirphp/twirp](https://github.com/twirphp/twirp) +| **Dart** | ✓ | | [github.com/apptreesoftware/protoc-gen-twirp_dart](https://github.com/apptreesoftware/protoc-gen-twirp_dart) +| **Elixir** | ✓ | ✓ | [github.com/keathley/twirp-elixir](https://github.com/keathley/twirp-elixir) +| **Swift** | ✓ | | [github.com/CrazyHulk/protoc-gen-swiftwirp](https://github.com/CrazyHulk/protoc-gen-swiftwirp) +| **Crystal** | ✓ | ✓ | [github.com/mloughran/twirp.cr](https://github.com/mloughran/twirp.cr) + + +### Support and Community + +We have a channel on the Gophers slack, [#twirp](https://gophers.slack.com/messages/twirp), +which is the best place to get quick answers to your questions. You can join the +Gopher slack [here](https://invite.slack.golangbridge.org/). + +### Releases + +Twirp follows semantic versioning through git tags, and uses Github Releases for +release notes and upgrade guides: +[Twirp Releases](https://github.com/twitchtv/twirp/releases) + +### Contributing + +Check out [CONTRIBUTING.md](./CONTRIBUTING.md) for notes on making contributions. + +### License + +This library is licensed under the Apache 2.0 License. diff --git a/vendor/github.com/twitchtv/twirp/THIRD_PARTY b/vendor/github.com/twitchtv/twirp/THIRD_PARTY new file mode 100644 index 0000000..1c4588d --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/THIRD_PARTY @@ -0,0 +1,29 @@ +** Protobuf -- https://github.com/protocolbuffers/protobuf-go +Copyright 2010 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/vendor/github.com/twitchtv/twirp/check_protoc_version.sh b/vendor/github.com/twitchtv/twirp/check_protoc_version.sh new file mode 100644 index 0000000..a6df258 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/check_protoc_version.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# Copyright 2018 Twitch Interactive, Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You may not +# use this file except in compliance with the License. A copy of the License is +# located at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# or in the "license" file accompanying this file. This file is distributed on +# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +which protoc +PROTOC_EXISTS=$? +if [ $PROTOC_EXISTS -eq 0 ]; then + PROTOC_VERSION=`protoc --version` + if [[ $PROTOC_VERSION == "libprotoc 3."* ]]; then + echo "protoc version: $PROTOC_VERSION" + exit 0 + fi + echo "required protoc v3, but found: $PROTOC_VERSION" + exit 1 +fi +echo "Please install protoc v3. See https://grpc.io/docs/protoc-installation/, for example in MacOS: brew install protobuf" +exit 1 diff --git a/vendor/github.com/twitchtv/twirp/client_options.go b/vendor/github.com/twitchtv/twirp/client_options.go new file mode 100644 index 0000000..36d57ac --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/client_options.go @@ -0,0 +1,196 @@ +// Copyright 2018 Twitch Interactive, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may not +// use this file except in compliance with the License. A copy of the License is +// located at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// or in the "license" file accompanying this file. This file is distributed on +// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. +package twirp + +import ( + "context" + "net/http" + "reflect" +) + +// ClientOption is a functional option for extending a Twirp client. +type ClientOption func(*ClientOptions) + +// WithClientHooks defines the hooks for a Twirp client. +func WithClientHooks(hooks *ClientHooks) ClientOption { + return func(opts *ClientOptions) { + opts.Hooks = hooks + } +} + +// WithClientInterceptors defines the interceptors for a Twirp client. +func WithClientInterceptors(interceptors ...Interceptor) ClientOption { + return func(opts *ClientOptions) { + opts.Interceptors = append(opts.Interceptors, interceptors...) + } +} + +// WithClientPathPrefix specifies a different prefix to use for routing. +// If not specified, the "/twirp" prefix is used by default. +// The service must be configured to serve on the same prefix. +// An empty value "" can be speficied to use no prefix. +// URL format: "<baseURL>[<prefix>]/<package>.<Service>/<Method>" +// More info on Twirp docs: https://twitchtv.github.io/twirp/docs/routing.html +func WithClientPathPrefix(prefix string) ClientOption { + return func(opts *ClientOptions) { + opts.setOpt("pathPrefix", prefix) + opts.pathPrefix = &prefix // for code generated before v8.1.0 + } +} + +// WithClientLiteralURLs configures the Twirp client to use the exact values +// as defined in the proto file for Service and Method names, +// fixing the issue https://github.com/twitchtv/twirp/issues/244, which is manifested +// when working with Twirp services implemented other languages (e.g. Python) and the proto file definitions +// are not properly following the [Protobuf Style Guide](https://developers.google.com/protocol-buffers/docs/style#services). +// By default (false), Go clients modify the routes by CamelCasing the values. For example, +// with Service: `haberdasher`, Method: `make_hat`, the URLs generated by Go clients are `Haberdasher/MakeHat`, +// but with this option enabled (true) the client will properly use `haberdasher/make_hat` instead. +func WithClientLiteralURLs(b bool) ClientOption { + return func(opts *ClientOptions) { + opts.setOpt("literalURLs", b) + opts.LiteralURLs = b // for code generated before v8.1.0 + } +} + +// ClientHooks is a container for callbacks that can instrument a +// Twirp-generated client. These callbacks all accept a context and some return +// a context. They can use this to add to the context, appending values or +// deadlines to it. +// +// The RequestPrepared hook is special because it can return errors. If it +// returns non-nil error, handling for that request will be stopped at that +// point. The Error hook will then be triggered. +// +// The RequestPrepared hook will always be called first and will be called for +// each outgoing request from the Twirp client. The last hook to be called +// will either be Error or ResponseReceived, so be sure to handle both cases in +// your hooks. +type ClientHooks struct { + // RequestPrepared is called as soon as a request has been created and before + // it has been sent to the Twirp server. + RequestPrepared func(context.Context, *http.Request) (context.Context, error) + + // ResponseReceived is called after a request has finished sending. Since this + // is terminal, the context is not returned. ResponseReceived will not be + // called in the case of an error being returned from the request. + ResponseReceived func(context.Context) + + // Error hook is called whenever an error occurs during the sending of a + // request. The Error is passed as an argument to the hook. + Error func(context.Context, Error) +} + +// ChainClientHooks creates a new *ClientHooks which chains the callbacks in +// each of the constituent hooks passed in. Each hook function will be +// called in the order of the ClientHooks values passed in. +// +// For the erroring hook, RequestPrepared, any returned +// errors prevent processing by later hooks. +func ChainClientHooks(hooks ...*ClientHooks) *ClientHooks { + if len(hooks) == 0 { + return nil + } + if len(hooks) == 1 { + return hooks[0] + } + return &ClientHooks{ + RequestPrepared: func(ctx context.Context, req *http.Request) (context.Context, error) { + for _, h := range hooks { + if h != nil && h.RequestPrepared != nil { + var err error + ctx, err = h.RequestPrepared(ctx, req) + if err != nil { + return ctx, err + } + } + } + return ctx, nil + }, + ResponseReceived: func(ctx context.Context) { + for _, h := range hooks { + if h != nil && h.ResponseReceived != nil { + h.ResponseReceived(ctx) + } + } + }, + Error: func(ctx context.Context, twerr Error) { + for _, h := range hooks { + if h != nil && h.Error != nil { + h.Error(ctx, twerr) + } + } + }, + } +} + +// ClientOptions encapsulate the configurable parameters on a Twirp client. +// This type is meant to be used only by generated code. +type ClientOptions struct { + // Untyped options map. The methods setOpt and ReadOpt are used to set + // and read options. The options are untyped so when a new option is added, + // newly generated code can still work with older versions of the runtime. + m map[string]interface{} + + Hooks *ClientHooks + Interceptors []Interceptor + + // Properties below are only used by code that was + // generated by older versions of Twirp (before v8.1.0). + // New options with standard types added in the future + // don't need new properties, they should use ReadOpt. + LiteralURLs bool + pathPrefix *string +} + +// ReadOpt extracts an option to a pointer value, +// returns true if the option exists and was extracted. +// This method is meant to be used by generated code, +// keeping the type dependency outside of the runtime. +// +// Usage example: +// +// opts.setOpt("fooOpt", 123) +// var foo int +// ok := opts.ReadOpt("fooOpt", &int) +// +func (opts *ClientOptions) ReadOpt(key string, out interface{}) bool { + val, ok := opts.m[key] + if !ok { + return false + } + + rout := reflect.ValueOf(out) + if rout.Kind() != reflect.Ptr { + panic("ReadOpt(key, out); out must be a pointer but it was not") + } + rout.Elem().Set(reflect.ValueOf(val)) + return true +} + +// setOpt adds an option key/value. It is used by ServerOption helpers. +// The value can be extracted with ReadOpt by passing a pointer to the same type. +func (opts *ClientOptions) setOpt(key string, val interface{}) { + if opts.m == nil { + opts.m = make(map[string]interface{}) + } + opts.m[key] = val +} + +// PathPrefix() is used only by clients generated before v8.1.0 +func (opts *ClientOptions) PathPrefix() string { + if opts.pathPrefix == nil { + return "/twirp" // default prefix + } + return *opts.pathPrefix +} diff --git a/vendor/github.com/twitchtv/twirp/context.go b/vendor/github.com/twitchtv/twirp/context.go new file mode 100644 index 0000000..eb8ff94 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/context.go @@ -0,0 +1,142 @@ +// Copyright 2018 Twitch Interactive, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may not +// use this file except in compliance with the License. A copy of the License is +// located at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// or in the "license" file accompanying this file. This file is distributed on +// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +package twirp + +import ( + "context" + "errors" + "net/http" + + "github.com/twitchtv/twirp/internal/contextkeys" +) + +// MethodName extracts the name of the method being handled in the given +// context. If it is not known, it returns ("", false). +func MethodName(ctx context.Context) (string, bool) { + name, ok := ctx.Value(contextkeys.MethodNameKey).(string) + return name, ok +} + +// ServiceName extracts the name of the service handling the given context. If +// it is not known, it returns ("", false). +func ServiceName(ctx context.Context) (string, bool) { + name, ok := ctx.Value(contextkeys.ServiceNameKey).(string) + return name, ok +} + +// PackageName extracts the fully-qualified protobuf package name of the service +// handling the given context. If it is not known, it returns ("", false). If +// the service comes from a proto file that does not declare a package name, it +// returns ("", true). +// +// Note that the protobuf package name can be very different than the go package +// name; the two are unrelated. +func PackageName(ctx context.Context) (string, bool) { + name, ok := ctx.Value(contextkeys.PackageNameKey).(string) + return name, ok +} + +// StatusCode retrieves the status code of the response (as string like "200"). +// If it is known returns (status, true). +// If it is not known, it returns ("", false). +func StatusCode(ctx context.Context) (string, bool) { + code, ok := ctx.Value(contextkeys.StatusCodeKey).(string) + return code, ok +} + +// WithHTTPRequestHeaders stores an http.Header in a context.Context. When +// using a Twirp-generated client, you can pass the returned context +// into any of the request methods, and the stored header will be +// included in outbound HTTP requests. +// +// This can be used to set custom HTTP headers like authorization tokens or +// client IDs. But note that HTTP headers are a Twirp implementation detail, +// only visible by middleware, not by the server implementation. +// +// WithHTTPRequestHeaders returns an error if the provided http.Header +// would overwrite a header that is needed by Twirp, like "Content-Type". +func WithHTTPRequestHeaders(ctx context.Context, h http.Header) (context.Context, error) { + if _, ok := h["Accept"]; ok { + return nil, errors.New("provided header cannot set Accept") + } + if _, ok := h["Content-Type"]; ok { + return nil, errors.New("provided header cannot set Content-Type") + } + if _, ok := h["Twirp-Version"]; ok { + return nil, errors.New("provided header cannot set Twirp-Version") + } + + copied := make(http.Header, len(h)) + for k, vv := range h { + if vv == nil { + copied[k] = nil + continue + } + copied[k] = make([]string, len(vv)) + copy(copied[k], vv) + } + + return context.WithValue(ctx, contextkeys.RequestHeaderKey, copied), nil +} + +func HTTPRequestHeaders(ctx context.Context) (http.Header, bool) { + h, ok := ctx.Value(contextkeys.RequestHeaderKey).(http.Header) + return h, ok +} + +// SetHTTPResponseHeader sets an HTTP header key-value pair using a context +// provided by a twirp-generated server, or a child of that context. +// The server will include the header in its response for that request context. +// +// This can be used to respond with custom HTTP headers like "Cache-Control". +// But note that HTTP headers are a Twirp implementation detail, +// only visible by middleware, not by the clients or their responses. +// +// The header will be ignored (noop) if the context is invalid (i.e. using a new +// context.Background() instead of passing the context from the handler). +// +// If called multiple times with the same key, it replaces any existing values +// associated with that key. +// +// SetHTTPResponseHeader returns an error if the provided header key +// would overwrite a header that is needed by Twirp, like "Content-Type". +func SetHTTPResponseHeader(ctx context.Context, key, value string) error { + if key == "Content-Type" { + return errors.New("header key can not be Content-Type") + } + + responseWriter, ok := ctx.Value(contextkeys.ResponseWriterKey).(http.ResponseWriter) + if ok { + responseWriter.Header().Set(key, value) + } // invalid context is ignored, not an error, this is to allow easy unit testing with mock servers + + return nil +} + +// AddHTTPResponseHeader behaves like SetHTTPResponseHeader, +// but it appends the key-value pair to the header (instead of replacing it). +// +// AddHTTPResponseHeader returns an error if the key is "Content-Type". +func AddHTTPResponseHeader(ctx context.Context, key, value string) error { + if key == "Content-Type" { + return errors.New("header key can not be Content-Type") + } + + responseWriter, ok := ctx.Value(contextkeys.ResponseWriterKey).(http.ResponseWriter) + if ok { + responseWriter.Header().Add(key, value) + } // invalid context is ignored, not an error, this is to allow easy unit testing with mock servers + + return nil +} diff --git a/vendor/github.com/twitchtv/twirp/ctxsetters/ctxsetters.go b/vendor/github.com/twitchtv/twirp/ctxsetters/ctxsetters.go new file mode 100644 index 0000000..3294da7 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/ctxsetters/ctxsetters.go @@ -0,0 +1,47 @@ +// Copyright 2018 Twitch Interactive, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may not +// use this file except in compliance with the License. A copy of the License is +// located at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// or in the "license" file accompanying this file. This file is distributed on +// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +// Package ctxsetters is an implementation detail for twirp generated code, used +// by the generated servers to set values in contexts for later access with the +// twirp package's accessors. +// +// Do not use ctxsetters outside of twirp's generated code. +package ctxsetters + +import ( + "context" + "net/http" + "strconv" + + "github.com/twitchtv/twirp/internal/contextkeys" +) + +func WithMethodName(ctx context.Context, name string) context.Context { + return context.WithValue(ctx, contextkeys.MethodNameKey, name) +} + +func WithServiceName(ctx context.Context, name string) context.Context { + return context.WithValue(ctx, contextkeys.ServiceNameKey, name) +} + +func WithPackageName(ctx context.Context, name string) context.Context { + return context.WithValue(ctx, contextkeys.PackageNameKey, name) +} + +func WithStatusCode(ctx context.Context, code int) context.Context { + return context.WithValue(ctx, contextkeys.StatusCodeKey, strconv.Itoa(code)) +} + +func WithResponseWriter(ctx context.Context, w http.ResponseWriter) context.Context { + return context.WithValue(ctx, contextkeys.ResponseWriterKey, w) +} diff --git a/vendor/github.com/twitchtv/twirp/errors.go b/vendor/github.com/twitchtv/twirp/errors.go new file mode 100644 index 0000000..b9664b4 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/errors.go @@ -0,0 +1,428 @@ +// Copyright 2018 Twitch Interactive, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may not +// use this file except in compliance with the License. A copy of the License is +// located at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// or in the "license" file accompanying this file. This file is distributed on +// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +// Package twirp provides core types used in generated Twirp servers and client. +// +// Twirp services handle errors using the `twirp.Error` interface. +// +// For example, a server method may return an InvalidArgumentError: +// +// if req.Order != "DESC" && req.Order != "ASC" { +// return nil, twirp.InvalidArgumentError("Order", "must be DESC or ASC") +// } +// +// And the same twirp.Error is returned by the client, for example: +// +// resp, err := twirpClient.RPCMethod(ctx, req) +// if err != nil { +// if twerr, ok := err.(twirp.Error); ok { +// switch twerr.Code() { +// case twirp.InvalidArgument: +// log.Error("invalid argument "+twirp.Meta("argument")) +// default: +// log.Error(twerr.Error()) +// } +// } +// } +// +// Clients may also return Internal errors if something failed on the system: +// the server, the network, or the client itself (i.e. failure parsing +// response). +// +package twirp + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "strconv" +) + +// Error represents an error in a Twirp service call. +type Error interface { + // Code is of the valid error codes. + Code() ErrorCode + + // Msg returns a human-readable, unstructured messages describing the error. + Msg() string + + // WithMeta returns a copy of the Error with the given key-value pair attached + // as metadata. If the key is already set, it is overwritten. + WithMeta(key string, val string) Error + + // Meta returns the stored value for the given key. If the key has no set + // value, Meta returns an empty string. There is no way to distinguish between + // an unset value and an explicit empty string. + Meta(key string) string + + // MetaMap returns the complete key-value metadata map stored on the error. + MetaMap() map[string]string + + // Error returns a string of the form "twirp error <Code>: <Msg>" + Error() string +} + +// code.Error(msg) builds a new Twirp error with code and msg. Example: +// twirp.NotFound.Error("Resource not found") +// twirp.Internal.Error("Oops") +func (code ErrorCode) Error(msg string) Error { + return NewError(code, msg) +} + +// code.Errorf(msg, args...) builds a new Twirp error with code and formatted msg. +// The format may include "%w" to wrap other errors. Examples: +// twirp.Internal.Error("Oops: %w", originalErr) +// twirp.NotFound.Error("Resource not found with id: %q", resourceID) +func (code ErrorCode) Errorf(msgFmt string, a ...interface{}) Error { + return NewErrorf(code, msgFmt, a...) +} + +// WrapError allows Twirp errors to wrap other errors. +// The wrapped error can be extracted later with (github.com/pkg/errors).Unwrap +// or errors.Is from the standard errors package on Go 1.13+. +func WrapError(twerr Error, err error) Error { + return &wrappedErr{ + wrapper: twerr, + cause: err, + } +} + +// NewError builds a twirp.Error. The code must be one of the valid predefined constants. +// To add metadata, use .WithMeta(key, value) method after building the error. +func NewError(code ErrorCode, msg string) Error { + if !IsValidErrorCode(code) { + return &twerr{code: Internal, msg: "invalid error type " + string(code)} + } + return &twerr{code: code, msg: msg} +} + +// NewErrorf builds a twirp.Error with a formatted msg. +// The format may include "%w" to wrap other errors. Examples: +// twirp.NewErrorf(twirp.Internal, "Oops: %w", originalErr) +// twirp.NewErrorf(twirp.NotFound, "resource with id: %q", resourceID) +func NewErrorf(code ErrorCode, msgFmt string, a ...interface{}) Error { + err := fmt.Errorf(msgFmt, a...) // format error message, may include "%w" with an original error + twerr := NewError(code, err.Error()) // use the error as msg + return WrapError(twerr, err) // wrap so the original error can be identified with errors.Is +} + +// NotFoundError is a convenience constructor for NotFound errors. +func NotFoundError(msg string) Error { + return NewError(NotFound, msg) +} + +// InvalidArgumentError is a convenience constructor for InvalidArgument errors. +// The argument name is included on the "argument" metadata for convenience. +func InvalidArgumentError(argument string, validationMsg string) Error { + err := NewError(InvalidArgument, argument+" "+validationMsg) + err = err.WithMeta("argument", argument) + return err +} + +// RequiredArgumentError builds an InvalidArgument error. +// Useful when a request argument is expected to have a non-zero value. +func RequiredArgumentError(argument string) Error { + return InvalidArgumentError(argument, "is required") +} + +// InternalError is a convenience constructor for Internal errors. +func InternalError(msg string) Error { + return NewError(Internal, msg) +} + +// InternalErrorf uses the formatted message as the internal error msg. +// The format may include "%w" to wrap other errors. Examples: +// twirp.InternalErrorf("database error: %w", err) +// twirp.InternalErrorf("failed to load resource %q: %w", resourceID, originalErr) +func InternalErrorf(msgFmt string, a ...interface{}) Error { + return NewErrorf(Internal, msgFmt, a...) +} + +// InternalErrorWith makes an internal error, wrapping the original error and using it +// for the error message, and with metadata "cause" with the original error type. +// This function is used by Twirp services to wrap non-Twirp errors as internal errors. +// The wrapped error can be extracted later with (github.com/pkg/errors).Unwrap +// or errors.Is from the standard errors package on Go 1.13+. +func InternalErrorWith(err error) Error { + twerr := NewError(Internal, err.Error()) + twerr = twerr.WithMeta("cause", fmt.Sprintf("%T", err)) // to easily tell apart wrapped internal errors from explicit ones + return WrapError(twerr, err) +} + +// ErrorCode represents a Twirp error type. +type ErrorCode string + +// Valid Twirp error types. Most error types are equivalent to gRPC status codes +// and follow the same semantics. +const ( + // Canceled indicates the operation was cancelled (typically by the caller). + Canceled ErrorCode = "canceled" + + // Unknown error. For example when handling errors raised by APIs that do not + // return enough error information. + Unknown ErrorCode = "unknown" + + // InvalidArgument indicates client specified an invalid argument. It + // indicates arguments that are problematic regardless of the state of the + // system (i.e. a malformed file name, required argument, number out of range, + // etc.). + InvalidArgument ErrorCode = "invalid_argument" + + // Malformed indicates an error occurred while decoding the client's request. + // This may mean that the message was encoded improperly, or that there is a + // disagreement in message format between the client and server. + Malformed ErrorCode = "malformed" + + // DeadlineExceeded means operation expired before completion. For operations + // that change the state of the system, this error may be returned even if the + // operation has completed successfully (timeout). + DeadlineExceeded ErrorCode = "deadline_exceeded" + + // NotFound means some requested entity was not found. + NotFound ErrorCode = "not_found" + + // BadRoute means that the requested URL path wasn't routable to a Twirp + // service and method. This is returned by the generated server, and usually + // shouldn't be returned by applications. Instead, applications should use + // NotFound or Unimplemented. + BadRoute ErrorCode = "bad_route" + + // AlreadyExists means an attempt to create an entity failed because one + // already exists. + AlreadyExists ErrorCode = "already_exists" + + // PermissionDenied indicates the caller does not have permission to execute + // the specified operation. It must not be used if the caller cannot be + // identified (Unauthenticated). + PermissionDenied ErrorCode = "permission_denied" + + // Unauthenticated indicates the request does not have valid authentication + // credentials for the operation. + Unauthenticated ErrorCode = "unauthenticated" + + // ResourceExhausted indicates some resource has been exhausted or rate-limited, + // perhaps a per-user quota, or perhaps the entire file system is out of space. + ResourceExhausted ErrorCode = "resource_exhausted" + + // FailedPrecondition indicates operation was rejected because the system is + // not in a state required for the operation's execution. For example, doing + // an rmdir operation on a directory that is non-empty, or on a non-directory + // object, or when having conflicting read-modify-write on the same resource. + FailedPrecondition ErrorCode = "failed_precondition" + + // Aborted indicates the operation was aborted, typically due to a concurrency + // issue like sequencer check failures, transaction aborts, etc. + Aborted ErrorCode = "aborted" + + // OutOfRange means operation was attempted past the valid range. For example, + // seeking or reading past end of a paginated collection. + // + // Unlike InvalidArgument, this error indicates a problem that may be fixed if + // the system state changes (i.e. adding more items to the collection). + // + // There is a fair bit of overlap between FailedPrecondition and OutOfRange. + // We recommend using OutOfRange (the more specific error) when it applies so + // that callers who are iterating through a space can easily look for an + // OutOfRange error to detect when they are done. + OutOfRange ErrorCode = "out_of_range" + + // Unimplemented indicates operation is not implemented or not + // supported/enabled in this service. + Unimplemented ErrorCode = "unimplemented" + + // Internal errors. When some invariants expected by the underlying system + // have been broken. In other words, something bad happened in the library or + // backend service. Do not confuse with HTTP Internal Server Error; an + // Internal error could also happen on the client code, i.e. when parsing a + // server response. + Internal ErrorCode = "internal" + + // Unavailable indicates the service is currently unavailable. This is a most + // likely a transient condition and may be corrected by retrying with a + // backoff. + Unavailable ErrorCode = "unavailable" + + // DataLoss indicates unrecoverable data loss or corruption. + DataLoss ErrorCode = "data_loss" + + // NoError is the zero-value, is considered an empty error and should not be + // used. + NoError ErrorCode = "" +) + +// ServerHTTPStatusFromErrorCode maps a Twirp error type into a similar HTTP +// response status. It is used by the Twirp server handler to set the HTTP +// response status code. Returns 0 if the ErrorCode is invalid. +func ServerHTTPStatusFromErrorCode(code ErrorCode) int { + switch code { + case Canceled: + return 408 // RequestTimeout + case Unknown: + return 500 // Internal Server Error + case InvalidArgument: + return 400 // BadRequest + case Malformed: + return 400 // BadRequest + case DeadlineExceeded: + return 408 // RequestTimeout + case NotFound: + return 404 // Not Found + case BadRoute: + return 404 // Not Found + case AlreadyExists: + return 409 // Conflict + case PermissionDenied: + return 403 // Forbidden + case Unauthenticated: + return 401 // Unauthorized + case ResourceExhausted: + return 429 // Too Many Requests + case FailedPrecondition: + return 412 // Precondition Failed + case Aborted: + return 409 // Conflict + case OutOfRange: + return 400 // Bad Request + case Unimplemented: + return 501 // Not Implemented + case Internal: + return 500 // Internal Server Error + case Unavailable: + return 503 // Service Unavailable + case DataLoss: + return 500 // Internal Server Error + case NoError: + return 200 // OK + default: + return 0 // Invalid! + } +} + +// IsValidErrorCode returns true if is one of the valid predefined constants. +func IsValidErrorCode(code ErrorCode) bool { + return ServerHTTPStatusFromErrorCode(code) != 0 +} + +// twirp.Error implementation +type twerr struct { + code ErrorCode + msg string + meta map[string]string +} + +func (e *twerr) Code() ErrorCode { return e.code } +func (e *twerr) Msg() string { return e.msg } + +func (e *twerr) Meta(key string) string { + if e.meta != nil { + return e.meta[key] // also returns "" if key is not in meta map + } + return "" +} + +func (e *twerr) WithMeta(key string, value string) Error { + newErr := &twerr{ + code: e.code, + msg: e.msg, + meta: make(map[string]string, len(e.meta)), + } + for k, v := range e.meta { + newErr.meta[k] = v + } + newErr.meta[key] = value + return newErr +} + +func (e *twerr) MetaMap() map[string]string { + return e.meta +} + +func (e *twerr) Error() string { + return fmt.Sprintf("twirp error %s: %s", e.code, e.msg) +} + +// wrappedErr is the error returned by twirp.InternalErrorWith(err), which is used by clients. +// Implements Unwrap() to allow go 1.13+ errors.Is/As checks, +// and Cause() to allow (github.com/pkg/errors).Unwrap. +type wrappedErr struct { + wrapper Error + cause error +} + +func (e *wrappedErr) Code() ErrorCode { return e.wrapper.Code() } +func (e *wrappedErr) Msg() string { return e.wrapper.Msg() } +func (e *wrappedErr) Meta(key string) string { return e.wrapper.Meta(key) } +func (e *wrappedErr) MetaMap() map[string]string { return e.wrapper.MetaMap() } +func (e *wrappedErr) Error() string { return e.wrapper.Error() } +func (e *wrappedErr) WithMeta(key string, val string) Error { + return &wrappedErr{ + wrapper: e.wrapper.WithMeta(key, val), + cause: e.cause, + } +} +func (e *wrappedErr) Unwrap() error { return e.cause } // for go1.13 + errors.Is/As +func (e *wrappedErr) Cause() error { return e.cause } // for github.com/pkg/errors + +// WriteError writes an HTTP response with a valid Twirp error format (code, msg, meta). +// Useful outside of the Twirp server (e.g. http middleware). +// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) +func WriteError(resp http.ResponseWriter, err error) error { + var twerr Error + if !errors.As(err, &twerr) { + twerr = InternalErrorWith(err) + } + + statusCode := ServerHTTPStatusFromErrorCode(twerr.Code()) + respBody := marshalErrorToJSON(twerr) + + resp.Header().Set("Content-Type", "application/json") // Error responses are always JSON + resp.Header().Set("Content-Length", strconv.Itoa(len(respBody))) + resp.WriteHeader(statusCode) // set HTTP status code and send response + + _, writeErr := resp.Write(respBody) + if writeErr != nil { + return writeErr + } + return nil +} + +// JSON serialization for errors +type twerrJSON struct { + Code string `json:"code"` + Msg string `json:"msg"` + Meta map[string]string `json:"meta,omitempty"` +} + +// marshalErrorToJSON returns JSON from a twirp.Error, that can be used as HTTP error response body. +// If serialization fails, it will use a descriptive Internal error instead. +func marshalErrorToJSON(twerr Error) []byte { + // make sure that msg is not too large + msg := twerr.Msg() + if len(msg) > 1e6 { + msg = msg[:1e6] + } + + tj := twerrJSON{ + Code: string(twerr.Code()), + Msg: msg, + Meta: twerr.MetaMap(), + } + + buf, err := json.Marshal(&tj) + if err != nil { + buf = []byte("{\"type\": \"" + Internal + "\", \"msg\": \"There was an error but it could not be serialized into JSON\"}") // fallback + } + + return buf +} diff --git a/vendor/github.com/twitchtv/twirp/interceptors.go b/vendor/github.com/twitchtv/twirp/interceptors.go new file mode 100644 index 0000000..0a5cbcf --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/interceptors.go @@ -0,0 +1,72 @@ +// Copyright 2018 Twitch Interactive, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may not +// use this file except in compliance with the License. A copy of the License is +// located at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// or in the "license" file accompanying this file. This file is distributed on +// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. +package twirp + +import ( + "context" +) + +// Interceptor is a form of middleware for Twirp requests, that can be installed on both +// clients and servers. To intercept RPC calls in the client, use the option +// `twirp.WithClientInterceptors` on the client constructor. To intercept RPC calls in the server, +// use the option `twirp.WithServerInterceptors` on the server constructor. +// +// Just like http middleware, interceptors can mutate requests and responses. +// This can enable some powerful integrations, but it should be used with much care +// because it may result in code that is very hard to debug. +// +// Example of an interceptor that logs every request and response: +// +// func LogInterceptor(l *log.Logger) twirp.Interceptor { +// return func(next twirp.Method) twirp.Method { +// return func(ctx context.Context, req interface{}) (interface{}, error) { +// l.Printf("Service: %s, Method: %s, Request: %v", +// twirp.ServiceName(ctx), twirp.MethodName(ctx), req) +// resp, err := next(ctx, req) +// l.Printf("Response: %v, Error: %v", resp) +// return resp, err +// } +// } +// } +// +type Interceptor func(Method) Method + +// Method is a generic representation of a Twirp-generated RPC method. +// It is used to define Interceptors. +type Method func(ctx context.Context, request interface{}) (interface{}, error) + +// ChainInterceptors chains multiple Interceptors into a single Interceptor. +// The first interceptor wraps the second one, and so on. +// Returns nil if interceptors is empty. Nil interceptors are ignored. +func ChainInterceptors(interceptors ...Interceptor) Interceptor { + filtered := make([]Interceptor, 0, len(interceptors)) + for _, interceptor := range interceptors { + if interceptor != nil { + filtered = append(filtered, interceptor) + } + } + switch n := len(filtered); n { + case 0: + return nil + case 1: + return filtered[0] + default: + first := filtered[0] + return func(next Method) Method { + for i := len(filtered) - 1; i > 0; i-- { + next = filtered[i](next) + } + return first(next) + } + } +} diff --git a/vendor/github.com/twitchtv/twirp/internal/contextkeys/keys.go b/vendor/github.com/twitchtv/twirp/internal/contextkeys/keys.go new file mode 100644 index 0000000..e8c35bd --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/internal/contextkeys/keys.go @@ -0,0 +1,28 @@ +// Copyright 2018 Twitch Interactive, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may not +// use this file except in compliance with the License. A copy of the License is +// located at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// or in the "license" file accompanying this file. This file is distributed on +// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +// Package contextkeys stores the keys to the context accessor +// functions, letting generated code safely set values in contexts +// without exposing the setters to the outside world. +package contextkeys + +type contextKey int + +const ( + MethodNameKey contextKey = 1 + iota + ServiceNameKey + PackageNameKey + StatusCodeKey + RequestHeaderKey + ResponseWriterKey +) diff --git a/vendor/github.com/twitchtv/twirp/logo.png b/vendor/github.com/twitchtv/twirp/logo.png Binary files differnew file mode 100644 index 0000000..c80f897 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/logo.png diff --git a/vendor/github.com/twitchtv/twirp/server_options.go b/vendor/github.com/twitchtv/twirp/server_options.go new file mode 100644 index 0000000..73798fc --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/server_options.go @@ -0,0 +1,242 @@ +// Copyright 2018 Twitch Interactive, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may not +// use this file except in compliance with the License. A copy of the License is +// located at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// or in the "license" file accompanying this file. This file is distributed on +// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. +package twirp + +import ( + "context" + "reflect" +) + +// ServerOption is a functional option for extending a Twirp service. +type ServerOption func(*ServerOptions) + +// WithServerHooks defines the hooks for a Twirp server. +func WithServerHooks(hooks *ServerHooks) ServerOption { + return func(opts *ServerOptions) { + opts.Hooks = hooks + } +} + +// WithServerInterceptors defines the interceptors for a Twirp server. +func WithServerInterceptors(interceptors ...Interceptor) ServerOption { + return func(opts *ServerOptions) { + opts.Interceptors = append(opts.Interceptors, interceptors...) + } +} + +// WithServerPathPrefix specifies a different prefix for routing. +// If not specified, the "/twirp" prefix is used by default. +// An empty value "" can be speficied to use no prefix. +// The clients must be configured to send requests using the same prefix. +// URL format: "<baseURL>[<prefix>]/<package>.<Service>/<Method>" +// More info on Twirp docs: https://twitchtv.github.io/twirp/docs/routing.html +func WithServerPathPrefix(prefix string) ServerOption { + return func(opts *ServerOptions) { + opts.setOpt("pathPrefix", prefix) + opts.pathPrefix = &prefix // for code generated before v8.1.0 + } +} + +// WithServerJSONSkipDefaults configures JSON serialization to skip +// unpopulated or default values in JSON responses, which results in +// smaller response sizes. This was the default before v7 and can be +// enabled for full backwards compatibility if required. +// This is now disabled by default, because JSON serialization is +// commonly used for manual debugging, in which case it is useful +// to see the full shape of the response. +// See: https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson +// See: https://developers.google.com/protocol-buffers/docs/proto3#json +func WithServerJSONSkipDefaults(skipDefaults bool) ServerOption { + return func(opts *ServerOptions) { + opts.setOpt("jsonSkipDefaults", skipDefaults) + opts.JSONSkipDefaults = skipDefaults // for code generated before v8.1.0 + } +} + +// WithServerJSONCamelCaseNames configures JSON serialization to use the +// default proto3 JSON encoding (lowerCamelCase) rather than the original +// proto field names. Twirp uses the original proto field names by default, +// because JSON encoding is often used for manual debugging of the API, +// but this option allows better compatibility with other proto-json parsers. +// See: https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson +// See: https://developers.google.com/protocol-buffers/docs/proto3#json +func WithServerJSONCamelCaseNames(jsonCamelCase bool) ServerOption { + return func(opts *ServerOptions) { + opts.setOpt("jsonCamelCase", jsonCamelCase) + } +} + +// ServerHooks is a container for callbacks that can instrument a +// Twirp-generated server. These callbacks all accept a context and return a +// context. They can use this to add to the request context as it threads +// through the system, appending values or deadlines to it. +// +// The RequestReceived and RequestRouted hooks are special: they can return +// errors. If they return a non-nil error, handling for that request will be +// stopped at that point. The Error hook will be triggered, and the error will +// be sent to the client. This can be used for stuff like auth checks before +// deserializing a request. +// +// The RequestReceived hook is always called first, and it is called for every +// request that the Twirp server handles. The last hook to be called in a +// request's lifecycle is always ResponseSent, even in the case of an error. +// +// Details on the timing of each hook are documented as comments on the fields +// of the ServerHooks type. +type ServerHooks struct { + // RequestReceived is called as soon as a request enters the Twirp + // server at the earliest available moment. + RequestReceived func(context.Context) (context.Context, error) + + // RequestRouted is called when a request has been routed to a + // particular method of the Twirp server. + RequestRouted func(context.Context) (context.Context, error) + + // ResponsePrepared is called when a request has been handled and a + // response is ready to be sent to the client. + ResponsePrepared func(context.Context) context.Context + + // ResponseSent is called when all bytes of a response (including an error + // response) have been written. Because the ResponseSent hook is terminal, it + // does not return a context. + ResponseSent func(context.Context) + + // Error hook is called when an error occurs while handling a request. The + // Error is passed as argument to the hook. + Error func(context.Context, Error) context.Context +} + +// ChainHooks creates a new *ServerHooks which chains the callbacks in +// each of the constituent hooks passed in. Each hook function will be +// called in the order of the ServerHooks values passed in. +// +// For the erroring hooks, RequestReceived and RequestRouted, any returned +// errors prevent processing by later hooks. +func ChainHooks(hooks ...*ServerHooks) *ServerHooks { + if len(hooks) == 0 { + return nil + } + if len(hooks) == 1 { + return hooks[0] + } + return &ServerHooks{ + RequestReceived: func(ctx context.Context) (context.Context, error) { + var err error + for _, h := range hooks { + if h != nil && h.RequestReceived != nil { + ctx, err = h.RequestReceived(ctx) + if err != nil { + return ctx, err + } + } + } + return ctx, nil + }, + RequestRouted: func(ctx context.Context) (context.Context, error) { + var err error + for _, h := range hooks { + if h != nil && h.RequestRouted != nil { + ctx, err = h.RequestRouted(ctx) + if err != nil { + return ctx, err + } + } + } + return ctx, nil + }, + ResponsePrepared: func(ctx context.Context) context.Context { + for _, h := range hooks { + if h != nil && h.ResponsePrepared != nil { + ctx = h.ResponsePrepared(ctx) + } + } + return ctx + }, + ResponseSent: func(ctx context.Context) { + for _, h := range hooks { + if h != nil && h.ResponseSent != nil { + h.ResponseSent(ctx) + } + } + }, + Error: func(ctx context.Context, twerr Error) context.Context { + for _, h := range hooks { + if h != nil && h.Error != nil { + ctx = h.Error(ctx, twerr) + } + } + return ctx + }, + } +} + +// ServerOptions encapsulate the configurable parameters on a Twirp server. +// This type is meant to be used only by generated code. +type ServerOptions struct { + // Untyped options map. The methods setOpt and ReadOpt are used to set + // and read options. The options are untyped so when a new option is added, + // newly generated code can still work with older versions of the runtime. + m map[string]interface{} + + Hooks *ServerHooks + Interceptors []Interceptor + + // Properties below are only used by code that was + // generated by older versions of Twirp (before v8.1.0). + // New options with standard types added in the future + // don't need new properties, they should use ReadOpt. + JSONSkipDefaults bool + pathPrefix *string +} + +// ReadOpt extracts an option to a pointer value, +// returns true if the option exists and was extracted. +// This method is meant to be used by generated code, +// keeping the type dependency outside of the runtime. +// +// Usage example: +// +// opts.setOpt("fooOpt", 123) +// var foo int +// ok := opts.ReadOpt("fooOpt", &int) +// +func (opts *ServerOptions) ReadOpt(key string, out interface{}) bool { + val, ok := opts.m[key] + if !ok { + return false + } + + rout := reflect.ValueOf(out) + if rout.Kind() != reflect.Ptr { + panic("ReadOpt(key, out); out must be a pointer but it was not") + } + rout.Elem().Set(reflect.ValueOf(val)) + return true +} + +// setOpt adds an option key/value. It is used by ServerOption helpers. +// The value can be extracted with ReadOpt by passing a pointer to the same type. +func (opts *ServerOptions) setOpt(key string, val interface{}) { + if opts.m == nil { + opts.m = make(map[string]interface{}) + } + opts.m[key] = val +} + +// PathPrefix() is used only by clients generated before v8.1.0 +func (opts *ServerOptions) PathPrefix() string { + if opts.pathPrefix == nil { + return "/twirp" // default prefix + } + return *opts.pathPrefix +} diff --git a/vendor/github.com/twitchtv/twirp/tools.json b/vendor/github.com/twitchtv/twirp/tools.json new file mode 100644 index 0000000..d0aa577 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/tools.json @@ -0,0 +1,17 @@ +{ + "Tools": [ + { + "Repository": "github.com/kisielk/errcheck", + "Commit": "db0ca22445717d1b2c51ac1034440e0a2a2de645" + }, + { + "Repository": "github.com/twitchtv/retool", + "Commit": "6f6d4930d88c40e23d2b54d12e64f0444e1fb4ef" + }, + { + "Repository": "google.golang.org/protobuf/cmd/protoc-gen-go", + "Commit": "fc9592f7ac4bade8f83e636263f8f07715c698d1" + } + ], + "RetoolVersion": "1.3.5" +}
\ No newline at end of file diff --git a/vendor/github.com/twitchtv/twirp/version_constant.go b/vendor/github.com/twitchtv/twirp/version_constant.go new file mode 100644 index 0000000..5e305aa --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/version_constant.go @@ -0,0 +1,22 @@ +// Copyright 2018 Twitch Interactive, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may not +// use this file except in compliance with the License. A copy of the License is +// located at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// or in the "license" file accompanying this file. This file is distributed on +// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +package twirp + +// TwirpPackageIsVersion7 is a constant referenced from generated code to +// assert version compatibility at compile time. +const TwirpPackageIsVersion7 = true + +// TwirpPackageMinVersion_8_1_0 is required from generated code to +// assert version compatibility at compile time. +const TwirpPackageMinVersion_8_1_0 = true diff --git a/vendor/gitlab.com/gitlab-org/software-supply-chain-security/authorization/authzd.git/pkg/rpc/ability.pb.go b/vendor/gitlab.com/gitlab-org/software-supply-chain-security/authorization/authzd.git/pkg/rpc/ability.pb.go new file mode 100644 index 0000000..939719f --- /dev/null +++ b/vendor/gitlab.com/gitlab-org/software-supply-chain-security/authorization/authzd.git/pkg/rpc/ability.pb.go @@ -0,0 +1,194 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.6 +// protoc v3.19.6 +// source: ability.proto + +package rpc + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type AllowRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Subject string `protobuf:"bytes,1,opt,name=subject,proto3" json:"subject,omitempty"` + Permission string `protobuf:"bytes,2,opt,name=permission,proto3" json:"permission,omitempty"` + Resource string `protobuf:"bytes,3,opt,name=resource,proto3" json:"resource,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AllowRequest) Reset() { + *x = AllowRequest{} + mi := &file_ability_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AllowRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AllowRequest) ProtoMessage() {} + +func (x *AllowRequest) ProtoReflect() protoreflect.Message { + mi := &file_ability_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AllowRequest.ProtoReflect.Descriptor instead. +func (*AllowRequest) Descriptor() ([]byte, []int) { + return file_ability_proto_rawDescGZIP(), []int{0} +} + +func (x *AllowRequest) GetSubject() string { + if x != nil { + return x.Subject + } + return "" +} + +func (x *AllowRequest) GetPermission() string { + if x != nil { + return x.Permission + } + return "" +} + +func (x *AllowRequest) GetResource() string { + if x != nil { + return x.Resource + } + return "" +} + +type AllowReply struct { + state protoimpl.MessageState `protogen:"open.v1"` + Result bool `protobuf:"varint,1,opt,name=result,proto3" json:"result,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AllowReply) Reset() { + *x = AllowReply{} + mi := &file_ability_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AllowReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AllowReply) ProtoMessage() {} + +func (x *AllowReply) ProtoReflect() protoreflect.Message { + mi := &file_ability_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AllowReply.ProtoReflect.Descriptor instead. +func (*AllowReply) Descriptor() ([]byte, []int) { + return file_ability_proto_rawDescGZIP(), []int{1} +} + +func (x *AllowReply) GetResult() bool { + if x != nil { + return x.Result + } + return false +} + +var File_ability_proto protoreflect.FileDescriptor + +const file_ability_proto_rawDesc = "" + + "\n" + + "\rability.proto\x12\tauthz.rpc\"d\n" + + "\fAllowRequest\x12\x18\n" + + "\asubject\x18\x01 \x01(\tR\asubject\x12\x1e\n" + + "\n" + + "permission\x18\x02 \x01(\tR\n" + + "permission\x12\x1a\n" + + "\bresource\x18\x03 \x01(\tR\bresource\"$\n" + + "\n" + + "AllowReply\x12\x16\n" + + "\x06result\x18\x01 \x01(\bR\x06result2F\n" + + "\aAbility\x12;\n" + + "\aAllowed\x12\x17.authz.rpc.AllowRequest\x1a\x15.authz.rpc.AllowReply\"\x00B\tZ\apkg/rpcb\x06proto3" + +var ( + file_ability_proto_rawDescOnce sync.Once + file_ability_proto_rawDescData []byte +) + +func file_ability_proto_rawDescGZIP() []byte { + file_ability_proto_rawDescOnce.Do(func() { + file_ability_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ability_proto_rawDesc), len(file_ability_proto_rawDesc))) + }) + return file_ability_proto_rawDescData +} + +var file_ability_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_ability_proto_goTypes = []any{ + (*AllowRequest)(nil), // 0: authz.rpc.AllowRequest + (*AllowReply)(nil), // 1: authz.rpc.AllowReply +} +var file_ability_proto_depIdxs = []int32{ + 0, // 0: authz.rpc.Ability.Allowed:input_type -> authz.rpc.AllowRequest + 1, // 1: authz.rpc.Ability.Allowed:output_type -> authz.rpc.AllowReply + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_ability_proto_init() } +func file_ability_proto_init() { + if File_ability_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_ability_proto_rawDesc), len(file_ability_proto_rawDesc)), + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_ability_proto_goTypes, + DependencyIndexes: file_ability_proto_depIdxs, + MessageInfos: file_ability_proto_msgTypes, + }.Build() + File_ability_proto = out.File + file_ability_proto_goTypes = nil + file_ability_proto_depIdxs = nil +} diff --git a/vendor/gitlab.com/gitlab-org/software-supply-chain-security/authorization/authzd.git/pkg/rpc/ability.twirp.go b/vendor/gitlab.com/gitlab-org/software-supply-chain-security/authorization/authzd.git/pkg/rpc/ability.twirp.go new file mode 100644 index 0000000..f5a3329 --- /dev/null +++ b/vendor/gitlab.com/gitlab-org/software-supply-chain-security/authorization/authzd.git/pkg/rpc/ability.twirp.go @@ -0,0 +1,1104 @@ +// Code generated by protoc-gen-twirp v8.1.3, DO NOT EDIT. +// source: ability.proto + +package rpc + +import context "context" +import fmt "fmt" +import http "net/http" +import io "io" +import json "encoding/json" +import strconv "strconv" +import strings "strings" + +import protojson "google.golang.org/protobuf/encoding/protojson" +import proto "google.golang.org/protobuf/proto" +import twirp "github.com/twitchtv/twirp" +import ctxsetters "github.com/twitchtv/twirp/ctxsetters" + +import bytes "bytes" +import errors "errors" +import path "path" +import url "net/url" + +// Version compatibility assertion. +// If the constant is not defined in the package, that likely means +// the package needs to be updated to work with this generated code. +// See https://twitchtv.github.io/twirp/docs/version_matrix.html +const _ = twirp.TwirpPackageMinVersion_8_1_0 + +// ================= +// Ability Interface +// ================= + +type Ability interface { + Allowed(context.Context, *AllowRequest) (*AllowReply, error) +} + +// ======================= +// Ability Protobuf Client +// ======================= + +type abilityProtobufClient struct { + client HTTPClient + urls [1]string + interceptor twirp.Interceptor + opts twirp.ClientOptions +} + +// NewAbilityProtobufClient creates a Protobuf client that implements the Ability interface. +// It communicates using Protobuf and can be configured with a custom HTTPClient. +func NewAbilityProtobufClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) Ability { + if c, ok := client.(*http.Client); ok { + client = withoutRedirects(c) + } + + clientOpts := twirp.ClientOptions{} + for _, o := range opts { + o(&clientOpts) + } + + // Using ReadOpt allows backwards and forwards compatibility with new options in the future + literalURLs := false + _ = clientOpts.ReadOpt("literalURLs", &literalURLs) + var pathPrefix string + if ok := clientOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { + pathPrefix = "/twirp" // default prefix + } + + // Build method URLs: <baseURL>[<prefix>]/<package>.<Service>/<Method> + serviceURL := sanitizeBaseURL(baseURL) + serviceURL += baseServicePath(pathPrefix, "authz.rpc", "Ability") + urls := [1]string{ + serviceURL + "Allowed", + } + + return &abilityProtobufClient{ + client: client, + urls: urls, + interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), + opts: clientOpts, + } +} + +func (c *abilityProtobufClient) Allowed(ctx context.Context, in *AllowRequest) (*AllowReply, error) { + ctx = ctxsetters.WithPackageName(ctx, "authz.rpc") + ctx = ctxsetters.WithServiceName(ctx, "Ability") + ctx = ctxsetters.WithMethodName(ctx, "Allowed") + caller := c.callAllowed + if c.interceptor != nil { + caller = func(ctx context.Context, req *AllowRequest) (*AllowReply, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*AllowRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*AllowRequest) when calling interceptor") + } + return c.callAllowed(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*AllowReply) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*AllowReply) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *abilityProtobufClient) callAllowed(ctx context.Context, in *AllowRequest) (*AllowReply, error) { + out := new(AllowReply) + ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +// =================== +// Ability JSON Client +// =================== + +type abilityJSONClient struct { + client HTTPClient + urls [1]string + interceptor twirp.Interceptor + opts twirp.ClientOptions +} + +// NewAbilityJSONClient creates a JSON client that implements the Ability interface. +// It communicates using JSON and can be configured with a custom HTTPClient. +func NewAbilityJSONClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) Ability { + if c, ok := client.(*http.Client); ok { + client = withoutRedirects(c) + } + + clientOpts := twirp.ClientOptions{} + for _, o := range opts { + o(&clientOpts) + } + + // Using ReadOpt allows backwards and forwards compatibility with new options in the future + literalURLs := false + _ = clientOpts.ReadOpt("literalURLs", &literalURLs) + var pathPrefix string + if ok := clientOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { + pathPrefix = "/twirp" // default prefix + } + + // Build method URLs: <baseURL>[<prefix>]/<package>.<Service>/<Method> + serviceURL := sanitizeBaseURL(baseURL) + serviceURL += baseServicePath(pathPrefix, "authz.rpc", "Ability") + urls := [1]string{ + serviceURL + "Allowed", + } + + return &abilityJSONClient{ + client: client, + urls: urls, + interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), + opts: clientOpts, + } +} + +func (c *abilityJSONClient) Allowed(ctx context.Context, in *AllowRequest) (*AllowReply, error) { + ctx = ctxsetters.WithPackageName(ctx, "authz.rpc") + ctx = ctxsetters.WithServiceName(ctx, "Ability") + ctx = ctxsetters.WithMethodName(ctx, "Allowed") + caller := c.callAllowed + if c.interceptor != nil { + caller = func(ctx context.Context, req *AllowRequest) (*AllowReply, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*AllowRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*AllowRequest) when calling interceptor") + } + return c.callAllowed(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*AllowReply) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*AllowReply) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *abilityJSONClient) callAllowed(ctx context.Context, in *AllowRequest) (*AllowReply, error) { + out := new(AllowReply) + ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +// ====================== +// Ability Server Handler +// ====================== + +type abilityServer struct { + Ability + interceptor twirp.Interceptor + hooks *twirp.ServerHooks + pathPrefix string // prefix for routing + jsonSkipDefaults bool // do not include unpopulated fields (default values) in the response + jsonCamelCase bool // JSON fields are serialized as lowerCamelCase rather than keeping the original proto names +} + +// NewAbilityServer builds a TwirpServer that can be used as an http.Handler to handle +// HTTP requests that are routed to the right method in the provided svc implementation. +// The opts are twirp.ServerOption modifiers, for example twirp.WithServerHooks(hooks). +func NewAbilityServer(svc Ability, opts ...interface{}) TwirpServer { + serverOpts := newServerOpts(opts) + + // Using ReadOpt allows backwards and forwards compatibility with new options in the future + jsonSkipDefaults := false + _ = serverOpts.ReadOpt("jsonSkipDefaults", &jsonSkipDefaults) + jsonCamelCase := false + _ = serverOpts.ReadOpt("jsonCamelCase", &jsonCamelCase) + var pathPrefix string + if ok := serverOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { + pathPrefix = "/twirp" // default prefix + } + + return &abilityServer{ + Ability: svc, + hooks: serverOpts.Hooks, + interceptor: twirp.ChainInterceptors(serverOpts.Interceptors...), + pathPrefix: pathPrefix, + jsonSkipDefaults: jsonSkipDefaults, + jsonCamelCase: jsonCamelCase, + } +} + +// writeError writes an HTTP response with a valid Twirp error format, and triggers hooks. +// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) +func (s *abilityServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) { + writeError(ctx, resp, err, s.hooks) +} + +// handleRequestBodyError is used to handle error when the twirp server cannot read request +func (s *abilityServer) handleRequestBodyError(ctx context.Context, resp http.ResponseWriter, msg string, err error) { + if context.Canceled == ctx.Err() { + s.writeError(ctx, resp, twirp.NewError(twirp.Canceled, "failed to read request: context canceled")) + return + } + if context.DeadlineExceeded == ctx.Err() { + s.writeError(ctx, resp, twirp.NewError(twirp.DeadlineExceeded, "failed to read request: deadline exceeded")) + return + } + s.writeError(ctx, resp, twirp.WrapError(malformedRequestError(msg), err)) +} + +// AbilityPathPrefix is a convenience constant that may identify URL paths. +// Should be used with caution, it only matches routes generated by Twirp Go clients, +// with the default "/twirp" prefix and default CamelCase service and method names. +// More info: https://twitchtv.github.io/twirp/docs/routing.html +const AbilityPathPrefix = "/twirp/authz.rpc.Ability/" + +func (s *abilityServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + ctx := req.Context() + ctx = ctxsetters.WithPackageName(ctx, "authz.rpc") + ctx = ctxsetters.WithServiceName(ctx, "Ability") + ctx = ctxsetters.WithResponseWriter(ctx, resp) + + var err error + ctx, err = callRequestReceived(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + if req.Method != "POST" { + msg := fmt.Sprintf("unsupported method %q (only POST is allowed)", req.Method) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } + + // Verify path format: [<prefix>]/<package>.<Service>/<Method> + prefix, pkgService, method := parseTwirpPath(req.URL.Path) + if pkgService != "authz.rpc.Ability" { + msg := fmt.Sprintf("no handler for path %q", req.URL.Path) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } + if prefix != s.pathPrefix { + msg := fmt.Sprintf("invalid path prefix %q, expected %q, on path %q", prefix, s.pathPrefix, req.URL.Path) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } + + switch method { + case "Allowed": + s.serveAllowed(ctx, resp, req) + return + default: + msg := fmt.Sprintf("no handler for path %q", req.URL.Path) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } +} + +func (s *abilityServer) serveAllowed(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + header := req.Header.Get("Content-Type") + i := strings.Index(header, ";") + if i == -1 { + i = len(header) + } + switch strings.TrimSpace(strings.ToLower(header[:i])) { + case "application/json": + s.serveAllowedJSON(ctx, resp, req) + case "application/protobuf": + s.serveAllowedProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *abilityServer) serveAllowedJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Allowed") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + d := json.NewDecoder(req.Body) + rawReqBody := json.RawMessage{} + if err := d.Decode(&rawReqBody); err != nil { + s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) + return + } + reqContent := new(AllowRequest) + unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} + if err = unmarshaler.Unmarshal(rawReqBody, reqContent); err != nil { + s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) + return + } + + handler := s.Ability.Allowed + if s.interceptor != nil { + handler = func(ctx context.Context, req *AllowRequest) (*AllowReply, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*AllowRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*AllowRequest) when calling interceptor") + } + return s.Ability.Allowed(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*AllowReply) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*AllowReply) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *AllowReply + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *AllowReply and nil error while calling Allowed. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + marshaler := &protojson.MarshalOptions{UseProtoNames: !s.jsonCamelCase, EmitUnpopulated: !s.jsonSkipDefaults} + respBytes, err := marshaler.Marshal(respContent) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/json") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + +func (s *abilityServer) serveAllowedProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Allowed") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + buf, err := io.ReadAll(req.Body) + if err != nil { + s.handleRequestBodyError(ctx, resp, "failed to read request body", err) + return + } + reqContent := new(AllowRequest) + if err = proto.Unmarshal(buf, reqContent); err != nil { + s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) + return + } + + handler := s.Ability.Allowed + if s.interceptor != nil { + handler = func(ctx context.Context, req *AllowRequest) (*AllowReply, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*AllowRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*AllowRequest) when calling interceptor") + } + return s.Ability.Allowed(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*AllowReply) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*AllowReply) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *AllowReply + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *AllowReply and nil error while calling Allowed. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + +func (s *abilityServer) ServiceDescriptor() ([]byte, int) { + return twirpFileDescriptor0, 0 +} + +func (s *abilityServer) ProtocGenTwirpVersion() string { + return "v8.1.3" +} + +// PathPrefix returns the base service path, in the form: "/<prefix>/<package>.<Service>/" +// that is everything in a Twirp route except for the <Method>. This can be used for routing, +// for example to identify the requests that are targeted to this service in a mux. +func (s *abilityServer) PathPrefix() string { + return baseServicePath(s.pathPrefix, "authz.rpc", "Ability") +} + +// ===== +// Utils +// ===== + +// HTTPClient is the interface used by generated clients to send HTTP requests. +// It is fulfilled by *(net/http).Client, which is sufficient for most users. +// Users can provide their own implementation for special retry policies. +// +// HTTPClient implementations should not follow redirects. Redirects are +// automatically disabled if *(net/http).Client is passed to client +// constructors. See the withoutRedirects function in this file for more +// details. +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) +} + +// TwirpServer is the interface generated server structs will support: they're +// HTTP handlers with additional methods for accessing metadata about the +// service. Those accessors are a low-level API for building reflection tools. +// Most people can think of TwirpServers as just http.Handlers. +type TwirpServer interface { + http.Handler + + // ServiceDescriptor returns gzipped bytes describing the .proto file that + // this service was generated from. Once unzipped, the bytes can be + // unmarshalled as a + // google.golang.org/protobuf/types/descriptorpb.FileDescriptorProto. + // + // The returned integer is the index of this particular service within that + // FileDescriptorProto's 'Service' slice of ServiceDescriptorProtos. This is a + // low-level field, expected to be used for reflection. + ServiceDescriptor() ([]byte, int) + + // ProtocGenTwirpVersion is the semantic version string of the version of + // twirp used to generate this file. + ProtocGenTwirpVersion() string + + // PathPrefix returns the HTTP URL path prefix for all methods handled by this + // service. This can be used with an HTTP mux to route Twirp requests. + // The path prefix is in the form: "/<prefix>/<package>.<Service>/" + // that is, everything in a Twirp route except for the <Method> at the end. + PathPrefix() string +} + +func newServerOpts(opts []interface{}) *twirp.ServerOptions { + serverOpts := &twirp.ServerOptions{} + for _, opt := range opts { + switch o := opt.(type) { + case twirp.ServerOption: + o(serverOpts) + case *twirp.ServerHooks: // backwards compatibility, allow to specify hooks as an argument + twirp.WithServerHooks(o)(serverOpts) + case nil: // backwards compatibility, allow nil value for the argument + continue + default: + panic(fmt.Sprintf("Invalid option type %T, please use a twirp.ServerOption", o)) + } + } + return serverOpts +} + +// WriteError writes an HTTP response with a valid Twirp error format (code, msg, meta). +// Useful outside of the Twirp server (e.g. http middleware), but does not trigger hooks. +// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) +func WriteError(resp http.ResponseWriter, err error) { + writeError(context.Background(), resp, err, nil) +} + +// writeError writes Twirp errors in the response and triggers hooks. +func writeError(ctx context.Context, resp http.ResponseWriter, err error, hooks *twirp.ServerHooks) { + // Convert to a twirp.Error. Non-twirp errors are converted to internal errors. + var twerr twirp.Error + if !errors.As(err, &twerr) { + twerr = twirp.InternalErrorWith(err) + } + + statusCode := twirp.ServerHTTPStatusFromErrorCode(twerr.Code()) + ctx = ctxsetters.WithStatusCode(ctx, statusCode) + ctx = callError(ctx, hooks, twerr) + + respBody := marshalErrorToJSON(twerr) + + resp.Header().Set("Content-Type", "application/json") // Error responses are always JSON + resp.Header().Set("Content-Length", strconv.Itoa(len(respBody))) + resp.WriteHeader(statusCode) // set HTTP status code and send response + + _, writeErr := resp.Write(respBody) + if writeErr != nil { + // We have three options here. We could log the error, call the Error + // hook, or just silently ignore the error. + // + // Logging is unacceptable because we don't have a user-controlled + // logger; writing out to stderr without permission is too rude. + // + // Calling the Error hook would confuse users: it would mean the Error + // hook got called twice for one request, which is likely to lead to + // duplicated log messages and metrics, no matter how well we document + // the behavior. + // + // Silently ignoring the error is our least-bad option. It's highly + // likely that the connection is broken and the original 'err' says + // so anyway. + _ = writeErr + } + + callResponseSent(ctx, hooks) +} + +// sanitizeBaseURL parses the the baseURL, and adds the "http" scheme if needed. +// If the URL is unparsable, the baseURL is returned unchanged. +func sanitizeBaseURL(baseURL string) string { + u, err := url.Parse(baseURL) + if err != nil { + return baseURL // invalid URL will fail later when making requests + } + if u.Scheme == "" { + u.Scheme = "http" + } + return u.String() +} + +// baseServicePath composes the path prefix for the service (without <Method>). +// e.g.: baseServicePath("/twirp", "my.pkg", "MyService") +// +// returns => "/twirp/my.pkg.MyService/" +// +// e.g.: baseServicePath("", "", "MyService") +// +// returns => "/MyService/" +func baseServicePath(prefix, pkg, service string) string { + fullServiceName := service + if pkg != "" { + fullServiceName = pkg + "." + service + } + return path.Join("/", prefix, fullServiceName) + "/" +} + +// parseTwirpPath extracts path components form a valid Twirp route. +// Expected format: "[<prefix>]/<package>.<Service>/<Method>" +// e.g.: prefix, pkgService, method := parseTwirpPath("/twirp/pkg.Svc/MakeHat") +func parseTwirpPath(path string) (string, string, string) { + parts := strings.Split(path, "/") + if len(parts) < 2 { + return "", "", "" + } + method := parts[len(parts)-1] + pkgService := parts[len(parts)-2] + prefix := strings.Join(parts[0:len(parts)-2], "/") + return prefix, pkgService, method +} + +// getCustomHTTPReqHeaders retrieves a copy of any headers that are set in +// a context through the twirp.WithHTTPRequestHeaders function. +// If there are no headers set, or if they have the wrong type, nil is returned. +func getCustomHTTPReqHeaders(ctx context.Context) http.Header { + header, ok := twirp.HTTPRequestHeaders(ctx) + if !ok || header == nil { + return nil + } + copied := make(http.Header) + for k, vv := range header { + if vv == nil { + copied[k] = nil + continue + } + copied[k] = make([]string, len(vv)) + copy(copied[k], vv) + } + return copied +} + +// newRequest makes an http.Request from a client, adding common headers. +func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType string) (*http.Request, error) { + req, err := http.NewRequest("POST", url, reqBody) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if customHeader := getCustomHTTPReqHeaders(ctx); customHeader != nil { + req.Header = customHeader + } + req.Header.Set("Accept", contentType) + req.Header.Set("Content-Type", contentType) + req.Header.Set("Twirp-Version", "v8.1.3") + return req, nil +} + +// JSON serialization for errors +type twerrJSON struct { + Code string `json:"code"` + Msg string `json:"msg"` + Meta map[string]string `json:"meta,omitempty"` +} + +// marshalErrorToJSON returns JSON from a twirp.Error, that can be used as HTTP error response body. +// If serialization fails, it will use a descriptive Internal error instead. +func marshalErrorToJSON(twerr twirp.Error) []byte { + // make sure that msg is not too large + msg := twerr.Msg() + if len(msg) > 1e6 { + msg = msg[:1e6] + } + + tj := twerrJSON{ + Code: string(twerr.Code()), + Msg: msg, + Meta: twerr.MetaMap(), + } + + buf, err := json.Marshal(&tj) + if err != nil { + buf = []byte("{\"type\": \"" + twirp.Internal + "\", \"msg\": \"There was an error but it could not be serialized into JSON\"}") // fallback + } + + return buf +} + +// errorFromResponse builds a twirp.Error from a non-200 HTTP response. +// If the response has a valid serialized Twirp error, then it's returned. +// If not, the response status code is used to generate a similar twirp +// error. See twirpErrorFromIntermediary for more info on intermediary errors. +func errorFromResponse(resp *http.Response) twirp.Error { + statusCode := resp.StatusCode + statusText := http.StatusText(statusCode) + + if isHTTPRedirect(statusCode) { + // Unexpected redirect: it must be an error from an intermediary. + // Twirp clients don't follow redirects automatically, Twirp only handles + // POST requests, redirects should only happen on GET and HEAD requests. + location := resp.Header.Get("Location") + msg := fmt.Sprintf("unexpected HTTP status code %d %q received, Location=%q", statusCode, statusText, location) + return twirpErrorFromIntermediary(statusCode, msg, location) + } + + respBodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return wrapInternal(err, "failed to read server error response body") + } + + var tj twerrJSON + dec := json.NewDecoder(bytes.NewReader(respBodyBytes)) + dec.DisallowUnknownFields() + if err := dec.Decode(&tj); err != nil || tj.Code == "" { + // Invalid JSON response; it must be an error from an intermediary. + msg := fmt.Sprintf("Error from intermediary with HTTP status code %d %q", statusCode, statusText) + return twirpErrorFromIntermediary(statusCode, msg, string(respBodyBytes)) + } + + errorCode := twirp.ErrorCode(tj.Code) + if !twirp.IsValidErrorCode(errorCode) { + msg := "invalid type returned from server error response: " + tj.Code + return twirp.InternalError(msg).WithMeta("body", string(respBodyBytes)) + } + + twerr := twirp.NewError(errorCode, tj.Msg) + for k, v := range tj.Meta { + twerr = twerr.WithMeta(k, v) + } + return twerr +} + +// twirpErrorFromIntermediary maps HTTP errors from non-twirp sources to twirp errors. +// The mapping is similar to gRPC: https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md. +// Returned twirp Errors have some additional metadata for inspection. +func twirpErrorFromIntermediary(status int, msg string, bodyOrLocation string) twirp.Error { + var code twirp.ErrorCode + if isHTTPRedirect(status) { // 3xx + code = twirp.Internal + } else { + switch status { + case 400: // Bad Request + code = twirp.Internal + case 401: // Unauthorized + code = twirp.Unauthenticated + case 403: // Forbidden + code = twirp.PermissionDenied + case 404: // Not Found + code = twirp.BadRoute + case 429: // Too Many Requests + code = twirp.ResourceExhausted + case 502, 503, 504: // Bad Gateway, Service Unavailable, Gateway Timeout + code = twirp.Unavailable + default: // All other codes + code = twirp.Unknown + } + } + + twerr := twirp.NewError(code, msg) + twerr = twerr.WithMeta("http_error_from_intermediary", "true") // to easily know if this error was from intermediary + twerr = twerr.WithMeta("status_code", strconv.Itoa(status)) + if isHTTPRedirect(status) { + twerr = twerr.WithMeta("location", bodyOrLocation) + } else { + twerr = twerr.WithMeta("body", bodyOrLocation) + } + return twerr +} + +func isHTTPRedirect(status int) bool { + return status >= 300 && status <= 399 +} + +// wrapInternal wraps an error with a prefix as an Internal error. +// The original error cause is accessible by github.com/pkg/errors.Cause. +func wrapInternal(err error, prefix string) twirp.Error { + return twirp.InternalErrorWith(&wrappedError{prefix: prefix, cause: err}) +} + +type wrappedError struct { + prefix string + cause error +} + +func (e *wrappedError) Error() string { return e.prefix + ": " + e.cause.Error() } +func (e *wrappedError) Unwrap() error { return e.cause } // for go1.13 + errors.Is/As +func (e *wrappedError) Cause() error { return e.cause } // for github.com/pkg/errors + +// ensurePanicResponses makes sure that rpc methods causing a panic still result in a Twirp Internal +// error response (status 500), and error hooks are properly called with the panic wrapped as an error. +// The panic is re-raised so it can be handled normally with middleware. +func ensurePanicResponses(ctx context.Context, resp http.ResponseWriter, hooks *twirp.ServerHooks) { + if r := recover(); r != nil { + // Wrap the panic as an error so it can be passed to error hooks. + // The original error is accessible from error hooks, but not visible in the response. + err := errFromPanic(r) + twerr := &internalWithCause{msg: "Internal service panic", cause: err} + // Actually write the error + writeError(ctx, resp, twerr, hooks) + // If possible, flush the error to the wire. + f, ok := resp.(http.Flusher) + if ok { + f.Flush() + } + + panic(r) + } +} + +// errFromPanic returns the typed error if the recovered panic is an error, otherwise formats as error. +func errFromPanic(p interface{}) error { + if err, ok := p.(error); ok { + return err + } + return fmt.Errorf("panic: %v", p) +} + +// internalWithCause is a Twirp Internal error wrapping an original error cause, +// but the original error message is not exposed on Msg(). The original error +// can be checked with go1.13+ errors.Is/As, and also by (github.com/pkg/errors).Unwrap +type internalWithCause struct { + msg string + cause error +} + +func (e *internalWithCause) Unwrap() error { return e.cause } // for go1.13 + errors.Is/As +func (e *internalWithCause) Cause() error { return e.cause } // for github.com/pkg/errors +func (e *internalWithCause) Error() string { return e.msg + ": " + e.cause.Error() } +func (e *internalWithCause) Code() twirp.ErrorCode { return twirp.Internal } +func (e *internalWithCause) Msg() string { return e.msg } +func (e *internalWithCause) Meta(key string) string { return "" } +func (e *internalWithCause) MetaMap() map[string]string { return nil } +func (e *internalWithCause) WithMeta(key string, val string) twirp.Error { return e } + +// malformedRequestError is used when the twirp server cannot unmarshal a request +func malformedRequestError(msg string) twirp.Error { + return twirp.NewError(twirp.Malformed, msg) +} + +// badRouteError is used when the twirp server cannot route a request +func badRouteError(msg string, method, url string) twirp.Error { + err := twirp.NewError(twirp.BadRoute, msg) + err = err.WithMeta("twirp_invalid_route", method+" "+url) + return err +} + +// withoutRedirects makes sure that the POST request can not be redirected. +// The standard library will, by default, redirect requests (including POSTs) if it gets a 302 or +// 303 response, and also 301s in go1.8. It redirects by making a second request, changing the +// method to GET and removing the body. This produces very confusing error messages, so instead we +// set a redirect policy that always errors. This stops Go from executing the redirect. +// +// We have to be a little careful in case the user-provided http.Client has its own CheckRedirect +// policy - if so, we'll run through that policy first. +// +// Because this requires modifying the http.Client, we make a new copy of the client and return it. +func withoutRedirects(in *http.Client) *http.Client { + copy := *in + copy.CheckRedirect = func(req *http.Request, via []*http.Request) error { + if in.CheckRedirect != nil { + // Run the input's redirect if it exists, in case it has side effects, but ignore any error it + // returns, since we want to use ErrUseLastResponse. + err := in.CheckRedirect(req, via) + _ = err // Silly, but this makes sure generated code passes errcheck -blank, which some people use. + } + return http.ErrUseLastResponse + } + return © +} + +// doProtobufRequest makes a Protobuf request to the remote Twirp service. +func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (_ context.Context, err error) { + reqBodyBytes, err := proto.Marshal(in) + if err != nil { + return ctx, wrapInternal(err, "failed to marshal proto request") + } + reqBody := bytes.NewBuffer(reqBodyBytes) + if err = ctx.Err(); err != nil { + return ctx, wrapInternal(err, "aborted because context was done") + } + + req, err := newRequest(ctx, url, reqBody, "application/protobuf") + if err != nil { + return ctx, wrapInternal(err, "could not build request") + } + ctx, err = callClientRequestPrepared(ctx, hooks, req) + if err != nil { + return ctx, err + } + + req = req.WithContext(ctx) + resp, err := client.Do(req) + if err != nil { + return ctx, wrapInternal(err, "failed to do request") + } + defer func() { _ = resp.Body.Close() }() + + if err = ctx.Err(); err != nil { + return ctx, wrapInternal(err, "aborted because context was done") + } + + if resp.StatusCode != 200 { + return ctx, errorFromResponse(resp) + } + + respBodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return ctx, wrapInternal(err, "failed to read response body") + } + if err = ctx.Err(); err != nil { + return ctx, wrapInternal(err, "aborted because context was done") + } + + if err = proto.Unmarshal(respBodyBytes, out); err != nil { + return ctx, wrapInternal(err, "failed to unmarshal proto response") + } + return ctx, nil +} + +// doJSONRequest makes a JSON request to the remote Twirp service. +func doJSONRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (_ context.Context, err error) { + marshaler := &protojson.MarshalOptions{UseProtoNames: true} + reqBytes, err := marshaler.Marshal(in) + if err != nil { + return ctx, wrapInternal(err, "failed to marshal json request") + } + if err = ctx.Err(); err != nil { + return ctx, wrapInternal(err, "aborted because context was done") + } + + req, err := newRequest(ctx, url, bytes.NewReader(reqBytes), "application/json") + if err != nil { + return ctx, wrapInternal(err, "could not build request") + } + ctx, err = callClientRequestPrepared(ctx, hooks, req) + if err != nil { + return ctx, err + } + + req = req.WithContext(ctx) + resp, err := client.Do(req) + if err != nil { + return ctx, wrapInternal(err, "failed to do request") + } + + defer func() { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = wrapInternal(cerr, "failed to close response body") + } + }() + + if err = ctx.Err(); err != nil { + return ctx, wrapInternal(err, "aborted because context was done") + } + + if resp.StatusCode != 200 { + return ctx, errorFromResponse(resp) + } + + d := json.NewDecoder(resp.Body) + rawRespBody := json.RawMessage{} + if err := d.Decode(&rawRespBody); err != nil { + return ctx, wrapInternal(err, "failed to unmarshal json response") + } + unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} + if err = unmarshaler.Unmarshal(rawRespBody, out); err != nil { + return ctx, wrapInternal(err, "failed to unmarshal json response") + } + if err = ctx.Err(); err != nil { + return ctx, wrapInternal(err, "aborted because context was done") + } + return ctx, nil +} + +// Call twirp.ServerHooks.RequestReceived if the hook is available +func callRequestReceived(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) { + if h == nil || h.RequestReceived == nil { + return ctx, nil + } + return h.RequestReceived(ctx) +} + +// Call twirp.ServerHooks.RequestRouted if the hook is available +func callRequestRouted(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) { + if h == nil || h.RequestRouted == nil { + return ctx, nil + } + return h.RequestRouted(ctx) +} + +// Call twirp.ServerHooks.ResponsePrepared if the hook is available +func callResponsePrepared(ctx context.Context, h *twirp.ServerHooks) context.Context { + if h == nil || h.ResponsePrepared == nil { + return ctx + } + return h.ResponsePrepared(ctx) +} + +// Call twirp.ServerHooks.ResponseSent if the hook is available +func callResponseSent(ctx context.Context, h *twirp.ServerHooks) { + if h == nil || h.ResponseSent == nil { + return + } + h.ResponseSent(ctx) +} + +// Call twirp.ServerHooks.Error if the hook is available +func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) context.Context { + if h == nil || h.Error == nil { + return ctx + } + return h.Error(ctx, err) +} + +func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) { + if h == nil || h.ResponseReceived == nil { + return + } + h.ResponseReceived(ctx) +} + +func callClientRequestPrepared(ctx context.Context, h *twirp.ClientHooks, req *http.Request) (context.Context, error) { + if h == nil || h.RequestPrepared == nil { + return ctx, nil + } + return h.RequestPrepared(ctx, req) +} + +func callClientError(ctx context.Context, h *twirp.ClientHooks, err twirp.Error) { + if h == nil || h.Error == nil { + return + } + h.Error(ctx, err) +} + +var twirpFileDescriptor0 = []byte{ + // 196 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4d, 0x4c, 0xca, 0xcc, + 0xc9, 0x2c, 0xa9, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4c, 0x2c, 0x2d, 0xc9, 0xa8, + 0xd2, 0x2b, 0x2a, 0x48, 0x56, 0x4a, 0xe1, 0xe2, 0x71, 0xcc, 0xc9, 0xc9, 0x2f, 0x0f, 0x4a, 0x2d, + 0x2c, 0x4d, 0x2d, 0x2e, 0x11, 0x92, 0xe0, 0x62, 0x2f, 0x2e, 0x4d, 0xca, 0x4a, 0x4d, 0x2e, 0x91, + 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x82, 0x71, 0x85, 0xe4, 0xb8, 0xb8, 0x0a, 0x52, 0x8b, 0x72, + 0x33, 0x8b, 0x8b, 0x33, 0xf3, 0xf3, 0x24, 0x98, 0xc0, 0x92, 0x48, 0x22, 0x42, 0x52, 0x5c, 0x1c, + 0x45, 0xa9, 0xc5, 0xf9, 0xa5, 0x45, 0xc9, 0xa9, 0x12, 0xcc, 0x60, 0x59, 0x38, 0x5f, 0x49, 0x85, + 0x8b, 0x0b, 0x6a, 0x4b, 0x41, 0x4e, 0xa5, 0x90, 0x18, 0x17, 0x5b, 0x51, 0x6a, 0x71, 0x69, 0x0e, + 0xc4, 0x0a, 0x8e, 0x20, 0x28, 0xcf, 0xc8, 0x8d, 0x8b, 0xdd, 0x11, 0xe2, 0x4e, 0x21, 0x6b, 0x2e, + 0x76, 0xb0, 0x86, 0xd4, 0x14, 0x21, 0x71, 0x3d, 0xb8, 0x6b, 0xf5, 0x90, 0x9d, 0x2a, 0x25, 0x8a, + 0x29, 0x51, 0x90, 0x53, 0xa9, 0xc4, 0xe0, 0xc4, 0x19, 0xc5, 0x5e, 0x90, 0x9d, 0xae, 0x5f, 0x54, + 0x90, 0x9c, 0xc4, 0x06, 0xf6, 0xb0, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x72, 0x35, 0x46, 0x7c, + 0x01, 0x01, 0x00, 0x00, +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 95a977f..3819a2c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -67,7 +67,7 @@ github.com/coreos/go-oidc/v3/oidc # github.com/cpuguy83/dockercfg v0.3.2 ## explicit; go 1.13 github.com/cpuguy83/dockercfg -# github.com/davecgh/go-spew v1.1.1 +# github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc ## explicit github.com/davecgh/go-spew/spew # github.com/deckarep/golang-set/v2 v2.8.0 @@ -339,7 +339,7 @@ github.com/planetscale/vtprotobuf/types/known/wrapperspb github.com/playwright-community/playwright-go github.com/playwright-community/playwright-go/cmd/playwright github.com/playwright-community/playwright-go/internal/safe -# github.com/pmezard/go-difflib v1.0.0 +# github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 ## explicit github.com/pmezard/go-difflib/difflib # github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 @@ -395,6 +395,11 @@ github.com/tklauser/go-sysconf # github.com/tklauser/numcpus v0.10.0 ## explicit; go 1.23.0 github.com/tklauser/numcpus +# github.com/twitchtv/twirp v8.1.3+incompatible +## explicit +github.com/twitchtv/twirp +github.com/twitchtv/twirp/ctxsetters +github.com/twitchtv/twirp/internal/contextkeys # github.com/xlgmokha/x v0.0.0-20250523153843-ded39aa54bc5 ## explicit; go 1.24 github.com/xlgmokha/x/pkg/context @@ -414,6 +419,9 @@ github.com/yusufpapurcu/wmi # github.com/zeebo/errs v1.4.0 ## explicit; go 1.12 github.com/zeebo/errs +# gitlab.com/gitlab-org/software-supply-chain-security/authorization/authzd.git v0.0.0-20250523230007-aebf1323e470 +## explicit; go 1.24 +gitlab.com/gitlab-org/software-supply-chain-security/authorization/authzd.git/pkg/rpc # go.opentelemetry.io/auto/sdk v1.1.0 ## explicit; go 1.22.0 go.opentelemetry.io/auto/sdk |
