summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-05-02 14:29:41 -0600
committermo khan <mo@mokhan.ca>2025-05-02 14:29:41 -0600
commitc583bcd1473205104a1e1af812ed4976d30c7baa (patch)
tree933edf78a4ac8aea55256e42641e56bbb4c58834
parent91defaefca47e9cebbe92c6abf33c4423df9bc7d (diff)
refactor: remove anything unrelated to the authz daemon
-rw-r--r--.gitlab-ci.yml49
-rw-r--r--.tool-versions2
-rw-r--r--Dockerfile14
-rw-r--r--Makefile11
-rw-r--r--README.md107
-rwxr-xr-xbin/api182
-rwxr-xr-xbin/idp895
-rwxr-xr-xbin/ui503
-rw-r--r--casbin.conf17
-rw-r--r--casbin.csv14
-rw-r--r--cmd/authzd/main.go15
-rw-r--r--cmd/gtwy/main.go12
-rw-r--r--db/api/groups.csv9
-rw-r--r--db/api/organizations.csv3
-rw-r--r--db/api/projects.csv9
-rw-r--r--db/idp/users.csv2
-rw-r--r--doc/share/authz/ABAC.md57
-rw-r--r--doc/share/authz/DESIGN.md194
-rw-r--r--doc/share/authz/EXPERIMENT.md42
-rw-r--r--doc/share/authz/FAQ.md33
-rw-r--r--doc/share/authz/POLICY.md124
-rw-r--r--doc/share/authz/RBAC.md103
-rw-r--r--doc/share/authz/README.md69
-rw-r--r--doc/share/authz/ReBAC.md149
-rw-r--r--doc/share/authz/SLIDES.md207
-rw-r--r--doc/share/authz/ZANZIBAR.md51
-rw-r--r--doc/share/authz/todo/001_ldap.md19
-rw-r--r--doc/share/authz/todo/002_hierarchical_membership.md19
-rw-r--r--doc/share/authz/todo/003_workload_identity_federation.md21
-rw-r--r--doc/share/authz/todo/004_acme_protocol.md69
-rw-r--r--doc/share/authz/todo/005_open_pubkey.md20
-rw-r--r--doc/share/authz/todo/006_zero_knowledge_proof.md17
-rw-r--r--go.mod170
-rw-r--r--go.sum463
-rw-r--r--lib/.keep0
-rw-r--r--lib/authx.rb6
-rw-r--r--lib/authx/rpc.rb4
-rw-r--r--lib/authx/rpc/ability_pb.rb24
-rw-r--r--lib/authx/rpc/ability_twirp.rb17
-rw-r--r--magefiles/magefile.go52
-rw-r--r--magefiles/step.go147
-rw-r--r--pkg/app/app.go25
-rw-r--r--pkg/app/routes.go18
-rw-r--r--pkg/authz/authz.go23
-rw-r--r--pkg/authz/casbin.go43
-rw-r--r--pkg/authz/cedar.go34
-rw-r--r--pkg/authz/token.go30
-rw-r--r--pkg/cfg/cfg.go34
-rw-r--r--pkg/cfg/mux.go11
-rw-r--r--pkg/cfg/option.go3
-rw-r--r--pkg/cfg/tls.go75
-rw-r--r--pkg/policies/policies_test.go2
-rw-r--r--pkg/prxy/prxy.go43
-rw-r--r--pkg/prxy/prxy_test.go49
-rw-r--r--pkg/rpc/ability.pb.go12
-rw-r--r--pkg/rpc/ability.twirp.go (renamed from pkg/rpc/gitlab.com/mokhax/spike/pkg/rpc/ability.twirp.go)45
-rw-r--r--pkg/rpc/ability_grpc.pb.go121
-rw-r--r--pkg/rpc/ability_service.go5
-rw-r--r--pkg/rpc/server.go20
-rw-r--r--pkg/rpc/server_test.go26
-rw-r--r--pkg/srv/srv.go26
-rw-r--r--pkg/test/test.go49
-rw-r--r--protos/ability.proto4
-rwxr-xr-xscript/cibuild15
-rw-r--r--test/e2e_test.go329
65 files changed, 191 insertions, 4772 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 449564fa..d2a608fd 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,16 +1,35 @@
-test:
- image: debian:bookworm
+stages:
+ - build
+ - test
+ - validate
+ - runway_staging
+ - runway_production
+
+variables:
+ CONTAINER_IMAGE_COMMIT: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
+
+include:
+ - project: 'gitlab-com/gl-infra/platform/runway/runwayctl'
+ file: 'ci-tasks/service-project/runway.yml'
+ inputs:
+ runway_service_id: authzd
+ image: "$CONTAINER_IMAGE_COMMIT"
+ runway_version: v3.61.5
+
+build image:
+ image: docker:28
+ interruptible: true
+ stage: build
+ services:
+ - docker:28-dind
+ before_script:
+ - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
script:
- - apt-get update -y
- - apt-get install -y build-essential chromium firefox-esr curl libffi-dev zlib1g-dev libyaml-dev
- - curl https://mise.run | sh
- - ~/.local/bin/mise install
- - echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.bashrc
- - source ~/.bashrc
- - echo '127.0.0.1 api.example.com' >> /etc/hosts
- - echo '127.0.0.1 idp.example.com' >> /etc/hosts
- - echo '127.0.0.1 ui.example.com' >> /etc/hosts
- - gem update --system
- - gem install bundler
- - make
- - ruby ./script/cibuild
+ - docker build --tag $CONTAINER_IMAGE_COMMIT .
+ - docker push $CONTAINER_IMAGE_COMMIT
+
+unit:
+ image: golang:alpine
+ stage: test
+ script:
+ - go test ./...
diff --git a/.tool-versions b/.tool-versions
index b141417e..eb4df2a3 100644
--- a/.tool-versions
+++ b/.tool-versions
@@ -1,5 +1,5 @@
cargo latest
golang 1.24.0
+make 4.4.1
protoc 3.19.6
-ruby 3.4.2
rust stable
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..d4e2b99d
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,14 @@
+# syntax=docker/dockerfile:1
+FROM golang:1.24.0 AS build
+ENV CGO_ENABLED=0
+WORKDIR /app
+COPY . ./
+RUN go build -o authzd ./cmd/authzd/main.go && mv ./authzd /bin/authzd
+
+
+FROM scratch
+ENV BIND_ADDR=":http"
+EXPOSE 80
+WORKDIR /var/www/
+COPY --from=build /bin/authzd /bin/authzd
+CMD ["/bin/authzd"]
diff --git a/Makefile b/Makefile
index 718a68b3..3bf54c6a 100644
--- a/Makefile
+++ b/Makefile
@@ -12,14 +12,3 @@ install-tools:
@command -v protoc-gen-go
@command -v protoc-gen-go-grpc
@command -v protoc-gen-twirp_ruby
- @command -v step
- @command -v step-ca
-
-clean:
- @rm -f slides.pdf
-
-slides.pdf: clean
- @pandoc -t beamer -V fontsize=8pt -s doc/share/authz/SLIDES.md -o slides.pdf
-
-presentation: slides.pdf
- @xdg-open slides.pdf
diff --git a/README.md b/README.md
index a8da10cc..32a926d2 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,7 @@
-# Authx - Proof of Concept
+# authzd - Authorization Daemon
-This repository explores various authentication and authorization workflows by introducing a separate authn/authz service.
-It serves as a proof of concept to illustrate potential workflows, not a production ready implementation.
-
-To keep the implementation accessible, external dependencies have been minimized, ensuring a clear reference for understanding key concepts, including:
-
-* SAML based authentication including IdP chaining to external identity providers
-* OIDC based authentication
-* OAuth endpoints with links to relevant RFCs for proper usage guidance
+This repository host a minimal PDP service used for making authorization
+decisions via a gRPC endpoint.
## Architecture
@@ -17,84 +11,57 @@ To keep the implementation accessible, external dependencies have been minimized
-------------
|
V
-----|:8080|-----------------------------------------------
+----|:443|-------------------------------------------------------------
|
V
- ---------------
- | API Gateway | (use casbin to evict early, reverse proxy, inject context headers)
- ---------------
- |
- | --------------------
- |--->| IdP (saml, oidc) |
- | --------------------
- | | :http | :grpc | (use declarative_policy)
- | --------------------
- | A A
- ----------- | |
- | | | |
- V V | |
- ------ ------------ |
- | UI | | REST API |----|
- ------ ------------
-
-[UI]: ui.example.com
-[REST API]: api.example.com
-[IdP]: idp.example.com
+ | ----------------------------------------
+ | | ______________________ |
+ |-->(:443)| envoy ->(:80)| /var/run/sparkled | |
+ | | ---------------------- |
+ | ----------------------------------------
+ |
+ | -------------------
+ | | IdP / IAM |
+ | |-------- |
+ |--->| :http | |---- pub membership change ---|
+ | |-------- | |
+ | ------------------- |
+ | ( nats.io)
+ | |
+ | |---- sub membership change ---
+ | |
+ | V (update acls)
+ | -------------------------------------
+ | | ___________________ |
+ |-->(:443)| envoy ->(:80)| /var/run/authzd | |
+ | ------------------| |
+ -------------------------------------
+ |
+ |----> cron reconciliation process >---->-------|
+ A |
+ |- audit, validate list, notify, remediate --|
```
-I have ommitted TLS, RS256 from the prototype to offload the decision of key
-management and rotation. See [smallstep](https://smallstep.com/docs/step-cli/)
-for PKI management.
-
-CSV files are used instead of a database to simulate different types of
-scenarios. The following organizational hierarchy is demonstrated here:
+## Getting Started
-```
-Organization(name: "default")
- * Group(name: "A")
- * Project(name: "A1")
- * Group(name: "B")
- * Project(name: "B1")
-Organization(name: "gitlab")
- * Group(name: "gitlab-org")
- * Project(name: "gitlab")
- * Group(name: "gitlab-com")
- * Group(name: "gl-security")
- * Group(name: "test-projects")
- * Project(name: "eicar-test-project")
- * Project(name: "disclosures")
- * Group(name: "support")
- * Group(name: "toolbox")
- * Project(name: "changelog-parser")
- * Project(name: "handbook")
- * Project(name: "www-gitlab-com")
-```
+### Prerequisites
-## Getting Started
+- [mise](https://mise.jdx.dev/)
+- [make](https://www.gnu.org/software/make/)
1. Install tools:
```sh
$ mise install
- ```
-
-1. Add entries to `/etc/hosts`:
-
- ```sh
- $ tail -n3 /etc/hosts
- 127.0.0.1 api.example.com
- 127.0.0.1 idp.example.com
- 127.0.0.1 ui.example.com
+ $ make install-tools
```
1. Start servers:
```sh
- $ mage
+ $ mage servers
```
-1. Open a browser to `http://ui.example.com:8080/`.
-
## Questions
See the [FAQ][9]
@@ -106,9 +73,7 @@ See the [FAQ][9]
* [protocol buffers][7]
* [twirp][8]
-[2]: https://gitlab.com/gitlab-org/gitlab/-/tree/master/app/policies
[5]: https://tip.golang.org/doc/modules/managing-dependencies#tools
[6]: https://grpc.io/docs/
[7]: https://protobuf.dev/programming-guides/proto3/
[8]: https://github.com/arthurnn/twirp-ruby/wiki/Code-Generation
-[9]: ./doc/share/authz/FAQ.md
diff --git a/bin/api b/bin/api
deleted file mode 100755
index 180aa874..00000000
--- a/bin/api
+++ /dev/null
@@ -1,182 +0,0 @@
-#!/usr/bin/env ruby
-
-require 'bundler/inline'
-
-gemfile do
- source 'https://rubygems.org'
-
- gem "csv", "~> 3.0"
- gem "declarative_policy", "~> 1.0"
- gem "erb", "~> 4.0"
- gem "globalid", "~> 1.0"
- gem "google-protobuf", "~> 3.0"
- gem "json", "~> 2.0"
- gem "logger", "~> 1.0"
- gem "rack", "~> 3.0"
- gem "rackup", "~> 2.0"
- gem "securerandom", "~> 0.1"
- gem "twirp", "~> 1.0"
- gem "webrick", "~> 1.0"
-end
-
-lib_path = Pathname.new(__FILE__).parent.parent.join('lib').realpath.to_s
-$LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
-
-require 'authx/rpc'
-
-$scheme = ENV.fetch("SCHEME", "http")
-$port = ENV.fetch("PORT", 8284).to_i
-$host = ENV.fetch("HOST", "localhost:#{$port}")
-
-class Entity
- class << self
- def all
- @items ||= ::CSV.read(File.join(__dir__, "../db/api/#{self.name.downcase}s.csv"), headers: true).map do |row|
- new(row.to_h.transform_keys(&:to_sym))
- end
- end
-
- def create!(attributes)
- new({ id: SecureRandom.uuid }.merge(attributes)).tap do |item|
- all << item
- end
- end
- end
-
- def initialize(attributes = {})
- @attributes = attributes
- end
-
- def id
- self[:id]
- end
-
- def [](attribute)
- @attributes.fetch(attribute.to_sym)
- end
-
- def to_h
- @attributes
- end
-
- def to_gid
- ::GlobalID.create(self, app: "example")
- end
-end
-
-class Organization < Entity
- class << self
- def default
- @default ||= all.find { |organization| organization[:name] == "default" }
- end
- end
-end
-
-class Group < Entity
-end
-
-class Project < Entity
-end
-
-module HTTPHelpers
- def authorized?(request, permission, resource)
- raise [permission, resource].inspect if resource.nil?
- authorization = Rack::Auth::AbstractRequest.new(request.env)
- return false unless authorization.provided?
-
- response = rpc.allowed({
- subject: authorization.params,
- permission: permission,
- resource: resource.to_gid.to_s,
- }, headers: { 'Authorization' => "Bearer #{authorization.params}"})
- puts [response&.data&.result, permission, resource.to_gid.to_s].inspect
- response.error.nil? && response.data.result
- end
-
- def json_not_found
- http_response(code: 404)
- end
-
- def json_ok(body)
- http_response(code: 200, body: JSON.pretty_generate(body))
- end
-
- def json_created(body)
- http_response(code: 201, body: JSON.pretty_generate(body.to_h))
- end
-
- def json_unauthorized(permission, resource)
- http_response(code: 401, body: JSON.pretty_generate({
- error: {
- code: 401,
- message: "`#{permission}` is required on `#{resource.to_gid}`",
- }
- }))
- end
-
- def http_response(code:, headers: { 'Content-Type' => 'application/json' }, body: nil)
- [
- code,
- headers.merge({ 'X-Backend-Server' => 'REST' }),
- [body].compact
- ]
- end
-end
-
-class API
- include HTTPHelpers
-
- attr_reader :rpc
-
- def initialize
- @rpc = ::Authx::Rpc::AbilityClient.new("http://idp.example.com:8080/twirp")
- end
-
- def call(env)
- request = Rack::Request.new(env)
- case request.request_method
- when Rack::GET
- case request.path
- when "/organizations", "/organizations.json"
- return json_ok(Organization.all.map(&:to_h))
- when "/groups", "/groups.json"
- resource = Organization.default
- if authorized?(request, :read_group, resource)
- return json_ok(Group.all.map(&:to_h))
- else
- return json_unauthorized(:read_group, resource)
- end
- when "/projects", "/projects.json"
- resource = Organization.default
- if authorized?(request, :read_project, resource)
- return json_ok(Project.all.map(&:to_h))
- else
- return json_unauthorized(:read_project, resource)
- end
- end
- when Rack::POST
- case request.path
- when "/projects", "/projects.json"
- resource = Organization.default
- if authorized?(request, :create_project, resource)
- return json_created(Project.create!(JSON.parse(request.body.read, symbolize_names: true)))
- else
- return json_unauthorized(:create_project, resource)
- end
- end
- end
- json_not_found
- end
-
- private
-end
-
-if __FILE__ == $0
- app = Rack::Builder.new do
- use Rack::CommonLogger
- use Rack::Reloader
- run API.new
- end.to_app
-
- Rackup::Server.start(app: app, Port: $port)
-end
diff --git a/bin/idp b/bin/idp
deleted file mode 100755
index 62462deb..00000000
--- a/bin/idp
+++ /dev/null
@@ -1,895 +0,0 @@
-#!/usr/bin/env ruby
-
-require "bundler/inline"
-
-gemfile do
- source "https://rubygems.org"
-
- gem "base64", "~> 0.1"
- gem "bcrypt", "~> 3.0"
- gem "csv", "~> 3.0"
- gem "declarative_policy", "~> 1.0"
- gem "erb", "~> 4.0"
- gem "globalid", "~> 1.0"
- gem "google-protobuf", "~> 3.0"
- gem "rack", "~> 3.0"
- gem "rack-session", "~> 2.0"
- gem "rackup", "~> 2.0"
- gem "saml-kit", "1.4.0"
- gem "twirp", "~> 1.0"
- gem "warden", "~> 1.0"
- gem "webrick", "~> 1.0"
-end
-
-lib_path = Pathname.new(__FILE__).parent.parent.join('lib').realpath.to_s
-$LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
-
-require 'authx/rpc'
-
-$scheme = ENV.fetch("SCHEME", "http")
-$port = ENV.fetch("PORT", 8282).to_i
-$host = ENV.fetch("HOST", "localhost:#{$port}")
-
-DeclarativePolicy.configure do
- name_transformation do |name|
- "::Authz::#{name}Policy"
- end
-end
-
-Warden::Manager.serialize_into_session do |user|
- user.id
-end
-
-Warden::Manager.serialize_from_session do |id|
- ::Authn::User.find(id)
-end
-
-Warden::Strategies.add(:password) do
- def valid?
- params['username'] && params['password']
- end
-
- def authenticate!
- user = ::Authn::User.login(params.transform_keys(&:to_sym))
- user.nil? ? fail!("Could not log in") : success!(user)
- end
-end
-
-module HTTPHelpers
- def current_user?(request)
- request.env['warden'].authenticated?
- end
-
- def current_user(request)
- request.env['warden'].user
- end
-
- def default_headers
- {
- 'X-Powered-By' => 'IdP'
- }
- end
-
- def http_not_found
- [404, default_headers, []]
- end
-
- def http_ok(headers = {}, body = nil)
- [200, default_headers.merge(headers), [body]]
- end
-
- def http_redirect_to(location)
- [302, { 'Location' => "#{$scheme}://#{$host}#{location}" }, []]
- end
-end
-
-module Authn
- class User
- include ::BCrypt
-
- class << self
- def all
- @all ||= ::CSV.read(File.join(__dir__, "../db/idp/users.csv"), headers: true).map do |row|
- new(row.to_h.transform_keys(&:to_sym))
- end
- end
-
- def find(id)
- all.find do |user|
- user[:id] == id
- end
- end
-
- def find_by
- all.find do |user|
- yield user
- end
- end
-
- def find_by_email(email)
- find_by do |user|
- user[:email] == email
- end
- end
-
- def find_by_username(username)
- find_by do |user|
- user[:username] == username
- end
- end
-
- def login(params = {})
- user = find_by_username(params[:username])
- user&.valid_password?(params[:password]) ? user : nil
- end
- end
-
- attr_reader :id
-
- def initialize(attributes)
- @attributes = attributes
- @id = self[:id]
- end
-
- def [](attribute)
- @attributes.fetch(attribute.to_sym)
- end
-
- def name_id_for(name_id_format)
- if name_id_format == Saml::Kit::Namespaces::EMAIL_ADDRESS
- self[:email]
- else
- self[:id]
- end
- end
-
- def create_access_token
- ::Authz::JWT.new(
- sub: to_global_id.to_s,
- auth_time: Time.now.to_i,
- email: self[:email],
- username: self[:username],
- )
- end
-
- def create_id_token
- ::Authz::JWT.new(sub: to_global_id.to_s)
- end
-
- def assertion_attributes_for(request)
- {
- email: self[:email],
- }
- end
-
- def valid_password?(entered_password)
- ::BCrypt::Password.new(self[:password_digest]) == entered_password
- end
-
- def to_global_id
- ::GlobalID.new(
- ::URI::GID.build(
- app: "example",
- model_name: "User",
- model_id: id,
- params: {}
- )
- ).to_s
- end
- end
-
- class OnDemandRegistry < Saml::Kit::DefaultRegistry
- def metadata_for(entity_id)
- found = super(entity_id)
- return found if found
-
- register_url(entity_id, verify_ssl: false)
- super(entity_id)
- end
- end
-
- class SessionsController
- include ::HTTPHelpers
-
- def call(env)
- request = Rack::Request.new(env)
- case request.request_method
- when Rack::GET
- case request.path
- when '/sessions/new'
- return get_login(request)
- end
- when Rack::POST
- case request.path
- when '/sessions'
- if (user = env['warden'].authenticate(:password))
- path = request.params["redirect_back"] ? request.params["redirect_back"] : "/"
- return http_redirect_to(path)
- else
- return http_redirect_to("/sessions/new")
- end
- when '/sessions/delete'
- request.env['warden'].logout
- return http_redirect_to('/')
- end
- end
-
- http_not_found
- end
-
- private
-
- def get_login(request)
- template = <<~ERB
- <!doctype html>
- <html lang="en" data-theme="light">
- <head>
- <title>IdP</title>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <meta name="color-scheme" content="light dark">
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
- </head>
- <body>
- <main class="container">
- <nav>
- <ul>
- <li><strong>IdP</strong></li>
- <li><a href="http://ui.example.com:8080/">UI</a></li>
- </ul>
- </nav>
-
- <form id="login-form" action="/sessions" method="post">
- <fieldset>
- <label>
- Username
- <input type="input" placeholder="Username" id="username" name="username" value="" />
- </label>
- <label>
- Password
- <input type="password" placeholder="Password" id="password" name="password" value="" />
- </label>
- </fieldset>
-
- <input type="hidden" name="redirect_back" value="<%= request.params["redirect_back"] %>" />
- <input type="submit" id="login-button" value="Login" />
- </form>
- </main>
- </body>
- </html>
- ERB
- html = ERB.new(template, trim_mode: '-').result(binding)
- [200, { 'Content-Type' => "text/html" }, [html]]
- end
- end
-
- class SAMLController
- include ::HTTPHelpers
-
- def initialize(scheme, host)
- Saml::Kit.configure do |x|
- x.entity_id = "#{$scheme}://#{$host}/saml/metadata.xml"
- x.registry = OnDemandRegistry.new
- x.logger = Logger.new("/dev/stderr")
- end
-
- @saml_metadata = Saml::Kit::Metadata.build do |builder|
- builder.contact_email = 'hi@example.com'
- builder.organization_name = "Acme, Inc"
- builder.organization_url = "#{scheme}://#{host}"
- builder.build_identity_provider do |x|
- x.add_single_sign_on_service("#{scheme}://#{host}/saml/new", binding: :http_post)
- x.name_id_formats = [Saml::Kit::Namespaces::PERSISTENT, Saml::Kit::Namespaces::EMAIL_ADDRESS]
- x.attributes << :email
- end
- end
- end
-
- def call(env)
- request = Rack::Request.new(env)
- case request.request_method
- when Rack::GET
- case request.path
- when "/saml/continue"
- if current_user?(request)
- saml_params = request.session[:saml_params]
- return saml_post_back(request, current_user(request), saml_params)
- else
- return http_redirect_to("/sessions/new?redirect_back=/saml/continue")
- end
- when "/saml/metadata.xml"
- return http_ok(
- { 'Content-Type' => "application/samlmetadata+xml" },
- saml_metadata.to_xml(pretty: true)
- )
- end
- when Rack::POST
- case request.path
- when "/saml/new"
- saml_params = saml_params_from(request)
-
- if current_user?(request)
- return saml_post_back(request, current_user(request), saml_params)
- else
- request.session[:saml_params] = saml_params
- return http_redirect_to("/sessions/new?redirect_back=/saml/continue")
- end
- end
- end
-
- http_not_found
- end
-
- private
-
- attr_reader :saml_metadata
-
- def saml_post_back(request, user, saml_params)
- saml_request = binding_for(request).deserialize(saml_params)
-
- @builder = nil
- url, saml_params = saml_request.response_for(
- user,
- binding: :http_post,
- relay_state: saml_params[:RelayState]
- ) { |builder| @builder = builder }
- template = <<~ERB
- <!doctype html>
- <html lang="en" data-theme="light">
- <head>
- <title>IdP</title>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <meta name="color-scheme" content="light dark">
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
- </head>
- <body>
- <main class="container">
- <nav>
- <ul>
- <li><strong>IdP</strong></li>
- <li><a href="http://ui.example.com:8080/">UI</a></li>
- </ul>
- </nav>
-
- <h2>Recieved SAML Request</h2>
- <textarea readonly="readonly" disabled="disabled" cols=225 rows=8><%=- saml_request.to_xml(pretty: true) -%></textarea>
-
- <h2>Sending SAML Response (IdP -> SP)</h2>
- <textarea readonly="readonly" disabled="disabled" cols=225 rows=32><%=- @builder.build.to_xml(pretty: true) -%></textarea>
- <form id="postback-form" action="<%= url %>" method="post">
- <%- saml_params.each do |(key, value)| -%>
- <input type="hidden" name="<%= key %>" value="<%= value %>" />
- <%- end -%>
- <input id="submit-button" type="submit" value="Continue" />
- </form>
- </main>
- </body>
- </html>
- ERB
- erb = ERB.new(template, trim_mode: '-')
- html = erb.result(binding)
- [200, { 'Content-Type' => "text/html" }, [html]]
- end
-
- def saml_params_from(request)
- if request.post?
- {
- "SAMLRequest" => request.params["SAMLRequest"],
- "RelayState" => request.params["RelayState"],
- }
- else
- query_string = request.query_string
- on = query_string.include?("&amp;") ? "&amp;" : "&"
- Hash[query_string.split(on).map { |x| x.split("=", 2) }].symbolize_keys
- end
- end
-
- def binding_for(request)
- Saml::Kit::Bindings::HttpPost.new(location: "#{$scheme}://#{$host}/saml/new")
- end
- end
-end
-
-class Organization
- class << self
- def find(id)
- new
- end
- end
-end
-
-module Authz
- class OrganizationPolicy < DeclarativePolicy::Base
- condition(:owner) { true }
-
- rule { owner }.enable :read_project
- rule { owner }.enable :read_group
- rule { owner }.enable :create_project
- end
-
- class JWT
- class << self
- # TODO:: validate signature
- def decode(encoded)
- _header, body, _signature = encoded
- .split('.', 3)
- .map { |x| JSON.parse(Base64.strict_decode64(x), symbolize_names: true) rescue {} }
- new(body)
- end
- end
-
- attr_reader :claims
-
- def initialize(claims)
- now = Time.now.to_i
- @claims = {
- iss: "#{$scheme}://#{$host}",
- iat: now,
- aud: "",
- nbf: now,
- jti: SecureRandom.uuid,
- exp: now + 3600,
- }.merge(claims)
- end
-
- def [](claim)
- claims.fetch(claim)
- end
-
- def active?
- now = Time.now.to_i
- self[:nbf] <= now && now < self[:exp]
- end
-
- def to_jwt
- [
- Base64.strict_encode64(JSON.generate(alg: "none")),
- Base64.strict_encode64(JSON.generate(claims)),
- ""
- ].join(".")
- end
- end
-
- module Rpc
- class Ability
- def allowed(request, env)
- {
- result: can?(request)
- }
- end
-
- private
-
- def can?(request)
- subject = subject_of(request.subject)
- resource = resource_from(request.resource)
- permission = request.permission.to_sym
-
- policy = DeclarativePolicy.policy_for(subject, resource)
- policy.can?(permission)
- rescue StandardError => error
- puts error.inspect
- false
- end
-
- def subject_of(encoded_token)
- token = ::Authz::JWT.decode(encoded_token)
- token&.claims[:sub]
- end
-
- def resource_from(global_id)
- GlobalID::Locator.locate(global_id)
- end
- end
- end
-
- class AuthorizationGrant
- class << self
- def all
- @all ||= []
- end
-
- def find_by(params)
- case params[:grant_type]
- when 'authorization_code'
- authorization_code_grant(params[:code], params[:code_verifier])
- when 'refresh_token'
- refresh_grant(params[:refresh_token])
- when 'client_credentials'
- client_credentials_grant(params)
- when 'password'
- password_grant(params[:username], params[:password])
- when "urn:ietf:params:oauth:grant-type:saml2-bearer" # RFC-7522
- saml_assertion_grant(params[:assertion])
- when 'urn:ietf:params:oauth:grant-type:jwt-bearer' # RFC7523
- jwt_bearer_grant(params)
- end
- end
-
- # TODO:: implement `code_verifier` param
- def authorization_code_grant(code, code_verifier)
- all.find do |grant|
- grant.active? && grant.code == code
- end
- end
-
- def refresh_grant(refresh_token)
- raise NotImplementedError
- end
-
- def client_credential_grant(params)
- raise NotImplementedError
- end
-
- def password_grant(username, password)
- raise NotImplementedError
- end
-
- def saml_assertion_grant(encoded_saml_assertion)
- xml = Base64.decode64(encoded_saml_assertion)
- # TODO:: Validate signature and prevent assertion reuse
- saml_assertion = Saml::Kit::Document.to_saml_document(xml)
-
- user = ::Authn::User.find_by_email(saml_assertion.name_id) ||
- ::Authn::User.find(saml_assertion.name_id)
- new(user, saml_assertion: saml_assertion)
- end
-
- def jwt_bearer_grant(params)
- raise NotImplementedError
- end
-
- def create!(user, params = {})
- new(user, params).tap do |grant|
- all << grant
- end
- end
- end
-
- attr_reader :code, :user, :params
-
- def initialize(user, params = {})
- @user = user
- @params = params
- @code = SecureRandom.uuid
- @exchanged_at = nil
- end
-
- def active?
- @exchanged_at.nil?
- end
-
- def inactive?
- !active?
- end
-
- def create_access_token
- raise "Invalid code" unless active?
-
- user.create_access_token.tap do
- @exchanged_at = Time.now
- end
- end
-
- def exchange
- {
- access_token: create_access_token.to_jwt,
- token_type: "Bearer",
- issued_token_type: "urn:ietf:params:oauth:token-type:access_token",
- expires_in: 3600,
- refresh_token: SecureRandom.hex(32)
- }.tap do |body|
- if params["scope"]&.include?("openid")
- body[:id_token] = user.create_id_token.to_jwt
- end
- end
- rescue StandardError => error
- {
- error: error.message,
- error_description: error.backtrace,
- }
- end
- end
-
- class OAuthController
- include ::HTTPHelpers
-
- def call(env)
- request = Rack::Request.new(env)
-
- case request.request_method
- when Rack::GET
- case request.path
- when "/oauth/authorize/continue"
- if current_user?(request)
- return get_authorize(request.session[:oauth_params])
- end
- when "/oauth/authorize" # RFC-6749
- oauth_params = request.params.slice('client_id', 'scope', 'redirect_uri', 'response_mode', 'response_type', 'state', 'code_challenge_method', 'code_challenge')
- if current_user?(request)
- return get_authorize(oauth_params)
- else
- request.session[:oauth_params] = oauth_params
- return http_redirect_to("/sessions/new?redirect_back=/oauth/authorize/continue")
- end
- else
- return http_not_found
- end
- when Rack::POST
- case request.path
- when "/oauth/authorize" # RFC-6749
- return post_authorize(request)
- when "/oauth/introspect" # RFC-7662
- params = request.content_type == "application/json" ? JSON.parse(request.body.read, symbolize_names: true) : Hash[URI.decode_www_form(request.body.read)].transform_keys(&:to_sym)
- return post_introspect(params.slice(:token, :token_type_hint))
- when "/oauth/token" # RFC-6749
- params = request.content_type == "application/json" ? JSON.parse(request.body.read, symbolize_names: true) : Hash[URI.decode_www_form(request.body.read)].transform_keys(&:to_sym)
- grant = AuthorizationGrant.find_by(params)
-
- return [404, { "Content-Type" => "application/json" }, [JSON.pretty_generate(error: 404, error_description: "Not Found")]] if grant.nil? || grant.inactive?
- return [200, { "Content-Type" => "application/json" }, [JSON.pretty_generate(grant.exchange)]]
- when "/oauth/revoke" # RFC-7009
- # TODO:: Revoke the JWT token and make it ineligible for usage
- return http_not_found
- else
- return http_not_found
- end
- end
- http_not_found
- end
-
- private
-
- def post_introspect(params)
- token = ::Authz::JWT.decode(params[:token])
- return [200, { "Content-Type" => "application/json" }, [JSON.pretty_generate(token.claims.merge(active: token.active?))]]
- end
-
- def get_authorize(oauth_params)
- template = <<~ERB
- <!doctype html>
- <html lang="en" data-theme="light">
- <head>
- <title>IdP</title>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <meta name="color-scheme" content="light dark">
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
- </head>
- <body>
- <main class="container">
- <nav>
- <ul>
- <li><strong>IdP</strong></li>
- <li><a href="http://ui.example.com:8080/">UI</a></li>
- </ul>
- </nav>
-
- <h1>Authorize?</h1>
- <p>Client ID: <%= oauth_params['client_id'] %></p>
- <form id="authorize-form" action="/oauth/authorize" method="post">
- <input type="hidden" name="client_id" value="<%= oauth_params['client_id'] %>" />
- <input type="hidden" name="scope" value="<%= oauth_params['scope'] %>" />
- <input type="hidden" name="redirect_uri" value="<%= oauth_params['redirect_uri'] %>" />
- <input type="hidden" name="response_mode" value="<%= oauth_params['response_mode'] %>" />
- <input type="hidden" name="response_type" value="<%= oauth_params['response_type'] %>" />
- <input type="hidden" name="state" value="<%= oauth_params['state'] %>" />
- <input type="hidden" name="code_challenge_method" value="<%= oauth_params['code_challenge_method'] %>" />
- <input type="hidden" name="code_challenge" value="<%= oauth_params['code_challenge'] %>" />
- <input id="submit-button" type="submit" value="Authorize" />
- </form>
- </main>
- </body>
- </html>
- ERB
- html = ERB.new(template, trim_mode: '-').result(binding)
- [200, { 'Content-Type' => "text/html" }, [html]]
- end
-
- def post_authorize(request)
- params = request.params.slice('client_id', 'redirect_uri', 'response_type', 'response_mode', 'state', 'code_challenge_method', 'code_challenge', 'scope')
- grant = AuthorizationGrant.create!(current_user(request), params)
- case params['response_type']
- when 'code'
- case params['response_mode']
- when 'fragment'
- return [302, { 'Location' => "#{params['redirect_uri']}#code=#{grant.code}&state=#{params['state']}" }, []]
- when 'query'
- return [302, { 'Location' => "#{params['redirect_uri']}?code=#{grant.code}&state=#{params['state']}" }, []]
- else
- # TODO:: form post
- end
- when 'token'
- return http_not_found
- else
- return http_not_found
- end
- end
- end
-end
-
-class IdentityProvider
- include ::HTTPHelpers
-
- def call(env)
- request = Rack::Request.new(env)
-
- case request.request_method
- when Rack::GET
- case request.path
- when '/'
- if current_user?(request)
- return get_dashboard(request)
- else
- return http_redirect_to("/sessions/new")
- end
- when '/.well-known/openid-configuration'
- return openid_metadata
- when '/.well-known/oauth-authorization-server'
- return oauth_metadata
- when '/.well-known/webfinger' # RFC-7033
- return http_not_found
- else
- return http_not_found
- end
- end
- http_not_found
- end
-
- private
-
- def get_dashboard(request)
- template = <<~ERB
- <!doctype html>
- <html lang="en" data-theme="light">
- <head>
- <title>IdP</title>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <meta name="color-scheme" content="light dark">
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
- </head>
- <body>
- <main class="container">
- <nav>
- <ul>
- <li><strong>IdP</strong></li>
- <li><a href="http://ui.example.com:8080/">UI</a></li>
- </ul>
- <ul>
- <li><a href="/">Home</a></li>
- <li><a href="http://ui.example.com:8080/groups.html">Groups</a></li>
- <li>
- <form action="/sessions/delete" method="post">
- <input type="submit" value="logout" />
- </form>
- </li>
- </ul>
- </nav>
-
- <h1> Hello, <%= current_user(request)[:username] %></h1>
- </main>
- </body>
- </html>
- ERB
- erb = ERB.new(template, trim_mode: '-')
- html = erb.result(binding)
- [200, { 'Content-Type' => "text/html" }, [html]]
- end
-
- # GET /.well-known/oauth-authorization-server
- def oauth_metadata
- [200, { 'Content-Type' => "application/json" }, [JSON.pretty_generate({
- issuer: "#{$scheme}://#{$host}/.well-known/oauth-authorization-server",
- authorization_endpoint: "#{$scheme}://#{$host}/oauth/authorize",
- token_endpoint: "#{$scheme}://#{$host}/oauth/token",
- jwks_uri: "", # RFC-7517
- registration_endpoint: "", # RFC-7591
- scopes_supported: ["openid", "profile", "email"],
- response_types_supported: ["code", "code id_token", "id_token", "token id_token"],
- response_modes_supported: ["query", "fragment", "form_post"],
- grant_types_supported: ["authorization_code", "implicit"], # RFC-7591
- token_endpoint_auth_methods_supported: ["client_secret_basic"], # RFC-7591
- token_endpoint_auth_signing_alg_values_supported: ["RS256"],
- service_documentation: "",
- ui_locales_supported: ["en-US"],
- op_policy_uri: "",
- op_tos_uri: "",
- revocation_endpoint: "#{$scheme}://#{$host}/oauth/revoke", # RFC-7009
- revocation_endpoint_auth_methods_supported: ["client_secret_basic"],
- revocation_endpoint_auth_signing_alg_values_supported: ["RS256"],
- introspection_endpoint: "#{$scheme}://#{$host}/oauth/introspect", # RFC-7662
- introspection_endpoint_auth_methods_supported: ["client_secret_basic"],
- introspection_endpoint_auth_signing_alg_values_supported: ["RS256"],
- code_challenge_methods_supported: [], # RFC-7636
- })]]
- end
-
- # GET /.well-known/openid-configuration
- def openid_metadata
- [200, { 'Content-Type' => "application/json" }, [JSON.pretty_generate({
- issuer: "#{$scheme}://#{$host}/.well-known/oauth-authorization-server",
- authorization_endpoint: "#{$scheme}://#{$host}/oauth/authorize",
- token_endpoint: "#{$scheme}://#{$host}/oauth/token",
- userinfo_endpoint: "#{$scheme}://#{$host}/oidc/user/",
- jwks_uri: "", # RFC-7517
- registration_endpoint: nil,
- scopes_supported: ["openid", "profile", "email"],
- response_types_supported: ["code", "code id_token", "id_token", "token id_token"],
- response_modes_supported: ["query", "fragment", "form_post"],
- grant_types_supported: ["authorization_code", "implicit"], # RFC-7591
- acr_values_supported: [],
- subject_types_supported: ["pairwise", "public"],
- id_token_signing_alg_values_supported: ["RS256"],
- id_token_encryption_alg_values_supported: [],
- id_token_encryption_enc_values_supported: [],
- userinfo_signing_alg_values_supported: ["RS256"],
- userinfo_encryption_alg_values_supported: [],
- userinfo_encryption_enc_values_supported: [],
- request_object_signing_alg_values_supported: ["none", "RS256"],
- request_object_encryption_alg_values_supported: [],
- request_object_encryption_enc_values_supported: [],
- token_endpoint_auth_methods_supported: ["client_secret_post", "client_secret_basic", "client_secret_jwt", "private_key_jwt"],
- token_endpoint_auth_signing_alg_values_supported: [],
- display_values_supported: [],
- claim_types_supported: ["normal", "aggregated", "distributed"],
- claims_supported: [
- "acr",
- "auth_time",
- "email",
- "email_verified",
- "family_name",
- "given_name",
- "iss",
- "locale",
- "name",
- "nickname",
- "picture",
- "profile",
- "sub",
- "website"
- ],
- service_documentation: nil,
- claims_locales_supported: [],
- ui_locales_supported: ["en-US"],
- claims_parameter_supported: false,
- request_parameter_supported: false,
- request_uri_paramater_supported: false,
- require_request_uri_registration: false,
- op_policy_uri: "",
- op_tos_uri: "",
- })]]
- end
-end
-
-if __FILE__ == $0
- app = Rack::Builder.new do
- use Rack::CommonLogger
- use Rack::Reloader
- use Rack::Session::Cookie, { domain: $host.split(":", 2)[0], path: "/", secret: SecureRandom.hex(64) }
- use ::Warden::Manager do |config|
- config.default_scope = :user
- config.default_strategies :password
- end
-
- map "/twirp" do
- # https://github.com/arthurnn/twirp-ruby/wiki/Service-Handlers
- run ::Authx::Rpc::AbilityService.new(::Authz::Rpc::Ability.new)
- end
- map "/oauth" do
- run ::Authz::OAuthController.new
- end
-
- map "/saml" do
- run Authn::SAMLController.new($scheme, $host)
- end
-
- map "/sessions" do
- run Authn::SessionsController.new
- end
- run IdentityProvider.new
- end.to_app
-
- Rackup::Server.start(app: app, Port: $port)
-end
diff --git a/bin/ui b/bin/ui
deleted file mode 100755
index 0c1e4c4c..00000000
--- a/bin/ui
+++ /dev/null
@@ -1,503 +0,0 @@
-#!/usr/bin/env ruby
-
-require "bundler/inline"
-
-gemfile do
- source "https://rubygems.org"
-
- gem "base64", "~> 0.1"
- gem "erb", "~> 4.0"
- gem "net-hippie", "~> 1.0"
- gem "rack", "~> 3.0"
- gem "rack-session", "~> 2.0"
- gem "rackup", "~> 2.0"
- gem "saml-kit", "1.4.0"
- gem "webrick", "~> 1.0"
-end
-
-$scheme = ENV.fetch("SCHEME", "http")
-$port = ENV.fetch("PORT", 8283).to_i
-$host = ENV.fetch("HOST", "localhost:#{$port}")
-$idp_host = ENV.fetch("IDP_HOST", "localhost:8282")
-
-Net::Hippie.logger = Logger.new($stdout, level: :debug)
-
-class OnDemandRegistry < Saml::Kit::DefaultRegistry
- def metadata_for(entity_id)
- found = super(entity_id)
- return found if found
-
- register_url(entity_id, verify_ssl: false)
- super(entity_id)
- end
-end
-
-Saml::Kit.configure do |x|
- x.entity_id = "#{$scheme}://#{$host}/saml/metadata.xml"
- x.registry = OnDemandRegistry.new
- x.logger = Logger.new("/dev/stderr")
-end
-
-module OAuth
- class Client
- attr_reader :client_id, :client_secret, :http, :authz_host
-
- def initialize(authz_host, client_id, client_secret)
- @authz_host = authz_host
- @client_id = client_id
- @client_secret = client_secret
- @http = Net::Hippie::Client.new(headers: ::Net::Hippie::Client::DEFAULT_HEADERS.merge({
- 'Authorization' => Net::Hippie.basic_auth(client_id, client_secret),
- }))
- end
-
- def [](key)
- server_metadata.fetch(key)
- end
-
- def authorize_uri(redirect_uri:, state: SecureRandom.uuid, response_type: "code", response_mode: "query", scope: "openid")
- [
- self[:authorization_endpoint],
- to_query(
- client_id: client_id,
- state: state,
- redirect_uri: redirect_uri,
- response_mode: response_mode,
- response_type: response_type,
- scope: scope,
- )
- ].join("?")
- end
-
- def exchange(grant_type, params = {})
- with_http do |client|
- client.post(self[:token_endpoint], body: body_for(grant_type, params))
- end
- end
-
- private
-
- def body_for(grant_type, params)
- case grant_type
- when "authorization_code"
- {
- grant_type: grant_type,
- code: params.fetch(:code),
- code_verifier: params.fetch(:code_verifier, "not_implemented"),
- }
- when "urn:ietf:params:oauth:grant-type:saml2-bearer"
- {
- grant_type: grant_type,
- assertion: params.fetch(:assertion),
- }
- else
- raise NotImplementedError.new(grant_type)
- end
- end
-
- def to_query(params = {})
- params.map do |(key, value)|
- [key, value].join("=")
- end.join("&")
- end
-
- def with_http
- http.with_retry do |client|
- yield client
- end
- end
-
- def server_metadata
- @server_metadata ||=
- with_http do |client|
- response = client.get("http://#{authz_host}/.well-known/oauth-authorization-server")
- JSON.parse(response.body, symbolize_names: true)
- end
- end
- end
-end
-
-module HTTPHelpers
- def current_user?(request)
- request.session[:access_token]
- end
-
- def not_found
- [404, { 'X-Backend-Server' => 'UI' }, []]
- end
-
- def redirect_to(location)
- if location.start_with?("http")
- [302, { 'Location' => location }, []]
- else
- [302, { 'Location' => "#{$scheme}://#{$host}#{location}" }, []]
- end
- end
-
- def with_layout(bind)
- template = <<~ERB
- <!doctype html>
- <html lang="en">
- <head>
- <title>UI</title>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <meta name="color-scheme" content="light dark">
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.colors.min.css">
- </head>
- <body>
- <main class="container">
- #{yield}
- </main>
- </body>
- </html>
- ERB
- ERB.new(template, trim_mode: '-').result(bind)
- end
-end
-
-class UI
- include ::HTTPHelpers
-
- attr_reader :oauth_client
-
- def initialize(oauth_client)
- @oauth_client = oauth_client
- end
-
- def call(env)
- request = Rack::Request.new(env)
- case request.request_method
- when Rack::GET
- case request.path
- when "/index.html"
- return get_index(request)
- when "/groups.html"
- if current_user?(request)
- return get_groups(request)
- else
- return redirect_to("/oidc/new")
- end
- when /\A\/groups\/\d+\/projects.html\z/
- if current_user?(request)
- return get_projects(request)
- else
- return redirect_to("/oidc/new")
- end
- when "/oauth/callback"
- return oauth_callback(Rack::Request.new(env))
- when "/oidc/new"
- return redirect_to(oauth_client.authorize_uri(
- redirect_uri: "#{request.base_url}/oauth/callback"
- ))
- when "/saml/metadata.xml"
- return metadata
- when "/saml/new"
- return saml_post_to_idp(Rack::Request.new(env))
- else
- return redirect_to("/index.html")
- end
- when Rack::POST
- case request.path
- when "/logout"
- request.session.delete(:access_token)
- request.session.delete(:id_token)
- request.session.delete(:refresh_token)
- return redirect_to("/")
- when "/saml/assertions"
- return saml_assertions(Rack::Request.new(env))
- else
- return not_found
- end
- end
- not_found
- end
-
- private
-
- def metadata
- xml = Saml::Kit::Metadata.build_xml do |builder|
- builder.embed_signature = false
- builder.contact_email = 'ui@example.com'
- builder.organization_name = "Acme, Inc"
- builder.organization_url = "https://example.com"
- builder.build_service_provider do |x|
- x.name_id_formats = [Saml::Kit::Namespaces::PERSISTENT]
- x.add_assertion_consumer_service("#{$scheme}://#{$host}/saml/assertions", binding: :http_post)
- end
- end
-
- [200, { 'Content-Type' => "application/samlmetadata+xml" }, [xml]]
- end
-
- def get_index(request)
- html = with_layout(binding) do
- <<~ERB
- <%- if current_user?(request) -%>
- <nav>
- <ul>
- <li><a href="http://#{$idp_host}/">IdP</a></li>
- <li><strong>UI</strong></li>
- </ul>
- <ul>
- <li><a href="/index.html">Home</a></li>
- <li><a href="/groups.html">Groups</a></li>
- <li>
- <form action="/logout" method="post">
- <input type="submit" value="Logout" />
- </form>
- </li>
- </ul>
- </nav>
- <h1>Access Token</h1>
- <pre><%= request.session[:access_token] %></pre>
-
- <h1>ID Token</h1>
- <pre><%= request.session[:id_token] %></pre>
- <%- else -%>
- <nav>
- <ul>
- <li><a href="http://#{$idp_host}/">IdP</a></li>
- <li><strong>UI</strong></li>
- </ul>
- <ul>
- <li><a href="/saml/new">SAML Login</a></li>
- <li><a href="/oidc/new">OIDC Login</a></li>
- </ul>
- </nav>
- <%- end -%>
- ERB
- end
- [200, { "Content-Type" => "text/html" }, [html]]
- end
-
- def oauth_callback(request)
- response = oauth_client.exchange("authorization_code", code: request.params['code'])
- if response.code == "200"
- tokens = JSON.parse(response.body, symbolize_names: true)
- request.session[:access_token] = tokens[:access_token]
- request.session[:id_token] = tokens[:id_token]
- request.session[:refresh_token] = tokens[:access_token]
-
- html = with_layout(binding) do
- <<~ERB
- <nav>
- <ul>
- <li><a href="http://#{$idp_host}/">IdP</a></li>
- <li><strong>UI</strong></li>
- </ul>
- <ul>
- <li><a href="/index.html">Home</a></li>
- <li><a href="/groups.html">Groups</a></li>
- <li>
- <form action="/logout" method="post">
- <input type="submit" value="Logout" />
- </form>
- </li>
- </ul>
- </nav>
- <h1>Access Token</h1>
- <pre style="display: none;"><%= response.body %></pre>
- <pre><%= JSON.pretty_generate(request.session[:access_token]) %></pre>
- ERB
- end
- [200, { 'Content-Type' => "text/html" }, [html]]
- else
- [response.code, response.header, [response.body]]
- end
- end
-
- def get_groups(request)
- http = Net::Hippie::Client.new(headers: ::Net::Hippie::Client::DEFAULT_HEADERS.merge({
- 'Authorization' => Net::Hippie.bearer_auth(request.session[:access_token])
- }))
-
- response = http.get("http://api.example.com:8080/groups.json")
- if response.code == "200"
- groups = JSON.parse(response.body, symbolize_names: true)
- html = with_layout(binding) do
- <<~ERB
- <nav>
- <ul>
- <li><a href="http://#{$idp_host}/">IdP</a></li>
- <li><strong>UI</strong></li>
- </ul>
- <ul>
- <li><a href="/index.html">Home</a></li>
- <li><a href="/groups.html">Groups</a></li>
- <li>
- <form action="/logout" method="post">
- <input type="submit" value="Logout" />
- </form>
- </li>
- </ul>
- </nav>
-
- <h1>Groups</h1>
- <table>
- <thead>
- <tr>
- <th>ID</th>
- <th>Name</th>
- <th>Organization ID</th>
- <th>Parent ID</th>
- <th>&nbsp;</th>
- </tr>
- </thead>
- <tbody>
- <%- groups.each do |group| -%>
- <tr>
- <td><%= group[:id] %></td>
- <td><%= group[:name] %></td>
- <td><%= group[:organization_id] %></td>
- <td><%= group[:parent_id] %></td>
- <td><a href="/groups/<%= group[:id] %>/projects.html">Projects</a></td>
- </tr>
- <%- end -%>
- </tbody>
- </table>
- ERB
- end
- [200, { 'Content-Type' => "text/html" }, [html]]
- else
- [response.code, response.header, [response.body]]
- end
- end
-
- def get_projects(request)
- http = Net::Hippie::Client.new(headers: ::Net::Hippie::Client::DEFAULT_HEADERS.merge({
- 'Authorization' => Net::Hippie.bearer_auth(request.session[:access_token])
- }))
-
- response = http.get("http://api.example.com:8080/projects.json")
- if response.code == "200"
- projects = JSON.parse(response.body, symbolize_names: true)
-
- html = with_layout(binding) do
- <<~ERB
- <nav>
- <ul>
- <li><a href="http://#{$idp_host}/">IdP</a></li>
- <li><strong>UI</strong></li>
- </ul>
- <ul>
- <li><a href="/index.html">Home</a></li>
- <li><a href="/groups.html">Groups</a></li>
- <li>
- <form action="/logout" method="post">
- <input type="submit" value="Logout" />
- </form>
- </li>
- </ul>
- </nav>
-
- <h1>Projects</h1>
- <table>
- <thead>
- <tr>
- <th>Name</th>
- <th>Group ID</th>
- </tr>
- </thead>
- <tbody>
- <%- projects.each do |project| -%>
- <tr>
- <td><%= project[:name] %></td>
- <td><%= project[:group_id] %></td>
- </tr>
- <%- end -%>
- </tbody>
- </table>
- ERB
- end
- [200, { 'Content-Type' => "text/html" }, [html]]
- else
- [response.code, response.header, [response.body]]
- end
- end
-
- def saml_post_to_idp(request)
- idp = Saml::Kit.registry.metadata_for("http://#{$idp_host}/saml/metadata.xml")
- relay_state = Base64.strict_encode64(JSON.generate(redirect_to: '/dashboard'))
-
- @saml_builder = nil
- uri, saml_params = idp.login_request_for(binding: :http_post, relay_state: relay_state) do |builder|
- @saml_builder = builder
- end
-
- html = with_layout(binding) do
- <<~ERB
- <h2>Sending SAML Request (SP -> IdP)</h2>
- <textarea readonly="readonly" disabled="disabled" cols=225 rows=8><%=- @saml_builder.to_xml(pretty: true) -%></textarea>
-
- <form id="idp-form" action="<%= uri %>" method="post">
- <%- saml_params.each do |(key, value)| -%>
- <input type="hidden" name="<%= key %>" value="<%= value %>" />
- <%- end -%>
- <input id="submit-button" type="submit" value="Continue" />
- </form>
- ERB
- end
- [200, { 'Content-Type' => "text/html" }, [html]]
- end
-
- def saml_assertions(request)
- sp = Saml::Kit.registry.metadata_for("#{request.base_url}/saml/metadata.xml")
- saml_binding = sp.assertion_consumer_service_for(binding: :http_post)
- saml_response = saml_binding.deserialize(request.params)
- raise saml_response.errors unless saml_response.valid?
-
- assertion = Base64.strict_encode64(saml_response.assertion.to_xml)
- response = oauth_client.exchange(
- "urn:ietf:params:oauth:grant-type:saml2-bearer",
- assertion: assertion,
- )
- if response.code == "200"
- tokens = JSON.parse(response.body, symbolize_names: true)
- request.session[:access_token] = tokens[:access_token]
- request.session[:refresh_token] = tokens[:access_token]
-
- html = with_layout(binding) do
- <<~ERB
- <nav>
- <ul>
- <li><a href="http://#{$idp_host}/">IdP</a></li>
- <li><strong>UI</strong></li>
- </ul>
- <ul>
- <li><a href="/index.html">Home</a></li>
- <li><a href="/groups.html">Groups</a></li>
- <li>
- <form action="/logout" method="post">
- <input type="submit" value="Logout" />
- </form>
- </li>
- </ul>
- </nav>
-
- <h2>Received SAML Response</h2>
- <textarea readonly="readonly" disabled="disabled" cols=220 rows=40><%=- saml_response.to_xml(pretty: true) -%></textarea>
- <pre id="raw-saml-response" style="display: none;"><%= request.params["SAMLResponse"] %></pre>
- <pre id="xml-saml-assertion" style="display: none;"><%= saml_response.assertion.to_xml(pretty: true) %></pre>
- <pre id="access-token" style="display: none;"><%= JSON.pretty_generate(request.session[:access_token]) %></pre>
- ERB
- end
- [200, { 'Content-Type' => "text/html" }, [html]]
- else
- [response.code, response.header, [response.body]]
- end
- end
-end
-
-if __FILE__ == $0
- app = Rack::Builder.new do
- use Rack::CommonLogger
- use Rack::Reloader
- use Rack::Session::Cookie, { domain: $host.split(":", 2)[0], path: "/", secret: SecureRandom.hex(64) }
-
- run UI.new(::OAuth::Client.new($idp_host, 'client_id', 'client_secret'))
- end.to_app
-
- Rackup::Server.start(app: app, Port: $port)
-end
diff --git a/casbin.conf b/casbin.conf
deleted file mode 100644
index efe93e0f..00000000
--- a/casbin.conf
+++ /dev/null
@@ -1,17 +0,0 @@
-[request_definition]
-r = subject, domain, action, object
-
-[policy_definition]
-p = subject, domain, action, object
-
-[policy_effect]
-e = some(where (p.eft == allow))
-
-[matchers]
-m =\
- (\
- (p.subject == "*" || r.subject == p.subject || regexMatch(r.subject, p.subject))\
- && (p.domain == "*" || r.domain == p.domain)\
- && (p.action == "*" || regexMatch(r.action, p.action))\
- && keyMatch(r.object, p.object)\
- )
diff --git a/casbin.csv b/casbin.csv
deleted file mode 100644
index 2610d21b..00000000
--- a/casbin.csv
+++ /dev/null
@@ -1,14 +0,0 @@
-p, "\Agid:\/\/[a-z]+\/[A-Za-z:]+\/\d+\z", api.example.com, (GET)|(POST)|(PATCH)|(PUT)|(DELETE)|(HEAD), /*.json
-p, *, *, (GET)|(HEAD), /health
-p, *, *, GET, /.well-known/*
-p, *, *, GET, /favicon.ico
-p, "\Agid:\/\/[a-z]+\/[A-Za-z:]+\/\d+\z", idp.example.com, (GET)|(POST)|(PATCH)|(PUT)|(DELETE)|(HEAD), /twirp/authx.rpc.*
-p, *, idp.example.com, (GET), /
-p, *, idp.example.com, (GET)|(POST), /oauth*
-p, *, idp.example.com, (GET)|(POST), /saml*
-p, *, idp.example.com, (GET)|(POST), /sessions*
-p, *, ui.example.com, (GET)|(POST), /oauth*
-p, *, ui.example.com, (GET)|(POST), /oidc*
-p, *, ui.example.com, (GET)|(POST), /saml*
-p, *, ui.example.com, (GET), /*.html
-p, *, ui.example.com, (POST), /logout
diff --git a/cmd/authzd/main.go b/cmd/authzd/main.go
index ae21e4ae..fbe89e22 100644
--- a/cmd/authzd/main.go
+++ b/cmd/authzd/main.go
@@ -1,18 +1,15 @@
package main
import (
- "log"
- "net"
+ "fmt"
+ "net/http"
"github.com/xlgmokha/x/pkg/env"
- "github.com/xlgmokha/x/pkg/x"
- "gitlab.com/mokhax/spike/pkg/rpc"
+ "gitlab.com/gitlab-org/software-supply-chain-security/authorization/authz.d/pkg/rpc"
)
func main() {
- server := rpc.New()
- log.Fatal(server.Serve(x.Must(net.Listen(
- "tcp",
- env.Fetch("BIND_ADDR", "localhost:50051"),
- ))))
+ bindAddr := env.Fetch("BIND_ADDR", "localhost:8080")
+ fmt.Printf("Listening on %v\n", bindAddr)
+ http.ListenAndServe(bindAddr, rpc.New())
}
diff --git a/cmd/gtwy/main.go b/cmd/gtwy/main.go
deleted file mode 100644
index f2a7d2e2..00000000
--- a/cmd/gtwy/main.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package main
-
-import (
- "log"
-
- "github.com/xlgmokha/x/pkg/env"
- "gitlab.com/mokhax/spike/pkg/app"
-)
-
-func main() {
- log.Fatal(app.Start(env.Fetch("BIND_ADDR", ":8080")))
-}
diff --git a/db/api/groups.csv b/db/api/groups.csv
deleted file mode 100644
index 03fcf608..00000000
--- a/db/api/groups.csv
+++ /dev/null
@@ -1,9 +0,0 @@
-"id","organization_id","parent_id","name"
-1,1,-1,"A"
-2,1,-1,"B"
-3,2,-1,"gitlab-org"
-4,2,-1,"gitlab-com"
-5,2,4,"gl-security"
-6,2,5,"test-projects"
-7,2,4,"support"
-8,2,7,"toolbox"
diff --git a/db/api/organizations.csv b/db/api/organizations.csv
deleted file mode 100644
index 840de05b..00000000
--- a/db/api/organizations.csv
+++ /dev/null
@@ -1,3 +0,0 @@
-"id","name"
-1,"default"
-2,"gitlab"
diff --git a/db/api/projects.csv b/db/api/projects.csv
deleted file mode 100644
index 594cc390..00000000
--- a/db/api/projects.csv
+++ /dev/null
@@ -1,9 +0,0 @@
-"id","group_id","name"
-1,1,"A1"
-2,2,"B1"
-3,3,"gitlab"
-4,6,"eicar-test-project"
-5,5,"disclosures"
-6,8,"changelog-parser"
-7,4,"handbook"
-8,4,"www-gitlab-com"
diff --git a/db/idp/users.csv b/db/idp/users.csv
deleted file mode 100644
index a0194d71..00000000
--- a/db/idp/users.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-"id","username","email","password_digest"
-1,"root","root@example.org","$2a$12$pFh1DgN0JcvRAeHeTCGfiuDtuaFaV0vG7He0B6YVpkKWsBy2ZmZtO"
diff --git a/doc/share/authz/ABAC.md b/doc/share/authz/ABAC.md
deleted file mode 100644
index 791fdeff..00000000
--- a/doc/share/authz/ABAC.md
+++ /dev/null
@@ -1,57 +0,0 @@
-# Attribute-Based Access Control (ABAC)
-
-> Attribute-based access control (ABAC) is considered one of th emost generalized
-> forms of access control as it can capture the salient features of discretionary
-> access (DAC), mandatory access control (MAC) and role-based access control
-> (RBAC) using appropriate attributes such as access control lists, security
-> labels and roles respectively. [5]
-
-ABAC has been studied for a long time and many different formal models have been
-proposed.
-
-ABAC is a logical access control model that is distinguishable because it
-controls access to objects by evaluating rules against the attributes of
-entities (subject and object), operations, and the environment relevant to a
-request.
-
-As new subjects join the organization, rules and objects do not need to be
-modified. As long as the subject is assigned the attributes necessary for access
-to the required objects, no modifications to existing rules or object attributes
-are required.
-
-There can be three types of attributes:
-
-1. Atomic-values or single valued attribute:
-1. Set-valued or multi-valued attribute:
-1. Structured Attribute:
-
-Attributes can be either:
-
-* Entity Attribute: a thing that can be distinctly identified.
-* Non-entity Attribute: whose range is not defined on the set of entities in the
- system.
-
-The range of an attribute is bounded or not:
-
-* Finite Domain Attribute: Range of this attribute type is a finite set of
- attribute value.
-* Infinite Domain Attribute: Range of this attribute type is a countably
- infinite set of attribute values.
-
-## Weaknesses
-
-It is often claimed that attributes can express relationships, and indeed this
-is trivial for direct relationships. However, the use of indirect relations,
-also called multilevel or composite relations, is fundamental to ReBAC. It is
-hard to see how ABAC can express long chains of relationships. It has been
-suggested that ReBAC emerged to overcome this shortcoming of attributes.
-
-## See Also
-
-* [Classifying and Comparing Attribute-Based and Relationship-Based Access Control][5]
-* [A Capability-based Distributed Authorization System to Enforce Context-aware Permission Sequences][6]
-* [Guide to Attribute Based Access Control (ABAC) Definition and Considerations][7]
-
-[5]: https://dl.acm.org/doi/pdf/10.1145/3029806.3029828
-[6]: https://dl.acm.org/doi/pdf/10.1145/3532105.3535014
-[7]: https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-162.pdf
diff --git a/doc/share/authz/DESIGN.md b/doc/share/authz/DESIGN.md
deleted file mode 100644
index 51129a1c..00000000
--- a/doc/share/authz/DESIGN.md
+++ /dev/null
@@ -1,194 +0,0 @@
-# Design
-
-## Current
-
-### Architecture
-
-```plaintext
- -------------
- | user-agent |
- -------------
- |
- V
-----|:443|------------------------------
- |
- V------------|
- | |
- |------| |
- V V V
- | ----------------------------
- |--->| authn | CI | ... | authz |
- V |--------------------------|
- |--->| UI | REST API | ... |
- | ----------------------------
- V A
- | |
- |---->---->---
-```
-
-## Proposed
-
-### Architecture
-
-```plaintext
- -------------
- | user-agent |
- -------------
- |
- V
-----|:8080|-----------------------------------------------
- |
- V
- ---------------
- | API Gateway |
- ---------------
- |
- | ---------------------------------
- | | IdP (saml, oidc) |
- | |-------------------------------|
- |--->| :http (authn) | :grpc (authz) |
- | ---------------------------------
- | A
- ----------- |
- | | |
- V V |
- ------ ------------ |
- | UI | | REST API |----------|
- ------ ------------ |
- | A
- |---->----------->------------|
-
-[UI]: ui.example.com
-[REST API]: api.example.com
-[IdP]: idp.example.com
-```
-
-SAML Login Flow
-
-```plantuml
-@startuml
-Browser -> UI: 1. Get dashboard
-UI --> Browser: Generate SAML <AuthnRequest /> and redirect to IdP
-
-Browser -> IdP: 2. Deliver SAML <AuthnRequest />
-IdP --> Browser: 3. Redirect to Login Page
-Browser -> IdP: 4. Login
-IdP --> Browser: 5. Generate SAML <AuthnResponse /> with <Assertion /> and redirect to UI
-
-Browser -> UI: 6. Deliver SAML <AuthnResponse />
-UI -> IdP: 7. Exchange <Assertion /> for Tokens
-IdP --> UI: Return `access_token` and `refresh_token`
-UI --> Browser: Redirect to dashboard
-Browser -> UI: Get dashboard
-UI -> API: 8. Request list of groups and provide Access Token
-API -> IdP: 9. Check if token is valid and check declarative policy
-IdP --> API: Return result of `Ability.allowed?`
-API --> UI: Return list of groups as JSON
-UI --> Browser: Return list of groups as HTML
-@enduml
-```
-
-1. `GET http://ui.example.com/saml/new`
-2. `POST http://idp.example.com/saml/new`
-3. `GET http://idp.example.com/sessions/new?redirect_back=/saml/continue`
-4. `POST http://idp.example.com/sessions`
-5. `GET http://idp.example.com/saml/continue`
-6. `POST http://ui.example.com/saml/assertions`
-7. `POST http://idp.example.com/oauth/token`
-8. `GET http://api.example.com/groups.json`
-9. `GET grpc://idp.example.com/twirp/authx.rpc.Ability/Allowed`
-
-OIDC Login Flow
-
-```plantuml
-@startuml
-Browser -> UI: 1. Get dashboard
-UI --> Browser: Generate OAuth Grant Request and redirect to IdP
-
-Browser -> IdP: 2. Deliver OAuth Grant Request
-IdP --> Browser: 3. Redirect to Login Page
-Browser -> IdP: 4. Login
-IdP --> Browser: 5. Generate Consent Screen for Authorization Code flow
-Browser -> IdP: 6. Consent
-IdP --> Browser: Generate Authorization Code and redirect to UI
-
-Browser -> UI: 7. Deliver Authorization Code Grant
-UI -> IdP: 8. Exchange Authorization Code Grant for Tokens
-IdP --> UI: Return `access_token` and `refresh_token`
-UI --> Browser: Redirect to dashboard
-Browser -> UI: Get dashboard
-UI -> API: 9. Request list of groups and provide Access Token
-API -> IdP: 10. Check if token is valid and check declarative policy
-IdP --> API: Return result of `Ability.allowed?`
-API --> UI: Return list of groups as JSON
-UI --> Browser: Return list of groups as HTML
-@enduml
-```
-
-1. `GET http://ui.example.com/oidc/new`
-2. `GET http://idp.example.com/oauth/authorize`
-3. `GET http://idp.example.com/sessions/new?redirect_back=/oauth/authorize/continue`
-4. `POST http://idp.example.com/sessions`
-5. `GET http://idp.example.com/oauth/authorize/continue`
-6. `POST http://idp.example.com/oauth/authorize`
-7. `GET http://ui.example.com/oauth/callback`
-8. `POST http://idp.example.com/oauth/token`
-9. `GET http://api.example.com/groups.json`
-10. `GET grpc://idp.example.com/twirp/authx.rpc.Ability/Allowed`
-
-### Permissions
-#### Option 1
-
-| permission | scope | description |
-| ---------- | ----- | ----------- |
-| `read` | `gid://app/Organization/1` | Can read Org 1 resource |
-| `read` | `gid://app/Organization/1/*` | Can read every resource below Org 1 hierarchy |
-| `read` | `gid://app/Organization/1/Group/1` | Can read Group 1 resource |
-| `read` | `gid://app/Organization/1/Group/1/*` | Can read every resource below Group 1 hierarchy |
-| `read` | `gid://app/Organization/1/Group/1/Project/1` | Can read project 1 |
-| `read` | `gid://app/Project/1` | Can read project 1 resource (short circuit example) |
-| `read` | `gid://app/Organization/1/Group/1?attributes[]=name&attributes[]=description` | Can read name and description of Group 1 resource |
-
-Example:
-
-The following example allows the subject of the token to read all of the descendant resources of `Project 1` and `Project 2` and it can read `Project 3`.
-
-```json
-{
- "sub": "gid://User/17",
- "scope": {
- "read": [
- "gid://app/Organization/1/Group/1/Project/1/*",
- "gid://app/Organization/1/Group/1/Project/2/*",
- "gid://app/Organization/1/Group/2/Project/3"
- ]
- }
-}
-```
-
-#### Option 2
-
-Encode access and scope directly into the name of the permission.
-
-| permission | description |
-| ---------- | ----------- |
-| `read:organization:1` | Can read Org 1 resource |
-| `read:organization:1:*` | Can read every resource below Org 1 hierarchy |
-| `read:organization:1:group:*` | Can read Group 1 resource |
-| `read:organization:1:group:1:*` | Can read every resource below Group 1 hierarchy |
-| `read:organization:1:group:1:project:1` | Can read project 1 |
-| `read:project:1` | Can read project 1 resource (short circuit example) |
-| `read:organization:1:group:1:attributes[]=name&attributes[]=description` | Can read name and description of Group 1 resource |
-
-Example:
-
-```json
-{
- "sub": "gid://User/17",
- "scope": [
- "read:organization:1:group:1:project:1:*",
- "read:organization:1:group:1:project:2:*",
- "read:organization:1:group:2:project:3"
- ]
-}
-```
diff --git a/doc/share/authz/EXPERIMENT.md b/doc/share/authz/EXPERIMENT.md
deleted file mode 100644
index 0460fe39..00000000
--- a/doc/share/authz/EXPERIMENT.md
+++ /dev/null
@@ -1,42 +0,0 @@
-## Experiments
-
-### Twirp + gRPC (AuthZ)
-
-This experiment exposes a gRPC endpoint that aligns with the [`Ability.allowed?(subject, permission, resource)`][1] interface from GitLab's declarative authorization logic.
-
-It demonstrates a headless authorization service that provides a low-latency decision point for other services to verify permissions.
-
-Actors in this experiment:
-
-* Headless authz service: A facade over GitLab’s existing declarative policies.
-* API (Resource Server in OAuth terms): A slimmed-down GitLab REST API that delegates authorization decisions to the authz service.
-
-### SAML, OIDC, OAuth
-
-This experiment showcases how a separate authx service can handle both authentication and authorization using standard protocols:
-
-* SAML & OIDC for authentication
-* OAuth for authorization
-
-Actors in this experiment:
-
-* Authx service: Acts as a SAML Identity Provider and an OAuth Authorization Server.
-* API: A slimmed-down GitLab REST API.
-
-### API Gateway
-
-This experiment explores a stateless authorization mechanism by integrating a policy DSL (such as [Casbin][3]) into a reverse proxy.
-Authorization decisions are made early in the request pipeline based on HTTP request headers and body content.
-
-### Sidecar Process
-
-This experiment demonstrates a sidecar approach for making authorization decisions within an nginx process.
-Inspired by [Open Policy Agent][4] deployments. This experiment:
-
-* Uses lua bindings in nginx to connect to a local client process.
-* The client process proxies requests to a gRPC based policy decision service.
-
-
-[1]: https://gitlab.com/gitlab-org/gitlab/-/blob/e1f6db024561e35462ac8d9f54b8f9678f6ed6ee/app/models/ability.rb#L73
-[3]: https://casbin.org/
-[4]: https://www.openpolicyagent.org/
diff --git a/doc/share/authz/FAQ.md b/doc/share/authz/FAQ.md
deleted file mode 100644
index 8e73beb2..00000000
--- a/doc/share/authz/FAQ.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Frequently Asked Question (FAQ)
-
-* Q: Are there permissions that do not cascade down the group hierarchy?
-* Q: How do we define the scope of a permission? (hierarchical?)
-* Q: What is the unique identifier for each security principal across service boundaries? (i.e. bigint, ulid, uuid, email)
-* Q: What permissions do each of the standard roles have today?
-* Q: How does a permission cascade down a group hierarchy?
-
-```
-Organization
- Group A
- * Roles
- * Developer
- * Maintainer
- * Custom A
- * base: developer
- * permissions:
- * admin_vulnerability: true
- * read_vulnerability: true (implicitly)
- * Custom B
- * base: maintainer
- * permissions:
- * Doesn't really matter because Maintainer has all the permissions available via a custom role. <- Fact check this
- Group Aa
- Project Aa1
- Project Aa2
- Group Aaa
- Project Aaa1
- Project Aaa2
-```
-
-* Q: If a user has a membership at `Group A`, does the permissions associated with that
-membership cascade down to `Group Aa` and `Group Aaa`?
diff --git a/doc/share/authz/POLICY.md b/doc/share/authz/POLICY.md
deleted file mode 100644
index ab2e8f1a..00000000
--- a/doc/share/authz/POLICY.md
+++ /dev/null
@@ -1,124 +0,0 @@
-# Policy
-
-> Policy is a planned system of rules and guidelines that directs users and automation to execute within purposeful boundaries. [1][1]
-
-The parts of a policy include: [1][1]
-
-* name: used to label the policy for future reference
-* purpose: the reason this policy exists
-* situation: the context in which the policy will be used
-* rules: individual controls or prescribed behaviours;
-* actions: action taken if a policy rule is violated
-
-> A policy is a statement that declares which principals are explicitly
-> permitted, or explicitly forbidden, to perform an action on a resource. - [2][2]
-
-## Policy Language
-
-A policy language facilitates: [3][3]
-
-1. the specification of composite policies, which in turn forms the basis of trust delegation.
-1. **the static analysis of policies and system configuration.**
-
-### Policy as Code (PaC)
-
-These are policies that are written, stored, managed and interpreted as code
-artifacts.
-
-> A policy engine is a program or process that is able to ingest
-> machine-readable policies and apply them to a particular problem domain to
-> constrain the behaviour of network resources. [1][1]
-
-PaC policy engine characteristics: [1][1]
-
-* Ingeting machine-readable policies (PaC)
-* Applying policies to specific problem domains (data)
-* Constraining behaviors (outcomes)
-
-```plaintext
- ----------
- | Policy |--------- A
- ---------- | / \
- V / \
- -------- --------- / \ -------------- --------
- | Data |------>| Input |--->< match >--->| Evaluation |--->( Outcom )
- -------- --------- \ / -------------- --------
- A \ /
- --------- | \ /
- | Query |---------- V
- ---------
-```
-
-Selection Criteria: [1][1]
-
-* Alignment
- - Technical Capabilities of team.
- - Internal strategy for how tools and applications are adopted/managed.
- - Fits the need and internal standards driving the decision
- - Primary use cases match our use cases
-* Analytics
- - logging
- - metrics
- - auditing
-* Automation
- - CI/CD Pipelines
- - Automated Deployments
-* Documentation
- - Examples
- - Patterns
- - Understandable
-* Adoption
- - Who is using this?
- - How much adoption has this project seen?
- - Active?
- - Project Maturity
- - Support Model
- - Intuitive
-* Complexity
- - Installation
- - Deployment
- - Configuration
- - Operation Modes (server, library, CLI)
-* Reporting
- * Standard reporting tools e.g. [OSCAL](https://pages.nist.gov/OSCAL/)
-* Security
- * Risks, vulnerabilities
- * Tools and processes for security issue discovery
-* Extensibility
- * Can custom code be written to extend the language.
-
-Scorecard [1][1]
-
-| Selection Criteria | Casbin | Cedar | Rego |
-| ------------------ | ------ | ----- | ---- |
-| Alignment | | | |
-| Analytics | | | |
-| Adoption | | | |
-| Automation | | | |
-| Documentation | | | |
-| Complexity | | | |
-| Reporting | | | |
-| Security | | | |
-| Extensibility | | | |
-| Total | | | |
-
-### Cedar
-
-### Rego
-
-[Rego](https://www.openpolicyagent.org/docs/latest/policy-language/) is a declarative assertion language that provides reasoning. This is a DSL
-for applying reasoning and assertions to domain-agnostic, structured data.
-
-* [Regorus](https://github.com/microsoft/regorus)
- * [Go binding](https://github.com/microsoft/regorus/tree/main/bindings/go)
- * [Ruby binding](https://github.com/microsoft/regorus/tree/main/bindings/ruby)
-
-## See Also
-
-* [Zanzibar](./ZANZIBAR.md)
-* [Dafny](https://dafny.org)
-* [Policy as Code by Jimmy Ray][1]
-
-[1]: https://learning.oreilly.com/library/view/policy-as-code/
-[2]: https://docs.cedarpolicy.com/overview/terminology.html#term-policy
-[3]: https://ucalgary.scholaris.ca/server/api/core/bitstreams/833a86a8-eb7f-4c50-af4d-696b8deb6fd8/content
diff --git a/doc/share/authz/RBAC.md b/doc/share/authz/RBAC.md
deleted file mode 100644
index 2f0054a6..00000000
--- a/doc/share/authz/RBAC.md
+++ /dev/null
@@ -1,103 +0,0 @@
-# Role-Based Access Control (RBAC)
-
-Assigns permissions to roles, which are collections of permissions related to specific job functions. [1][1]
-
-This style of access control aligns with how humans organize themselves within
-organizations by assigning job functions to roles. This model is simple and
-aligns well with how humans operate within their job function.
-
-This model also helps to align security objectives with higher level
-organizational policy (.e.g. ethics, privacy, administration). This type of
-model requires centralized control and enforcement.
-
-> The act of granting membership and specifying transactions for a role is loosely
-> analogous to the process of clearing users (granting membership) and the
-> labeling (associate operation sensitivities) of objects ... - Role-Based Access Controls by Ferraiolo, Kuhn Et el.
-
-> A role can be thought of as a set of transactions that a user or set of users
-> can perform within the context of an organization. - Role-Based Access Controls by Ferraiolo, Kuhn Et el.
-
-Roles are group oriented and they provide a means of naming and describing
-many-to-many relationships between individuals and rights.
-
-1. For each subject, the active role is the one that the subject is currently using:
-
- ```
- AR(s: subject) = { the active role for subject s}.
- ```
-
-2. Each subject may be authorized to perform one or more roles:
-
- ```
- RA(s: subject) = { authorized roles for subject s }.
- ```
-
-3. Each role may be authorized to perform one or more transactions:
-
- ```
- TA(r: role) = { transactions authorized for role r}.
- ```
-
-4. Subjets may execute transactions. The predicate `exec(s,t)` is true if
- subject `s` can execute transaction `t` at the current time, otherwise it is
- false:
-
- ```
- exec(s :subject, t: tran) = true iff subject `s` can execute transaction `t`
- ```
-
-Three basic rules are required:
-
-1. Role assignment: A subject can execute a transaction only if the subject has
- selected or been assigned a role
-2. Role authorization: A subject's active role must be authorized for the
- subject
-3. Transaction authorization: A subject can execute a transaction only if the
- transaction is authorized for the subject's active role
-
-Another user of RBAC is to support integrity. One aspect of integrity is a
-requirement that data and processes be modified only in authorized ways by
-authorized users.
-
-The problem of determining whether data have been modified only in authorized
-ways can be as complex as the transaction that did the modification. For this
-reason, the practical approach is for transactions to be certified and
-trusted. If transactions must be trusted then _access control can be incorporated
-directly into each transaction_.
-
-RBAC makes a decision based on the subject's association with a role. RBAC does
-not easily support multi-factor decisions. RBAC role assignments tend to be
-based upon more static organizational positions, presenting challenges in
-certain RBAC architectures where dynamic access control decisions are required
-(.e.g. `CI_JOB_TOKEN`). Trying to implement these kidns of access control
-decisoins would require the creation of numerous roles that are ad hoc and
-limited in membership, leading to what is often termed "role explosion".
-
-ABAC avoids the need for explicit authorizations to be directly assigned to
-individual subjects prior to a request to perform an operation on the object.
-
-> ABAC: An access control method where subject requests to perform operations on
-> objets are granted or denied based on assigned attributes of the subject,
-> assigned attributes of the object, environment conditions, and a set of
-> policies that are specified in terms of those attributes and conditions.
-
-* Attributes: characteristics of the subject, object, or environment conditions.
-* Subject: is a human or or non-person entity (NPE) that issues access requests
- to perform operations on objects.
-* Object: a system resource for which access is managed by the ABAC system
-* Operation: is the execution of a function at the request of a subject upon an
- object. (e.g. read, write, edit, delete, copy, execute and modify)
-* Policy: is the representation of rules or relationships that makes it possible
- to determine if a requested access should be allowed.
-* Environment conditions: operational or situational context in which the access
- request occurs.
-
-> Roles can inherit from each other and imply permissions. - [1][1]
-
-## See also
-
-* [Role-Based Access Controls][1]
-* [Zanzibar][2]
-
-[1]: https://csrc.nist.gov/files/pubs/conference/1992/10/13/rolebased-access-controls/final/docs/ferraiolo-kuhn-92.pdf
-[2]: https://storage.googleapis.com/gweb-research2023-media/pubtools/5068.pdf
diff --git a/doc/share/authz/README.md b/doc/share/authz/README.md
deleted file mode 100644
index 5b7da3fb..00000000
--- a/doc/share/authz/README.md
+++ /dev/null
@@ -1,69 +0,0 @@
-# Authz
-
-## Access Control Models
-
-Access Controls provide a means of restricting access to objects based on the
-identity of subjects and/or groups to which they belong.
-
-* Role-Based Access Control ([RBAC](./RBAC.md))
-* Relationship-Based Access Control ([ReBAC](./ReBAC.md))
-* Attribute-Based Access Control ([ABAC](./ABAC.md))
-
-Authentication (Authn) is used to determine that users or systems are who they
-claim to be and provide proof in the form of identity principals and attributes.
-
-Authorization (Authz) is used to decide what privileges an actor has within a
-system.
-
-## Policy
-
-* [What is a policy?](./POLICY.md)
-* Policy Language Evaluation
- * Casbin
- * Cedar
- * [Dafny](https://dafny.org/)
- * Rego
-
-Criteria for evaluating policy languages:
-
-* Must be able to model different types of access control models (RBAC, ReBAC, ABAC)
-* Must be able to perform static analysis
-* Must be actively supported
-* Must have concise documentation
-* Must provide ability to extend language using Ruby/Golang for describing complex policies.
-* Must have a compatible license that permits the way that we can use it.
-
-Ideally, we must be able to model the following relationships:
-
-| type | required |
-| ---- | -------- |
-| `user-to-resource` | required |
-| `resource-to-resource` | required |
-| `user-to-user` | not required |
-
-Note: `user-to-user` relationships are not in the current access control model.
-
-## Architecture
-
-```plaintext
- ------------------ ------- -------------
- | Users/Services |--->| PEP |--->| Resources |
- ------------------ ------- -------------
- | A
- V |
- ------- ------------ -------
- | PDP |-->| Policies |<---| PAP |
- ------- ------------ -------
- | A A
- V | |
- ------- -----------------
- | PIP | | Administrator |
- ------- -----------------
-
-PAP: Policy Administration Point
-PDP: Policy Decision Point
-PEP: Policy Enforcement Point
-PIP: Policy Information Point
-```
-
-* [Kubernetes Policy Management](https://github.com/kubernetes/sig-security/blob/main/sig-security-docs/papers/policy/kubernetes-policy-management.pdf)
diff --git a/doc/share/authz/ReBAC.md b/doc/share/authz/ReBAC.md
deleted file mode 100644
index 9202e0c4..00000000
--- a/doc/share/authz/ReBAC.md
+++ /dev/null
@@ -1,149 +0,0 @@
-# Relationship-Based Access Control (ReBAC)
-
-> Authorization decisions are based on the relationship between the resource owner
-> and the resource accessor in a social network maintained by the protection
-> system. [1]
-
-A Social Network System (SNS) maintains a social network for at least two reason:
-
-1. It is used by the users to navigate the information space of the system
-2. The social network is used as a basis for formulating the access control
- policies of user contributed resources.
-
-Access Control Paradigm:
-
-1. the explicit tracking of one or more social networks by the protection system
-1. the expression of access control policies in terms of the relationship
- between the resource owner and the resource accessor
-
-Suited for domains in which relationship and authorization decisions are from
-the structure of trust that is inherent in the application domain rather than
-subjective assessment of users.
-
-It is more natural to base authz decisions on whether the resource owner and
-accessor are in a particular kind of relationship.
-
-In a standard RBAC system, when a permission `p` is assigned to role `R`, we are
-essentially formulating the following policy: `grant p to user u if R(u)`.
-
-PriMA is another recently proposed privacy protection mechanism for SNSs.
-
-Unlike [RBAC][4] and [ABAC][3], ReBAC policies deal with relational policies
-rather than unary predicates on attributes and roles.
-
-In, Online Social Network (OSN) applications, authorization for users' access to
-specific content is typically based on ther interpersonal relationships between
-the accessing user and content owner. OSN ReBAC models user user-to-user
-relationships.
-
-The user of indirect relations, also called multilevel or composite relations,
-is fundamental to ReBAC (e.g. friend of friend).
-
-Recently, researchers have proposed extended ReBAC models applicable to other
-computing systems beyond OSNs.
-
-Most ReBAC models consider `user-to-user` and possibly `user-to-resource`
-relationshps. Very few consider `resource-to-resource` relationships. Models
-that consider `resource-to-resource` relationships typically do so through
-users.
-
-## Access Control Policies
-
-Let `U` be the set of all users in the system.
-Accesses are directed against resources. A resource may represent one or more
-objects or certain system operations. Let `R` be the set of resources protected
-by the SNS. A typical member of `R` is denoted by `r`.
-
-Assocated with every access request are therefore the following:
-
-* a protected resource that is being accessed
-* the owner of that resource
-* the accessor of that resource who requests access.
-
-Owner of a resource implies that the accessor must be in a specific kind of
-relationship with the owner in order be granted. Huh?
-
-Associated with every resource is an `access control policy`.
-Policy is modeled as a ternary predicate: `U x U x G(U, I) => {0, 1}`.
-
-## Protection System
-
-A protection system `N` is a 7-tuple `(I, U, R, C, C0, policy, owner)` where:
-
-* `I` is the set of relation identifiers (See my question in the README about resource identifiers in our system)
-* `U` is a finite set of users in the system
-* `R` is a finite set of resources to be protected by the system.
-* `C` is a _infinite_ universe of `access contexts`.
- * `C0` is the root context. (* Could this be the root `Organization` that a `User` belongs to?)
-* `R => PP(U, I)` assigns a policy predicate to every resource in the system. (This means that every resource is addressable through a universal identifier, right? Goodbye `bigint`? Yay!)
-* `owner: R -> U` is a function that assigns an owner to every resource in the system.
-
-Where the access control policy of a resource comes from doesn't matter. The
-possibilities are:
-
-1. Mandatory: Some policies are mandated by the system administrator
-1. Discretionary: The resource owners are responsible for specifying their
- access control policies
-1. Policy Vocabulary: A set of policy predicates from which users take their
- picks. (friends, friends-of-friends)
-
-## Protection State
-
-Given a protection system `N = {I,U,R,C,C0,policy,owner}`, a protection state is
-a triple `{C,sn,extends}` composed of the following elements:
-
-* `C`: is the set of active contexts in the state. This set is infinite and
- non-empty.
-* function `sn : C -> G(U,I)` that maps each context of the state to a social
- network `sn(s)` records the relationships that have been articulated in
- context `c`.
-* `extends ~= C x C` is a binary relation defined over `C`, such that:
- * a: the directed graph is a tree
- * b: `c0` is the root of the tree
- * c: if `(c1,c2) ~= extends` then `c1` is the child of `c2` in the tree. The
- extends relationship defines a **context hierarchy**.
-
-## Authorization
-
-Authz is achieved by consulting relationships in a social network. Authorization
-decisions are made primarily by consulting the relationship between accessor and
-the owner. In a real implementations, it is possible for the system to have a
-hybrid authorization scheme that is both relationship based and role based.
-
-Relationship inheritance allows relationships articulated in ancestor contexts
-to be inherited by the effictive social network of descendant contexts. The
-social network of a child context contains no less relationships than that of a
-parent context.
-
-## Policy Language
-
-It is desirable to have a [policy language](./POLICY.md) for specifying ReBAC policies.
-
-A policy language facilitates:
-
-1. the specification of composite policies, which in turn forms the basis of trust delegation.
-1. **the static analysis of policies and system configuration.**
-
-## Context Hierarchy
-
-The context hierarchy assumes a tree shape: i.e., only single inheritance is permitted.
-Multiple inheritance corresponds to a more flexible means of constraining when
-relationships can be "activated" simultaneously.
-
-## Conclusion
-
->Relationship-Based Access Control works best in application domains in which
->binary relations are more natural for expressing authorization decisions than
->unary relations (e.g., roles).
-
-## See also
-
-* [Relationship-Based Access Control: Protection Model and Policy Language by Philip W. L. Fong][1]
-* [Learning Relationship-Based Access Control Policies from Black-Box Systems][2]
-* [Classifying and Comparing Attribute-Based and Relationship-Based Access Control][5]
-
-[1]: https://cspages.ucalgary.ca/~pwlfong/Pub/codaspy2011.pdf
-[2]: https://dl.acm.org/doi/pdf/10.1145/3517121
-[3]: ./ABAC.md
-[4]: ./RBAC.md
-[5]: https://dl.acm.org/doi/pdf/10.1145/3029806.3029828
diff --git a/doc/share/authz/SLIDES.md b/doc/share/authz/SLIDES.md
deleted file mode 100644
index c14387bc..00000000
--- a/doc/share/authz/SLIDES.md
+++ /dev/null
@@ -1,207 +0,0 @@
-# Authx = Authn + Authz
-
-* Authentication: Are you who you say you are?
-* Authorization: Are you allowed to do that?
-
-# Authx - Examples
-
-1. Travel by Plane (High security context)
- * Authentication: Passport
- * Authorization: Boarding Pass
-1. Travel by Bus (Low security context)
- * Authentication: Not required
- * Authorization: Bus ticket
-
-# Authx
-
-The Resource Server provides the security context and knows if the resource that
-is being access requires a high or low security context.
-
-# Authx
-
-Not every resource requires a high security context.
-
-i.e. we don't need to make a network call to the PDP for every single authorization decision if the security context is low.
-
-# Authx - Challenges
-
-* PKI: key rotation, revocation, signing, encryption
-* Uptime Guarantees
-* Auditability
-* Complexity
-* Interoperability
-* Extensibility
-* Observability
-* ...
-
-# OAuth 2.x
-
-OAuth is for Authorization.
-
-# OAuth 2.x - Protocol Flow
-
-```plaintext
- +--------+ +---------------+
- | |--(A)- Authorization Request ->| Resource |
- | | | Owner |
- | |<-(B)-- Authorization Grant ---| |
- | | +---------------+
- | |
- | | +---------------+
- | |--(C)-- Authorization Grant -->| Authorization |
- | Client | | Server |
- | |<-(D)----- Access Token -------| |
- | | +---------------+
- | |
- | | +---------------+
- | |--(E)----- Access Token ------>| Resource |
- | | | Server |
- | |<-(F)--- Protected Resource ---| |
- +--------+ +---------------+
-```
-
-https://datatracker.ietf.org/doc/html/rfc6749#section-1.2
-
-# OAuth 2.x - Protocol Endpoints
-
-The authorization process utilizes two authorization server endpoints (HTTP resources):
-
-- Authorization endpoint - used by the client to obtain authorization from the resource owner via user-agent redirection.
-- Token endpoint - used by the client to exchange an authorization grant for an access token, typically with client authentication.
-
-https://datatracker.ietf.org/doc/html/rfc6749#section-3
-
-# OAuth 2.x - Authorization Grants
-
-* Authorization Code: `authorization_code`
-* JWT Bearer: `urn:ietf:params:oauth:grant-type:jwt-bearer`
-* Refresh Token: `refresh_token`
-* SAML Assertion: `urn:ietf:params:oauth:grant-type:saml2-bearer`
-
-# OAuth 2.x - Authorization Code Grant Protocol Flow
-
-```plaintext
- +----------+
- | Resource |
- | Owner |
- | |
- +----------+
- ^
- |
- (B)
- +----|-----+ Client Identifier +---------------+
- | -+----(A)-- & Redirection URI ---->| |
- | User- | | Authorization |
- | Agent -+----(B)-- User authenticates --->| Server |
- | | | |
- | -+----(C)-- Authorization Code ---<| |
- +-|----|---+ +---------------+
- | | ^ v
- (A) (C) | |
- | | | |
- ^ v | |
- +---------+ | |
- | |>---(D)-- Authorization Code ---------' |
- | Client | & Redirection URI |
- | | |
- | |<---(E)----- Access Token -------------------'
- +---------+ (w/ Optional Refresh Token)
-```
-
-# OAuth 2.x - Implicit Grant (not a recommendation)
-
-```plaintext
- +--------------------+
- | Resource Owner |
- +--------------------+
- ^
- (B)
- +----|-----+ Client Identifier +---------------+
- | +----(A)-- & Redirection URI --->| Authorization |
- | User- | | Server |
- | Agent -|----(B)-- User authenticates -->| |
- | |<---(C)--- Redirection URI ----<+---------------+
- | | with Access Token
- | | +---------------+
- | |----(D)--- Redirection URI ---->| Web-Hosted |
- | | without Fragment | Client |
- | | | Resource |
- | (F) |<---(E)------- Script ---------<+---------------+
- +-|--------+
- (A) (G) Access Token
- ^ v
- +---------+
- | Client |
- +---------+
-```
-
-# OAuth 2.x - Refresh Token Grant Protocol Flow
-
-```plaintext
- +--------+ +---------------+
- | |--(A)------- Authorization Grant --------->| |
- | | | |
- | |<-(B)----------- Access Token -------------| |
- | | & Refresh Token | |
- | | | |
- | | +----------+ | |
- | |--(C)---- Access Token ---->| | | |
- | | | | | |
- | |<-(D)- Protected Resource --| Resource | | Authorization |
- | Client | | Server | | Server |
- | |--(E)---- Access Token ---->| | | |
- | | | | | |
- | |<-(F)- Invalid Token Error -| | | |
- | | +----------+ | |
- | | | |
- | |--(G)----------- Refresh Token ----------->| |
- | | | |
- | |<-(H)----------- Access Token -------------| |
- +--------+ & Optional Refresh Token +---------------+
-```
-
-# OpenID Connect (OIDC)
-
-OIDC adds Authn to OAuth.
-
-# OIDC - Protocol Flow
-
-OIDC = Authn + OAuth
-
-```plaintext
-+--------+ +--------+
-| | | |
-| |---------(1) AuthN Request-------->| |
-| | | |
-| | +--------+ | |
-| | | | | |
-| | | End- |<--(2) AuthN & AuthZ-->| |
-| | | User | | |
-| RP | | | | OP |
-| | +--------+ | |
-| | | |
-| |<--------(3) AuthN Response--------| |
-| | | |
-| |---------(4) UserInfo Request----->| |
-| | | |
-| |<--------(5) UserInfo Response-----| |
-| | | |
-+--------+ +--------+
-```
-
-https://openid.net/specs/openid-connect-core-1_0.html#Overview
-
-# WLIF
-
-TODO:: Add a protocol flow diagram
-
-* https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation#how-it-works
-* https://docs.google.com/document/d/1XyuQXuUJE0kGC2jqy_vaLPGxAFjzMvJWOS74QoP7UA8/
-
-# Primitives
-
-We need:
-
-* PKI: Certificate Authority generate and sign intermediate certs
-* OAuth 2.x Authorization Server
-* OIDC Provider (OP)
diff --git a/doc/share/authz/ZANZIBAR.md b/doc/share/authz/ZANZIBAR.md
deleted file mode 100644
index fc49ab2d..00000000
--- a/doc/share/authz/ZANZIBAR.md
+++ /dev/null
@@ -1,51 +0,0 @@
-# Zanzibar
-
-Zanzibar is a planet-scale distributed ACL storage and evaluation system.
-Zanzibar's data model supports permissions, users, groups, and inheritance as
-found in the above systems. [1][1]
-
-Zanzibar servers delegate checks and reads to each other based on consistent
-hashing. Both the caller and the callee sides of the delegated operations cache
-the results to prevent hot spots. Zanzibar handles 22 million internal RPCs per
-second. In-memory caching handles approximately 200 million lookups per second
-at peak.
-
-Clients include:
-
-* Google Calendar
-* Google Cloud
-* Google Drive
-* Google Maps
-* Google Photos
-* YouTube
-
-Lessons learned
-
-* Access control patterns vary widely
-* Freshness requirements are often but not always loose. (i.e. moderate staleness during ACL evaluation is okay)
-* Performance optimizations
- * Request hedging is key to reducing tail latency. (Search issues 10s-100s of authz checks to serve a single set of search results.)
- * Hot-spot mitigation is critical for high availability: some workloads create hot spots in ACL data that can overwhelm database servers.
- * Performance isolation is indispensible to protect against misbehaving clients. (e.g. new feature launch that is popular. Unpredicatable usage patterns.)
-
-Roles are similar to Zanzibar relations. Roles can inherit from each other and
-imply permissions. Clients have implemented RBAC policies on top of Zanzibar's
-namespace configuration.
-
-Most IAM systems offer:
-
-* assigning users to roles or groups
-* domain-specific policy languages
-* API to edit ACLs.
-
-These systems have a communit unified ACL storage and and RPC-based API.
-
-It evaluates deeply or widely nested group membership with Leopard, a
-specialized index for efficient computation of set operations with snapshot
-consistency.
-
-## See Also
-
-* [1][1]
-
-[1]: https://storage.googleapis.com/gweb-research2023-media/pubtools/5068.pdf
diff --git a/doc/share/authz/todo/001_ldap.md b/doc/share/authz/todo/001_ldap.md
deleted file mode 100644
index 408cc771..00000000
--- a/doc/share/authz/todo/001_ldap.md
+++ /dev/null
@@ -1,19 +0,0 @@
-As an `Owner`, I want to `control group membership via LDAP`, so that `I can manage users in my company using my existing LDAP server`.
-
-# SYNOPSIS
-
-An external LDAP server should be able to synchronize data to the IdP.
-
-# DESCRIPTION
-
-# SEE ALSO
-
-* [ ] TBD
-
-# Tasks
-
-* [ ] TBD
-
-# Acceptance Criteria
-
-* [ ] TBD
diff --git a/doc/share/authz/todo/002_hierarchical_membership.md b/doc/share/authz/todo/002_hierarchical_membership.md
deleted file mode 100644
index 224eed7a..00000000
--- a/doc/share/authz/todo/002_hierarchical_membership.md
+++ /dev/null
@@ -1,19 +0,0 @@
-As an `Owner`, I want to `manage group membership`, so that `I can manage users in my company`.
-
-# SYNOPSIS
-
-Owners of an Organization can create group hierarchies and manage memberships to those groups.
-
-# DESCRIPTION
-
-# SEE ALSO
-
-* [ ] TBD
-
-# Tasks
-
-* [ ] TBD
-
-# Acceptance Criteria
-
-* [ ] TBD
diff --git a/doc/share/authz/todo/003_workload_identity_federation.md b/doc/share/authz/todo/003_workload_identity_federation.md
deleted file mode 100644
index a93353a7..00000000
--- a/doc/share/authz/todo/003_workload_identity_federation.md
+++ /dev/null
@@ -1,21 +0,0 @@
-As an `Operator`, I want to `integrate multicloud workloads`, so that `I can operate multiple clouds include on-premises`.
-
-# SYNOPSIS
-
-I have no idea what workload identity federation is but it sounds like a neat
-way to federate authn to an external/central identity provider to interop
-between multiple clouds.
-
-# DESCRIPTION
-
-# SEE ALSO
-
-* https://cloud.google.com/iam/docs/workload-identity-federation
-
-# Tasks
-
-* [ ] TBD
-
-# Acceptance Criteria
-
-* [ ] TBD
diff --git a/doc/share/authz/todo/004_acme_protocol.md b/doc/share/authz/todo/004_acme_protocol.md
deleted file mode 100644
index 93628f77..00000000
--- a/doc/share/authz/todo/004_acme_protocol.md
+++ /dev/null
@@ -1,69 +0,0 @@
-As an `Operator`, I want to `use the ACME protocol`, so that `I have a standards based way to manage PKI`.
-
-# SYNOPSIS
-
-Use the ACME protocol to generate TLS PKI.
-
-# DESCRIPTION
-
-Below is an example startup configuration for using the ACME protocol in golang
-and distributing the key material using file storage. The file storage should be
-replaced with a distributed blob storage that is locked down. This also uses
-the `STEPPATH` environment variable to access an internal root certificate
-authority.
-
-```golang
-func WithAcmeTLS(ctx context.Context, directoryURL string, cacheDir string) cfg.Option {
- storageFor := func(config *cfg.Config) certmagic.Storage {
- return &certmagic.FileStorage{Path: cacheDir}
- }
- return func(config *cfg.Config) {
- host := os.Getenv("HOST")
- tls := srv.NewTLS(ctx, host, storageFor(config), []certmagic.ACMEIssuer{
- {
- Agreed: true,
- CA: directoryURL,
- DisableHTTPChallenge: true,
- Email: "everyone@example.com",
- TestCA: directoryURL,
- TrustedRoots: newCertPool(),
- AltTLSALPNPort: bindingPort(),
- },
- })
- config.TLS = x.Must(tls.Config())
- }
-}
-
-func newCertPool() *x509.CertPool {
- certPool := x.Must(x509.SystemCertPool())
- certPool.AddCert(func() *x509.Certificate {
- block, _ := pem.Decode(x.Must(ioutil.ReadFile(
- filepath.Join(os.ExpandEnv("$STEPPATH"), "/certs/root_ca.crt"),
- )))
- return x.Must(x509.ParseCertificate(block.Bytes))
- }())
- return certPool
-}
-
-func bindingPort() int {
- parts := strings.SplitN(os.Getenv("BIND_ADDR"), ":", 2)
- bindPort, err := strconv.Atoi(parts[1])
- if err != nil {
- bindPort = 0
- }
- return bindPort
-}
-```
-
-# SEE ALSO
-
-* [RFC-8555](https://datatracker.ietf.org/doc/html/rfc8555)
-* [$STEPPATH](https://smallstep.com/docs/step-cli/reference/path/#examples)
-
-# Tasks
-
-* [ ] TBD
-
-# Acceptance Criteria
-
-* [ ] TBD
diff --git a/doc/share/authz/todo/005_open_pubkey.md b/doc/share/authz/todo/005_open_pubkey.md
deleted file mode 100644
index 92b0b7a8..00000000
--- a/doc/share/authz/todo/005_open_pubkey.md
+++ /dev/null
@@ -1,20 +0,0 @@
-As an `Owner`, I want to `enable ssh access via open pubkey`, so that `have a standard based approach to connect over SSH`.
-
-# SYNOPSIS
-
-Research OpenPubKey to see how it operates on top of OIDC to provide ephemeral
-SSH keys for temporary access.
-
-# DESCRIPTION
-
-# SEE ALSO
-
-* https://docs.bastionzero.com/openpubkey-ssh
-
-# Tasks
-
-* [ ] TBD
-
-# Acceptance Criteria
-
-* [ ] TBD
diff --git a/doc/share/authz/todo/006_zero_knowledge_proof.md b/doc/share/authz/todo/006_zero_knowledge_proof.md
deleted file mode 100644
index eb3984e9..00000000
--- a/doc/share/authz/todo/006_zero_knowledge_proof.md
+++ /dev/null
@@ -1,17 +0,0 @@
-As an `Developer`, I want to `understand what a zero knowledge proof is`, so that `I can understand how to be considerate of privacy`.
-
-# SYNOPSIS
-
-# DESCRIPTION
-
-# SEE ALSO
-
-* https://dl.acm.org/doi/pdf/10.1145/3708622.3708634
-
-# Tasks
-
-* [ ] TBD
-
-# Acceptance Criteria
-
-* [ ] TBD
diff --git a/go.mod b/go.mod
index 82570abf..fc3cd024 100644
--- a/go.mod
+++ b/go.mod
@@ -1,242 +1,111 @@
-module gitlab.com/mokhax/spike
+module gitlab.com/gitlab-org/software-supply-chain-security/authorization/authz.d
go 1.24.0
require (
- github.com/caddyserver/certmagic v0.22.2
github.com/casbin/casbin/v3 v3.0.0-beta.7
- github.com/cedar-policy/cedar-go v1.1.1
- github.com/lestrrat-go/jwx/v3 v3.0.0-alpha3
+ github.com/cedar-policy/cedar-go v1.2.1
+ github.com/lestrrat-go/jwx/v3 v3.0.1
github.com/magefile/mage v1.15.0
- github.com/playwright-community/playwright-go v0.5001.0
github.com/rs/zerolog v1.34.0
github.com/stretchr/testify v1.10.0
- github.com/xlgmokha/x v0.0.0-20250404223908-0b29f54f06e7
- go.uber.org/zap v1.27.0
- golang.org/x/oauth2 v0.28.0
+ github.com/twitchtv/twirp v8.1.3+incompatible
+ github.com/xlgmokha/x v0.0.0-20250430185455-b691eda27477
google.golang.org/grpc v1.71.0
google.golang.org/protobuf v1.36.6
)
require (
- cloud.google.com/go v0.118.3 // indirect
cloud.google.com/go/auth v0.15.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
- cloud.google.com/go/iam v1.4.1 // indirect
- cloud.google.com/go/kms v1.21.0 // indirect
- cloud.google.com/go/longrunning v0.6.6 // indirect
- cloud.google.com/go/security v1.18.4 // indirect
- dario.cat/mergo v1.0.1 // indirect
- filippo.io/edwards25519 v1.1.0 // indirect
- github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect
- github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect
- github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect
- github.com/Masterminds/goutils v1.1.1 // indirect
- github.com/Masterminds/semver/v3 v3.3.0 // indirect
- github.com/Masterminds/sprig/v3 v3.3.0 // indirect
- github.com/Microsoft/go-winio v0.6.2 // indirect
- github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
+ github.com/Knetic/govaluate v3.0.1-0.20250325060307-7625b7f8c03d+incompatible // indirect
github.com/a8m/envsubst v1.4.2 // indirect
github.com/arthurnn/twirp-ruby v1.13.0 // indirect
- github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
- github.com/aws/aws-sdk-go v1.51.8 // indirect
- github.com/aws/aws-sdk-go-v2 v1.36.1 // indirect
- github.com/aws/aws-sdk-go-v2/config v1.29.6 // indirect
- github.com/aws/aws-sdk-go-v2/credentials v1.17.59 // indirect
- github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 // indirect
- github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 // indirect
- github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 // indirect
- github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 // indirect
- github.com/aws/aws-sdk-go-v2/service/kms v1.37.18 // indirect
- github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 // indirect
- github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 // indirect
- github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 // indirect
- github.com/aws/smithy-go v1.22.2 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bitly/go-simplejson v0.5.1 // indirect
- github.com/boombuler/barcode v1.0.1 // indirect
github.com/bsm/redislock v0.9.4 // indirect
- github.com/caddyserver/zerossl v0.1.3 // indirect
- github.com/ccoveille/go-safecast v1.6.0 // indirect
- github.com/cenkalti/backoff/v4 v4.3.0 // indirect
- github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
- github.com/chzyer/readline v1.5.1 // indirect
github.com/coreos/go-oidc/v3 v3.13.0 // indirect
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect
- github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deckarep/golang-set/v2 v2.7.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
- github.com/dgraph-io/badger v1.6.2 // indirect
- github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
- github.com/dgraph-io/ristretto v0.1.1 // indirect
- github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
- github.com/dustin/go-humanize v1.0.1 // indirect
- github.com/fatih/color v1.16.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
- github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // indirect
- github.com/go-chi/chi/v5 v5.2.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
- github.com/go-piv/piv-go/v2 v2.3.0 // indirect
- github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/go-stack/stack v1.8.1 // indirect
- github.com/goccy/go-json v0.10.3 // indirect
+ github.com/goccy/go-json v0.10.5 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
- github.com/golang/glog v1.2.4 // indirect
github.com/golang/protobuf v1.5.4 // indirect
- github.com/golang/snappy v0.0.4 // indirect
- github.com/google/btree v1.1.3 // indirect
- github.com/google/certificate-transparency-go v1.2.2 // indirect
github.com/google/go-tpm v0.9.3 // indirect
- github.com/google/go-tpm-tools v0.4.5 // indirect
- github.com/google/go-tspi v0.3.0 // indirect
- github.com/google/jsonapi v1.0.0 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
github.com/gorilla/mux v1.8.1 // indirect
- github.com/hashicorp/errwrap v1.1.0 // indirect
- github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
- github.com/hashicorp/go-hclog v1.6.3 // indirect
- github.com/hashicorp/go-multierror v1.1.1 // indirect
- github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
- github.com/hashicorp/go-rootcerts v1.0.2 // indirect
- github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 // indirect
- github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect
- github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
- github.com/hashicorp/go-sockaddr v1.0.2 // indirect
- github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
- github.com/hashicorp/vault/api v1.16.0 // indirect
- github.com/hashicorp/vault/api/auth/approle v0.9.0 // indirect
- github.com/hashicorp/vault/api/auth/aws v0.9.0 // indirect
- github.com/hashicorp/vault/api/auth/kubernetes v0.9.0 // indirect
- github.com/huandu/xstrings v1.5.0 // indirect
- github.com/jackc/pgpassfile v1.0.0 // indirect
- github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
- github.com/jackc/pgx/v5 v5.6.0 // indirect
- github.com/jackc/puddle/v2 v2.2.1 // indirect
- github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect
github.com/justinas/alice v1.2.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
- github.com/klauspost/cpuid/v2 v2.2.10 // indirect
- github.com/kylelemons/godebug v1.1.0 // indirect
- github.com/lestrrat-go/blackmagic v1.0.2 // indirect
+ github.com/lestrrat-go/blackmagic v1.0.3 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
- github.com/lestrrat-go/httprc/v3 v3.0.0-beta1 // indirect
+ github.com/lestrrat-go/httprc/v3 v3.0.0-beta2 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
- github.com/libdns/libdns v0.2.3 // indirect
github.com/magiconair/properties v1.8.7 // indirect
- github.com/manifoldco/promptui v0.9.0 // indirect
- github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa // indirect
- github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
- github.com/mholt/acmez/v3 v3.1.1 // indirect
- github.com/miekg/dns v1.1.63 // indirect
- github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/minio/highwayhash v1.0.3 // indirect
- github.com/mitchellh/copystructure v1.2.0 // indirect
- github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
- github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nats-io/jwt/v2 v2.7.3 // indirect
github.com/nats-io/nats-server/v2 v2.11.1-0.20250328102232-bfb8894825bc // indirect
github.com/nats-io/nkeys v0.4.10 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
- github.com/newrelic/go-agent/v3 v3.37.0 // indirect
github.com/oauth2-proxy/oauth2-proxy/v7 v7.8.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
- github.com/peterbourgon/diskv/v3 v3.0.1 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
- github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
+ github.com/playwright-community/playwright-go v0.5001.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
- github.com/pquerna/otp v1.4.0 // indirect
github.com/prometheus/client_golang v1.21.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/redis/go-redis/v9 v9.7.3 // indirect
- github.com/rs/xid v1.6.0 // indirect
- github.com/russross/blackfriday/v2 v2.1.0 // indirect
- github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
- github.com/schollz/jsonstore v1.1.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
- github.com/shopspring/decimal v1.4.0 // indirect
- github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
- github.com/sirupsen/logrus v1.9.3 // indirect
- github.com/slackhq/nebula v1.9.5 // indirect
- github.com/smallstep/certificates v0.28.3 // indirect
- github.com/smallstep/certinfo v1.13.0 // indirect
- github.com/smallstep/cli v0.28.6 // indirect
- github.com/smallstep/cli-utils v0.12.1 // indirect
- github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 // indirect
- github.com/smallstep/linkedca v0.23.0 // indirect
- github.com/smallstep/nosql v0.7.0 // indirect
- github.com/smallstep/pkcs7 v0.2.1 // indirect
- github.com/smallstep/scep v0.0.0-20240926084937-8cf1ca453101 // indirect
- github.com/smallstep/truststore v0.13.0 // indirect
- github.com/smallstep/zcrypto v0.0.0-20221001003018-1ab2364d2a91 // indirect
- github.com/smallstep/zlint v0.0.0-20220930192201-67fb4aa21910 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
- github.com/thales-e-security/pool v0.0.2 // indirect
- github.com/twitchtv/twirp v8.1.3+incompatible // indirect
- github.com/urfave/cli v1.22.16 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
- github.com/weppos/publicsuffix-go v0.20.0 // indirect
- github.com/x448/float16 v0.8.4 // indirect
- github.com/zeebo/blake3 v0.2.4 // indirect
- go.etcd.io/bbolt v1.3.11 // indirect
- go.mozilla.org/pkcs7 v0.9.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
- go.step.sm/crypto v0.59.1 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
- go.uber.org/zap/exp v0.3.0 // indirect
- golang.org/x/crypto v0.36.0 // indirect
- golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
- golang.org/x/mod v0.24.0 // indirect
+ golang.org/x/crypto v0.37.0 // indirect
+ golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/net v0.37.0 // indirect
- golang.org/x/sync v0.12.0 // indirect
- golang.org/x/sys v0.31.0 // indirect
- golang.org/x/term v0.30.0 // indirect
- golang.org/x/text v0.23.0 // indirect
+ golang.org/x/oauth2 v0.28.0 // indirect
+ golang.org/x/sync v0.13.0 // indirect
+ golang.org/x/sys v0.32.0 // indirect
+ golang.org/x/text v0.24.0 // indirect
golang.org/x/time v0.11.0 // indirect
- golang.org/x/tools v0.31.0 // indirect
google.golang.org/api v0.228.0 // indirect
- google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect
@@ -244,10 +113,7 @@ require (
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- howett.net/plist v1.0.0 // indirect
k8s.io/apimachinery v0.32.3 // indirect
- k8s.io/klog/v2 v2.130.1 // indirect
- software.sslmate.com/src/go-pkcs12 v0.5.0 // indirect
)
tool (
@@ -256,8 +122,6 @@ tool (
github.com/nats-io/nats-server/v2
github.com/oauth2-proxy/oauth2-proxy/v7
github.com/playwright-community/playwright-go/cmd/playwright
- github.com/smallstep/certificates/cmd/step-ca
- github.com/smallstep/cli/cmd/step
github.com/twitchtv/twirp/protoc-gen-twirp
google.golang.org/grpc/cmd/protoc-gen-go-grpc
google.golang.org/protobuf/cmd/protoc-gen-go
diff --git a/go.sum b/go.sum
index cd329892..c1cae74b 100644
--- a/go.sum
+++ b/go.sum
@@ -1,59 +1,15 @@
-cloud.google.com/go v0.118.3 h1:jsypSnrE/w4mJysioGdMBg4MiW/hHx/sArFpaBWHdME=
-cloud.google.com/go v0.118.3/go.mod h1:Lhs3YLnBlwJ4KA6nuObNMZ/fCbOQBPuWKPoE0Wa/9Vc=
cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps=
cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
-cloud.google.com/go/iam v1.4.1 h1:cFC25Nv+u5BkTR/BT1tXdoF2daiVbZ1RLx2eqfQ9RMM=
-cloud.google.com/go/iam v1.4.1/go.mod h1:2vUEJpUG3Q9p2UdsyksaKpDzlwOrnMzS30isdReIcLM=
-cloud.google.com/go/kms v1.21.0 h1:x3EeWKuYwdlo2HLse/876ZrKjk2L5r7Uexfm8+p6mSI=
-cloud.google.com/go/kms v1.21.0/go.mod h1:zoFXMhVVK7lQ3JC9xmhHMoQhnjEDZFoLAr5YMwzBLtk=
-cloud.google.com/go/longrunning v0.6.6 h1:XJNDo5MUfMM05xK3ewpbSdmt7R2Zw+aQEMbdQR65Rbw=
-cloud.google.com/go/longrunning v0.6.6/go.mod h1:hyeGJUrPHcx0u2Uu1UFSoYZLn4lkMrccJig0t4FI7yw=
-cloud.google.com/go/security v1.18.4 h1:vY/Z2D+bE9PqdZNiPpW+RLSzDNDVWkNDFKdCnqOeCis=
-cloud.google.com/go/security v1.18.4/go.mod h1:+oNVB34sloqG2K3IpoT2KUDgNAbAJ9A2uENjAUvgzRQ=
-dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
-dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
-filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
-filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
-github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
-github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
-github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 h1:m/sWOGCREuSBqg2htVQTBY8nOZpyajYztF0vUvSZTuM=
-github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0/go.mod h1:Pu5Zksi2KrU7LPbZbNINx6fuVrUp/ffvpxdDj+i8LeE=
-github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 h1:FbH3BbSb4bvGluTesZZ+ttN/MDsnMmQP36OSnDuSXqw=
-github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA=
-github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
-github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb h1:ZVN4Iat3runWOFLaBCDVU5a9X/XikSRBosye++6gojw=
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
-github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
-github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
-github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
-github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
-github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
-github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
-github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
-github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
-github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY+9ef8E=
-github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE=
+github.com/Knetic/govaluate v3.0.1-0.20250325060307-7625b7f8c03d+incompatible h1:PQkGQvISFXAw+Lkmcyd5OUGDVtdQdY1u0CIDjDbBg64=
+github.com/Knetic/govaluate v3.0.1-0.20250325060307-7625b7f8c03d+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/a8m/envsubst v1.4.2 h1:4yWIHXOLEJHQEFd4UjrWDrYeYlV7ncFWJOCBRLOZHQg=
github.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
@@ -62,99 +18,35 @@ github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQg
github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8=
github.com/antithesishq/antithesis-sdk-go v0.4.3-default-no-op h1:+OSa/t11TFhqfrX0EOSqQBDJ0YlpmK0rDSiB19dg9M0=
github.com/antithesishq/antithesis-sdk-go v0.4.3-default-no-op/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl3v2yvUZjmKncl7U91fup7E=
-github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
-github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/arthurnn/twirp-ruby v1.13.0 h1:j0T7I5oxe2niKFdfjiiCmkiydwYeegrbwVMs+Gajm6M=
github.com/arthurnn/twirp-ruby v1.13.0/go.mod h1:1fVOQuSLzwXoPi9/ejlDYG3roilJIPAZN2sw+A3o48o=
-github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
-github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
-github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
-github.com/aws/aws-sdk-go v1.51.8 h1:tD7gQq5XKuKdhA6UMEH26ZNQH0s+HbL95rzv/ACz5TQ=
-github.com/aws/aws-sdk-go v1.51.8/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
-github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E=
-github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM=
-github.com/aws/aws-sdk-go-v2/config v1.29.6 h1:fqgqEKK5HaZVWLQoLiC9Q+xDlSp+1LYidp6ybGE2OGg=
-github.com/aws/aws-sdk-go-v2/config v1.29.6/go.mod h1:Ft+WLODzDQmCTHDvqAH1JfC2xxbZ0MxpZAcJqmE1LTQ=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.59 h1:9btwmrt//Q6JcSdgJOLI98sdr5p7tssS9yAsGe8aKP4=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.59/go.mod h1:NM8fM6ovI3zak23UISdWidyZuI1ghNe2xjzUZAyT+08=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 h1:KwsodFKVQTlI5EyhRSugALzsV6mG/SGrdjlMXSZSdso=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28/go.mod h1:EY3APf9MzygVhKuPXAc5H+MkGb8k/DOSQjWS0LgkKqI=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 h1:BjUcr3X3K0wZPGFg2bxOWW3VPN8rkE3/61zhP+IHviA=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32/go.mod h1:80+OGC/bgzzFFTUmcuwD0lb4YutwQeKLFpmt6hoWapU=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 h1:m1GeXHVMJsRsUAqG6HjZWx9dj7F5TR+cF1bjyfYyBd4=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32/go.mod h1:IitoQxGfaKdVLNg0hD8/DXmAqNy0H4K2H2Sf91ti8sI=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 h1:SYVGSFQHlchIcy6e7x12bsrxClCXSP5et8cqVhL8cuw=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13/go.mod h1:kizuDaLX37bG5WZaoxGPQR/LNFXpxp0vsUnqfkWXfNE=
-github.com/aws/aws-sdk-go-v2/service/kms v1.37.18 h1:pi9M/9n1PLayBXjia7LfwgXwcpFdFO7Q2cqKOZa1ZmM=
-github.com/aws/aws-sdk-go-v2/service/kms v1.37.18/go.mod h1:vZXvmzfhdsPj/axc8+qk/2fSCP4hGyaZ1MAduWEHAxM=
-github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 h1:/eE3DogBjYlvlbhd2ssWyeuovWunHLxfgw3s/OJa4GQ=
-github.com/aws/aws-sdk-go-v2/service/sso v1.24.15/go.mod h1:2PCJYpi7EKeA5SkStAmZlF6fi0uUABuhtF8ILHjGc3Y=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 h1:M/zwXiL2iXUrHputuXgmO94TVNmcenPHxgLXLutodKE=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14/go.mod h1:RVwIw3y/IqxC2YEXSIkAzRDdEU1iRabDPaYjpGCbCGQ=
-github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 h1:TzeR06UCMUq+KA3bDkujxK1GVGy+G8qQN/QVYzGLkQE=
-github.com/aws/aws-sdk-go-v2/service/sts v1.33.14/go.mod h1:dspXf/oYWGWo6DEvj98wpaTeqt5+DMidZD0A9BYTizc=
-github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
-github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
-github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
-github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
-github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bsm/redislock v0.9.4 h1:X/Wse1DPpiQgHbVYRE9zv6m070UcKoOGekgvpNhiSvw=
github.com/bsm/redislock v0.9.4/go.mod h1:Epf7AJLiSFwLCiZcfi6pWFO/8eAYrYpQXFxEDPoDeAk=
-github.com/caddyserver/certmagic v0.22.2 h1:qzZURXlrxwR5m25/jpvVeEyJHeJJMvAwe5zlMufOTQk=
-github.com/caddyserver/certmagic v0.22.2/go.mod h1:hbqE7BnkjhX5IJiFslPmrSeobSeZvI6ux8tyxhsd6qs=
-github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
-github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
github.com/casbin/casbin/v3 v3.0.0-beta.7 h1:siS3e6cRtuyFlshUgJfw0wnWuK3z3U/ald0C8Jtof24=
github.com/casbin/casbin/v3 v3.0.0-beta.7/go.mod h1:69HoI+h4yMUTydUMxT7VQh7FgGpoJsB/ZskkVGcvasQ=
-github.com/ccoveille/go-safecast v1.6.0 h1:kxc0VIsdEaYoKZbDiGBZBV62zAp0RdtFNH6E3Krev8s=
-github.com/ccoveille/go-safecast v1.6.0/go.mod h1:QqwNjxQ7DAqY0C721OIO9InMk9zCwcsO7tnRuHytad8=
github.com/cedar-policy/cedar-go v1.1.1 h1:yXCEaYQCXC+BxbOx/8TDHQu4dfuWVLs8irGrGjqClu0=
github.com/cedar-policy/cedar-go v1.1.1/go.mod h1:pEgiK479O5dJfzXnTguOMm+bCplzy5rEEFPGdZKPWz4=
-github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
-github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
-github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cedar-policy/cedar-go v1.2.1 h1:f4Ie1j6OT2TqtmnbVcR7OyJWfyPEUvYrFYaurQZhTAo=
+github.com/cedar-policy/cedar-go v1.2.1/go.mod h1:Ahhlu3DxDzvGTR88v/+j/EeiMEyvta11hlkdDgj4AZU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
-github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
-github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
-github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
-github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-oidc/v3 v3.13.0 h1:M66zd0pcc5VxvBNM4pB331Wrsanby+QomQYjN8HamW8=
github.com/coreos/go-oidc/v3 v3.13.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
-github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
-github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
-github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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=
@@ -163,39 +55,16 @@ github.com/deckarep/golang-set/v2 v2.7.0 h1:gIloKvD7yH2oip4VLhsv3JyLLFnC0Y2mlusg
github.com/deckarep/golang-set/v2 v2.7.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
-github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
-github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
-github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
-github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
-github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
-github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
-github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
-github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
-github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
-github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
-github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
-github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
-github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
-github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
-github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
-github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
-github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
-github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
-github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
@@ -205,59 +74,26 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/go-piv/piv-go/v2 v2.3.0 h1:kKkrYlgLQTMPA6BiSL25A7/x4CEh2YCG7rtb/aTkx+g=
-github.com/go-piv/piv-go/v2 v2.3.0/go.mod h1:ShZi74nnrWNQEdWzRUd/3cSig3uNOcEZp+EWl0oewnI=
-github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
-github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
-github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
-github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
-github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
+github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc=
-github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
-github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
-github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
-github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
-github.com/google/certificate-transparency-go v1.2.2 h1:5TAzjQnCfN1vps2XWUgU6Svt++rgy9a+b8CBYXaKUAo=
-github.com/google/certificate-transparency-go v1.2.2/go.mod h1:d1o5XNQzK/yz8IXzEce6KXgMxfter/EcMP9vG4dBRNE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
-github.com/google/go-configfs-tsm v0.3.3-0.20240919001351-b4b5b84fdcbc h1:SG12DWUUM5igxm+//YX5Yq4vhdoRnOG9HkCodkOn+YU=
-github.com/google/go-configfs-tsm v0.3.3-0.20240919001351-b4b5b84fdcbc/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo=
-github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
-github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
-github.com/google/go-sev-guest v0.12.1 h1:H4rFYnPIn8HtqEsNTmh56Zxcf9BI9n48ZSYCnpYLYvc=
-github.com/google/go-sev-guest v0.12.1/go.mod h1:SK9vW+uyfuzYdVN0m8BShL3OQCtXZe/JPF7ZkpD3760=
-github.com/google/go-tdx-guest v0.3.2-0.20241009005452-097ee70d0843 h1:+MoPobRN9HrDhGyn6HnF5NYo4uMBKaiFqAtf/D/OB4A=
-github.com/google/go-tdx-guest v0.3.2-0.20241009005452-097ee70d0843/go.mod h1:g/n8sKITIT9xRivBUbizo34DTsUm2nN2uU3A662h09g=
github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc=
github.com/google/go-tpm v0.9.3/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
-github.com/google/go-tpm-tools v0.4.5 h1:3fhthtyMDbIZFR5/0y1hvUoZ1Kf4i1eZ7C73R4Pvd+k=
-github.com/google/go-tpm-tools v0.4.5/go.mod h1:ktjTNq8yZFD6TzdBFefUfen96rF3NpYwpSb2d8bc+Y8=
-github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
-github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
github.com/google/jsonapi v1.0.0 h1:qIGgO5Smu3yJmSs+QlvhQnrscdZfFhiV6S8ryJAglqU=
github.com/google/jsonapi v1.0.0/go.mod h1:YYHiRPJT8ARXGER8In9VuLv4qvLfDmA9ULQqptbLE4s=
-github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ=
-github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
@@ -270,141 +106,54 @@ github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrk
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
-github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
-github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
-github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
-github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
-github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
-github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
-github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
-github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
-github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
-github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
-github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
-github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
-github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
-github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 h1:W9WN8p6moV1fjKLkeqEgkAMu5rauy9QeYDAmIaPuuiA=
-github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6/go.mod h1:MpCPSPGLDILGb4JMm94/mMi3YysIqsXzGCzkEZjcjXg=
-github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ=
-github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
-github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
-github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
-github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
-github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
-github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
-github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
-github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hashicorp/vault/api v1.16.0 h1:nbEYGJiAPGzT9U4oWgaaB0g+Rj8E59QuHKyA5LhwQN4=
-github.com/hashicorp/vault/api v1.16.0/go.mod h1:KhuUhzOD8lDSk29AtzNjgAu2kxRA9jL9NAbkFlqvkBA=
-github.com/hashicorp/vault/api/auth/approle v0.9.0 h1:FdpspwGVWnGiWmAxd5L1Yd+T+fX2kYnyAIvI5oGdvNs=
-github.com/hashicorp/vault/api/auth/approle v0.9.0/go.mod h1:fvtJhBs3AYMs2fXk4U5+u+7unhUGuboiKzFpLPpIazw=
-github.com/hashicorp/vault/api/auth/aws v0.9.0 h1:XtV/fRhgf/MaPv5SLBgXf+Yn/QGjFYIlJkdcPktDlbc=
-github.com/hashicorp/vault/api/auth/aws v0.9.0/go.mod h1:DvUmnZg9T+HGYdkagNMCssdgbt9ODAyBjllY8PpaMvM=
-github.com/hashicorp/vault/api/auth/kubernetes v0.9.0 h1:xV3xXMtSV8tq5iefueAw3OOdhhXyjnyhrQkIFM5fh54=
-github.com/hashicorp/vault/api/auth/kubernetes v0.9.0/go.mod h1:3K6uEUKZLBQ3d+eXAa4Ubp4UocswU90zY4QP5Az3Vw8=
-github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
-github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
-github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
-github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
-github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
-github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
-github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
-github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
-github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
-github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
-github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
-github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
-github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY=
-github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
-github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
-github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
-github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
-github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
-github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
-github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
-github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
+github.com/lestrrat-go/blackmagic v1.0.3 h1:94HXkVLxkZO9vJI/w2u1T0DAoprShFd13xtnSINtDWs=
+github.com/lestrrat-go/blackmagic v1.0.3/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/httprc/v3 v3.0.0-beta1 h1:pzDjP9dSONCFQC/AE3mWUnHILGiYPiMKzQIS+weKJXA=
github.com/lestrrat-go/httprc/v3 v3.0.0-beta1/go.mod h1:wdsgouffPvWPEYh8t7PRH/PidR5sfVqt0na4Nhj60Ms=
+github.com/lestrrat-go/httprc/v3 v3.0.0-beta2 h1:SDxjGoH7qj0nBXVrcrxX8eD94wEnjR+EEuqqmeqQYlY=
+github.com/lestrrat-go/httprc/v3 v3.0.0-beta2/go.mod h1:Nwo81sMxE0DcvTB+rJyynNhv/DUu2yZErV7sscw9pHE=
github.com/lestrrat-go/jwx/v3 v3.0.0-alpha3 h1:HHT8iW+UcPBgBr5A3soZQQsL5cBor/u6BkLB+wzY/R0=
github.com/lestrrat-go/jwx/v3 v3.0.0-alpha3/go.mod h1:ak32WoNtHE0aLowVWBcCvXngcAnW4tuC0YhFwOr/kwc=
+github.com/lestrrat-go/jwx/v3 v3.0.1 h1:fH3T748FCMbXoF9UXXNS9i0q6PpYyJZK/rKSbkt2guY=
+github.com/lestrrat-go/jwx/v3 v3.0.1/go.mod h1:XP2WqxMOSzHSyf3pfibCcfsLqbomxakAnNqiuaH8nwo=
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
-github.com/libdns/libdns v0.2.3 h1:ba30K4ObwMGB/QTmqUxf3H4/GmUrCAIkMWejeGl12v8=
-github.com/libdns/libdns v0.2.3/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
-github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
-github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
-github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
-github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
-github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa h1:hI1uC2A3vJFjwvBn0G0a7QBRdBUp6Y048BtLAHRTKPo=
github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa/go.mod h1:8vxFeeg++MqgCHwehSuwTlYCF0ALyDJbYJ1JsKi7v6s=
-github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
-github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
-github.com/mholt/acmez/v3 v3.1.1 h1:Jh+9uKHkPxUJdxM16q5mOr+G2V0aqkuFtNA28ihCxhQ=
-github.com/mholt/acmez/v3 v3.1.1/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
-github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
-github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
-github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
-github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
-github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q=
github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ=
-github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
-github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
-github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
-github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
-github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
-github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
-github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nats-io/jwt/v2 v2.7.3 h1:6bNPK+FXgBeAqdj4cYQ0F8ViHRbi7woQLq4W29nUAzE=
@@ -417,8 +166,6 @@ github.com/nats-io/nkeys v0.4.10 h1:glmRrpCmYLHByYcePvnTBEAwawwapjCPMjy2huw20wc=
github.com/nats-io/nkeys v0.4.10/go.mod h1:OjRrnIKnWBFl+s4YK5ChQfvHP2fxqZexrKJoVVyWB3U=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
-github.com/newrelic/go-agent/v3 v3.37.0 h1:vAidwr7gUThxT+NvxDG3qUxgeuJbzxhYAEeiKtPn/ig=
-github.com/newrelic/go-agent/v3 v3.37.0/go.mod h1:4QXvru0vVy/iu7mfkNHT7T2+9TC9zPGO8aUEdKqY138=
github.com/oauth2-proxy/mockoidc v0.0.0-20240214162133-caebfff84d25 h1:9bCMuD3TcnjeqjPT2gSlha4asp8NvgcFRYExCaikCxk=
github.com/oauth2-proxy/mockoidc v0.0.0-20240214162133-caebfff84d25/go.mod h1:eDjgYHYDJbPLBLsyZ6qRaugP0mX8vePOhZ5id1fdzJw=
github.com/oauth2-proxy/oauth2-proxy/v7 v7.8.2 h1:V401AcLfoQsX9s5iCD+/V/X0H+pNXwFxSLlPxUSr4c8=
@@ -427,16 +174,10 @@ github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0
github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
-github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
-github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
-github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o=
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
-github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/playwright-community/playwright-go v0.5001.0 h1:EY3oB+rU9cUp6CLHguWE8VMZTwAg+83Yyb7dQqEmGLg=
@@ -444,9 +185,6 @@ github.com/playwright-community/playwright-go v0.5001.0/go.mod h1:kBNWs/w2aJ2ZUp
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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
-github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
-github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
@@ -459,133 +197,48 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
-github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
-github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
-github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
-github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
-github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
-github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E=
-github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
-github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
-github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
-github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
-github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/slackhq/nebula v1.9.5 h1:ZrxcvP/lxwFglaijmiwXLuCSkybZMJnqSYI1S8DtGnY=
-github.com/slackhq/nebula v1.9.5/go.mod h1:1+4q4wd3dDAjO8rKCttSb9JIVbklQhuJiBp5I0lbIsQ=
-github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY=
-github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc=
-github.com/smallstep/certificates v0.28.3 h1:rcMh1TAs8m2emP3aDJxKLkE9jriAtcFtCuj2gttnpmI=
-github.com/smallstep/certificates v0.28.3/go.mod h1:P/IjGTvRCem3YZ7d1XtUxpvK/8dfFsJn7gaVLpMXbJw=
-github.com/smallstep/certinfo v1.13.0 h1:iv/Fc1c8vke1asJZI7s3XoH7Wo/MY7znK0TlDUsSAhs=
-github.com/smallstep/certinfo v1.13.0/go.mod h1:2pGT3T7r0s5f3BpJRi/j5K5akgvL3RfYXts5rDICkEA=
-github.com/smallstep/cli v0.28.6 h1:gjR8MNWI0f8YWesSoeFmGR3EbqnPWZcsy14GINe4Pwo=
-github.com/smallstep/cli v0.28.6/go.mod h1:3CBca5nOW1CbZzKqh8WfMHjpqvX4hQGowoY2G9lEkdY=
-github.com/smallstep/cli-utils v0.12.1 h1:D9QvfbFqiKq3snGZ2xDcXEFrdFJ1mQfPHZMq/leerpE=
-github.com/smallstep/cli-utils v0.12.1/go.mod h1:skV2Neg8qjiKPu2fphM89H9bIxNpKiiRTnX9Q6Lc+20=
-github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 h1:kjYvkvS/Wdy0PVRDUAA0gGJIVSEZYhiAJtfwYgOYoGA=
-github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
-github.com/smallstep/linkedca v0.23.0 h1:5W/7EudlK1HcCIdZM68dJlZ7orqCCCyv6bm2l/0JmLU=
-github.com/smallstep/linkedca v0.23.0/go.mod h1:7cyRM9soAYySg9ag65QwytcgGOM+4gOlkJ/YA58A9E8=
-github.com/smallstep/nosql v0.7.0 h1:YiWC9ZAHcrLCrayfaF+QJUv16I2bZ7KdLC3RpJcnAnE=
-github.com/smallstep/nosql v0.7.0/go.mod h1:H5VnKMCbeq9QA6SRY5iqPylfxLfYcLwvUff3onQ8+HU=
-github.com/smallstep/pkcs7 v0.0.0-20240911091500-b1cae6277023/go.mod h1:CM5KrX7rxWgwDdMj9yef/pJB2OPgy/56z4IEx2UIbpc=
-github.com/smallstep/pkcs7 v0.2.1 h1:6Kfzr/QizdIuB6LSv8y1LJdZ3aPSfTNhTLqAx9CTLfA=
-github.com/smallstep/pkcs7 v0.2.1/go.mod h1:RcXHsMfL+BzH8tRhmrF1NkkpebKpq3JEM66cOFxanf0=
-github.com/smallstep/scep v0.0.0-20240926084937-8cf1ca453101 h1:LyZqn24/ZiVg8v9Hq07K6mx6RqPtpDeK+De5vf4QEY4=
-github.com/smallstep/scep v0.0.0-20240926084937-8cf1ca453101/go.mod h1:EuKQjYGQwhUa1mgD21zxIgOgUYLsqikJmvxNscxpS/Y=
-github.com/smallstep/truststore v0.13.0 h1:90if9htAOblavbMeWlqNLnO9bsjjgVv2hQeQJCi/py4=
-github.com/smallstep/truststore v0.13.0/go.mod h1:3tmMp2aLKZ/OA/jnFUB0cYPcho402UG2knuJoPh4j7A=
-github.com/smallstep/zcrypto v0.0.0-20221001003018-1ab2364d2a91 h1:XE0cgVBMkYPxOZv4F3YY5mX9GgentifWU6vyJb6gKmc=
-github.com/smallstep/zcrypto v0.0.0-20221001003018-1ab2364d2a91/go.mod h1:9AA5+s5DF+8sE93nQ7HUalesU2SDqNfvrwn+dls9upw=
-github.com/smallstep/zlint v0.0.0-20220930192201-67fb4aa21910 h1:eIjaqvVEq+8eWaZd56yA7Ux5W6gJ9kqvq9ZWTsp3fkc=
-github.com/smallstep/zlint v0.0.0-20220930192201-67fb4aa21910/go.mod h1:GeHHT7sJDI9ti3oEaFnvx1F4N8n3ZSw2YM1+sbEoxc4=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
-github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
-github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
-github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
-github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
-github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
-github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg=
-github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
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/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
-github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ=
-github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po=
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
-github.com/weppos/publicsuffix-go v0.12.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
-github.com/weppos/publicsuffix-go v0.20.0 h1:59ypvSUbW3Dunc6zVm+v+MmXf2Q6cGiNDkxgRIzEnaA=
-github.com/weppos/publicsuffix-go v0.20.0/go.mod h1:5ZC/Uv3fIEUE0eP6o9+Yg4+5+W8V0/BieMi05feGXVA=
-github.com/weppos/publicsuffix-go/publicsuffix/generator v0.0.0-20220704091424-e0182326a282/go.mod h1:GHfoeIdZLdZmLjMlzBftbTDntahTttUMWjxZwQJhULE=
-github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
-github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xlgmokha/x v0.0.0-20250404223908-0b29f54f06e7 h1:Jjik5MGVznsXlo+otZXsWuKvbg3lCixMEIIkoxx0Ojc=
github.com/xlgmokha/x v0.0.0-20250404223908-0b29f54f06e7/go.mod h1:kLXa5uHaL3VF9ly6XlioU/Q1gittXvAYh6s1WpOFaU8=
-github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/xlgmokha/x v0.0.0-20250430185455-b691eda27477 h1:oeKrn9BSDSic5MTXKhQesxhIhB7byxl86IHtCD+yw4k=
+github.com/xlgmokha/x v0.0.0-20250430185455-b691eda27477/go.mod h1:axGPKzoJCNTmPJxYqN5l+Z9gGbPe0yolkT61a5p3QiI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
-github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
-github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
-github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
-github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
-github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
-github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
-go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
-go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
-go.mozilla.org/pkcs7 v0.9.0 h1:yM4/HS9dYv7ri2biPtxt8ikvB37a980dg69/pKmS+eI=
-go.mozilla.org/pkcs7 v0.9.0/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
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/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
@@ -602,48 +255,28 @@ go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
-go.step.sm/crypto v0.59.1 h1:jUL+5p19YS9YJKLaPUgkS2OdGm7s0+hwP7AqTFyF9Cg=
-go.step.sm/crypto v0.59.1/go.mod h1:XHavmnzfTyPpQE/n4YokEtjiBzP3LZI9/1O061f5y0o=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
-go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
-go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
-go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
-go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
-go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
-go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
-golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
-golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
-golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
-golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
+golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
+golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
+golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
+golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
-golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
-golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
-golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
@@ -651,85 +284,52 @@ golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
-golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
-golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
+golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
+golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
+golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
-golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
-golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
-golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
-golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
-golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
-golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
-golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
+golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
-golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
+golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.228.0 h1:X2DJ/uoWGnY5obVjewbp8icSL5U4FzuCfy9OjbLSnLs=
google.golang.org/api v0.228.0/go.mod h1:wNvRS1Pbe8r4+IfBIniV8fwCpGwTrYa+kMUDiC5z5a4=
-google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
-google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c=
@@ -741,28 +341,17 @@ google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zt
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
-gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
-howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
-k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
-k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
-software.sslmate.com/src/go-pkcs12 v0.5.0 h1:EC6R394xgENTpZ4RltKydeDUjtlM5drOYIG9c6TVj2M=
-software.sslmate.com/src/go-pkcs12 v0.5.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
diff --git a/lib/.keep b/lib/.keep
deleted file mode 100644
index e69de29b..00000000
--- a/lib/.keep
+++ /dev/null
diff --git a/lib/authx.rb b/lib/authx.rb
deleted file mode 100644
index 5ee3f543..00000000
--- a/lib/authx.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# frozen_string_literal: true
-
-require "authx/rpc"
-
-module Authx
-end
diff --git a/lib/authx/rpc.rb b/lib/authx/rpc.rb
deleted file mode 100644
index 78edbc46..00000000
--- a/lib/authx/rpc.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# frozen_string_literal: true
-
-require "authx/rpc/ability_pb"
-require "authx/rpc/ability_twirp"
diff --git a/lib/authx/rpc/ability_pb.rb b/lib/authx/rpc/ability_pb.rb
deleted file mode 100644
index ee71dc57..00000000
--- a/lib/authx/rpc/ability_pb.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by the protocol buffer compiler. DO NOT EDIT!
-# source: ability.proto
-
-require 'google/protobuf'
-
-Google::Protobuf::DescriptorPool.generated_pool.build do
- add_file("ability.proto", :syntax => :proto3) do
- add_message "authx.rpc.AllowRequest" do
- optional :subject, :string, 1
- optional :permission, :string, 2
- optional :resource, :string, 3
- end
- add_message "authx.rpc.AllowReply" do
- optional :result, :bool, 1
- end
- end
-end
-
-module Authx
- module Rpc
- AllowRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("authx.rpc.AllowRequest").msgclass
- AllowReply = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("authx.rpc.AllowReply").msgclass
- end
-end
diff --git a/lib/authx/rpc/ability_twirp.rb b/lib/authx/rpc/ability_twirp.rb
deleted file mode 100644
index 120e9a99..00000000
--- a/lib/authx/rpc/ability_twirp.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# Code generated by protoc-gen-twirp_ruby 1.11.0, DO NOT EDIT.
-require 'twirp'
-require_relative 'ability_pb.rb'
-
-module Authx
- module Rpc
- class AbilityService < ::Twirp::Service
- package 'authx.rpc'
- service 'Ability'
- rpc :Allowed, AllowRequest, AllowReply, :ruby_method => :allowed
- end
-
- class AbilityClient < ::Twirp::Client
- client_for AbilityService
- end
- end
-end
diff --git a/magefiles/magefile.go b/magefiles/magefile.go
index 71aa3bc8..06dc0251 100644
--- a/magefiles/magefile.go
+++ b/magefiles/magefile.go
@@ -16,49 +16,10 @@ import (
// If not set, running mage will list available targets
var Default = Servers
-// Run the Identity Provider
-func Idp() error {
- env := map[string]string{
- "SCHEME": "http",
- "PORT": "8282",
- "HOST": "idp.example.com:8080",
- }
- return sh.RunWithV(env, "ruby", "./bin/idp")
-}
-
-// Run the UI (a.k.a Service Provider)
-func UI() error {
- env := map[string]string{
- "SCHEME": "http",
- "PORT": "8283",
- "HOST": "ui.example.com:8080",
- "IDP_HOST": "idp.example.com:8080",
- }
- return sh.RunWithV(env, "ruby", "./bin/ui")
-}
-
-// Run the API Gateway
-func Gateway() error {
- env := map[string]string{
- "BIND_ADDR": ":8080",
- }
- return sh.RunWithV(env, "go", "run", "./cmd/gtwy/main.go")
-}
-
-// Run the REST API
-func Api() error {
- env := map[string]string{
- "SCHEME": "http",
- "PORT": "8284",
- "HOST": "localhost:8284",
- }
- return sh.RunWithV(env, "ruby", "./bin/api")
-}
-
// Run the Authzd Service
func Authzd() error {
env := map[string]string{
- "BIND_ADDR": ":50051",
+ "BIND_ADDR": ":8080",
}
return sh.RunWithV(env, "go", "run", "./cmd/authzd/main.go")
}
@@ -82,13 +43,8 @@ func Protos() error {
if err := sh.RunV(
"protoc",
"--proto_path=./protos",
- "--go_out=pkg/rpc",
- "--go_opt=paths=source_relative",
- "--go-grpc_out=pkg/rpc",
- "--go-grpc_opt=paths=source_relative",
- "--twirp_out=pkg/rpc",
- "--ruby_out=lib/authx/rpc",
- "--twirp_ruby_out=lib/authx/rpc",
+ "--go_out=.",
+ "--twirp_out=.",
file,
); err != nil {
return err
@@ -100,7 +56,7 @@ func Protos() error {
// Run All the servers
func Servers(ctx context.Context) {
- mg.CtxDeps(ctx, (Step{}).Server, Nats, Idp, UI, Api, Authzd, Gateway)
+ mg.CtxDeps(ctx, Nats, Authzd)
}
// Run the end to end tests
diff --git a/magefiles/step.go b/magefiles/step.go
deleted file mode 100644
index 25cf23b0..00000000
--- a/magefiles/step.go
+++ /dev/null
@@ -1,147 +0,0 @@
-//go:build mage
-// +build mage
-
-package main
-
-import (
- "context"
- "encoding/json"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
-
- "github.com/magefile/mage/mg"
- "github.com/magefile/mage/sh"
- "github.com/magefile/mage/target"
- "github.com/xlgmokha/x/pkg/env"
- "github.com/xlgmokha/x/pkg/x"
-)
-
-type Step mg.Namespace
-
-func (s Step) Clean() error {
- globs := []string{
- "tmp/step/*/*",
- }
- for _, item := range globs {
- fs, err := filepath.Glob(item)
- if err != nil {
- return err
- }
- for _, f := range fs {
- if strings.HasSuffix(f, "/.keep") {
- continue
- }
- if err := os.RemoveAll(f); err != nil {
- return err
- }
- }
- }
- return nil
-}
-
-func (s Step) Setup() {
- mg.SerialDeps(s.mkPassword, s.createCA, s.enableACMEProvisioner)
-}
-
-func (s Step) Install() error {
- return sh.RunWithV(
- s.env(),
- "step",
- "certificate",
- "install",
- s.pathPlus("/certs/root_ca.crt"),
- )
-}
-
-func (s Step) Server(ctx context.Context) error {
- mg.SerialDeps(s.Setup)
-
- return sh.RunWithV(
- s.env(),
- "step-ca",
- s.pathPlus("config/ca.json"),
- "--password-file="+s.pathPlus("password.txt"),
- )
-}
-
-func (s Step) Provisioners() error {
- return sh.RunV("curl", "-k", "-s", "https://localhost:8081/provisioners")
-}
-
-func (s Step) ACME() error {
- return sh.RunV("curl", "-k", "-s", "https://localhost:8081/acme/acme/directory")
-}
-
-func (s Step) Status() {
- mg.SerialDeps(s.Provisioners, s.ACME)
-}
-
-func (s Step) mkPassword() error {
- file := s.passwordFile()
- if ok, err := target.Dir(file); err != nil || !ok {
- return nil
- }
-
- return os.WriteFile(file, []byte("password"), 0600)
-}
-
-func (s Step) createCA() error {
- if ok, err := target.Dir(s.pathPlus("config/ca.json"), s.passwordFile()); err != nil || !ok {
- return nil
- }
-
- return sh.RunWithV(
- s.env(),
- "step",
- "ca",
- "init",
- "--deployment-type=standalone",
- "--address=localhost:8081",
- "--dns=localhost",
- "--dns=*.localhost",
- "--name=CA",
- "--provisioner=example",
- "--provisioner-password-file="+s.passwordFile(),
- "--password-file="+s.passwordFile(),
- )
-}
-
-func (s Step) enableACMEProvisioner() error {
- bytes, err := ioutil.ReadFile(s.pathPlus("config/ca.json"))
- if err != nil {
- return err
- }
-
- items := map[string]interface{}{}
- if err := json.Unmarshal(bytes, &items); err != nil {
- return err
- }
-
- provisioners := items["authority"].(map[string]interface{})["provisioners"].([]interface{})
- if len(provisioners) < 2 {
- return sh.RunWithV(s.env(), "step", "ca", "provisioner", "add", "acme", "--type", "ACME")
- }
- return nil
-}
-
-func (step Step) passwordFile() string {
- return step.pathPlus("password.txt")
-}
-
-func (s Step) path() string {
- return env.Fetch("STEPPATH", filepath.Join(x.Must(os.Getwd()), "/tmp/step"))
-}
-
-func (s Step) env() map[string]string {
- return map[string]string{
- "STEPPATH": s.path(),
- "HOST": "localhost",
- "PORT": "8081",
- }
-}
-
-func (s Step) pathPlus(path string) string {
- return filepath.Join(s.path(), path)
-}
diff --git a/pkg/app/app.go b/pkg/app/app.go
deleted file mode 100644
index 89a2bd34..00000000
--- a/pkg/app/app.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package app
-
-import (
- "os"
-
- "github.com/xlgmokha/x/pkg/log"
- "gitlab.com/mokhax/spike/pkg/authz"
- "gitlab.com/mokhax/spike/pkg/cfg"
- "gitlab.com/mokhax/spike/pkg/srv"
-)
-
-func Start(bindAddr string) error {
- logger := log.New(os.Stdout, log.Fields{"app": "gtwy"})
- mux := authz.HTTP(authz.WithCasbin(), Routes())
- return srv.Run(cfg.New(
- bindAddr,
- cfg.WithMux(log.HTTP(logger)(mux)),
- cfg.WithTLS([]string{
- "api.example.com",
- "authzd.example.com",
- "idp.example.com",
- "ui.example.com",
- }),
- ))
-}
diff --git a/pkg/app/routes.go b/pkg/app/routes.go
deleted file mode 100644
index ff1291c2..00000000
--- a/pkg/app/routes.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package app
-
-import (
- "net/http"
-
- "gitlab.com/mokhax/spike/pkg/prxy"
-)
-
-func Routes() http.Handler {
- mux := http.NewServeMux()
- mux.Handle("/", prxy.New(map[string]string{
- "api.example.com": "http://localhost:8284",
- "authzd.example.com": "http://localhost:50051",
- "idp.example.com": "http://localhost:8282",
- "ui.example.com": "http://localhost:8283",
- }))
- return mux
-}
diff --git a/pkg/authz/authz.go b/pkg/authz/authz.go
deleted file mode 100644
index 5a93a29c..00000000
--- a/pkg/authz/authz.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package authz
-
-import "net/http"
-
-type Authorizer interface {
- Authorize(*http.Request) bool
-}
-
-type AuthorizerFunc func(*http.Request) bool
-
-func (f AuthorizerFunc) Authorize(r *http.Request) bool {
- return f(r)
-}
-
-func HTTP(authorizer Authorizer, h http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if authorizer.Authorize(r) {
- h.ServeHTTP(w, r)
- } else {
- w.WriteHeader(http.StatusForbidden)
- }
- })
-}
diff --git a/pkg/authz/casbin.go b/pkg/authz/casbin.go
deleted file mode 100644
index 140bdb98..00000000
--- a/pkg/authz/casbin.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package authz
-
-import (
- "fmt"
- "net"
- "net/http"
-
- "github.com/casbin/casbin/v3"
- "github.com/xlgmokha/x/pkg/log"
- "github.com/xlgmokha/x/pkg/x"
-)
-
-func WithCasbin() Authorizer {
- enforcer := x.Must(casbin.NewEnforcer("casbin.conf", "casbin.csv"))
-
- return AuthorizerFunc(func(r *http.Request) bool {
- host, _, err := net.SplitHostPort(r.Host)
- if err != nil {
- log.WithFields(r.Context(), log.Fields{"error": err})
- return false
- }
-
- subject, found := TokenFrom(r).Subject()
- if !found {
- subject = "*"
- }
- ok, err := enforcer.Enforce(subject, host, r.Method, r.URL.Path)
- if err != nil {
- log.WithFields(r.Context(), log.Fields{"error": err})
- return false
- }
-
- fmt.Printf("%v: %v -> %v %v%v\n", ok, subject, r.Method, host, r.URL.Path)
- log.WithFields(r.Context(), log.Fields{
- "authz": ok,
- "subject": subject,
- "action": r.Method,
- "domain": host,
- "object": r.URL.Path,
- })
- return ok
- })
-}
diff --git a/pkg/authz/cedar.go b/pkg/authz/cedar.go
deleted file mode 100644
index 18674c74..00000000
--- a/pkg/authz/cedar.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package authz
-
-import (
- "net"
- "net/http"
-
- cedar "github.com/cedar-policy/cedar-go"
- "github.com/xlgmokha/x/pkg/log"
- "gitlab.com/mokhax/spike/pkg/gid"
- "gitlab.com/mokhax/spike/pkg/policies"
-)
-
-func WithCedar() Authorizer {
- return AuthorizerFunc(func(r *http.Request) bool {
- host, _, err := net.SplitHostPort(r.Host)
- if err != nil {
- log.WithFields(r.Context(), log.Fields{"error": err})
- return false
- }
- subject, found := TokenFrom(r).Subject()
- if !found {
- subject = "gid://example/User/*"
- }
-
- return policies.Allowed(cedar.Request{
- Principal: gid.NewEntityUID(subject),
- Action: cedar.NewEntityUID("HttpMethod", cedar.String(r.Method)),
- Resource: cedar.NewEntityUID("HttpPath", cedar.String(r.URL.Path)),
- Context: cedar.NewRecord(cedar.RecordMap{
- "host": cedar.String(host),
- }),
- })
- })
-}
diff --git a/pkg/authz/token.go b/pkg/authz/token.go
deleted file mode 100644
index 2794bf4a..00000000
--- a/pkg/authz/token.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package authz
-
-import (
- "net/http"
- "strings"
-
- "github.com/lestrrat-go/jwx/v3/jwt"
- "github.com/xlgmokha/x/pkg/log"
-)
-
-func TokenFrom(r *http.Request) jwt.Token {
- authorization := r.Header.Get("Authorization")
- if authorization == "" || !strings.Contains(authorization, "Bearer") {
- return jwt.New()
- }
-
- token, err := jwt.ParseRequest(r,
- jwt.WithContext(r.Context()),
- jwt.WithHeaderKey("Authorization"),
- jwt.WithValidate(false), // TODO:: Connect this to a JSON Web Key Set
- jwt.WithVerify(false), // TODO:: Connect this to a JSON Web Key Set
- )
-
- if err != nil {
- log.WithFields(r.Context(), log.Fields{"error": err})
- return jwt.New()
- }
-
- return token
-}
diff --git a/pkg/cfg/cfg.go b/pkg/cfg/cfg.go
deleted file mode 100644
index 0d7a6427..00000000
--- a/pkg/cfg/cfg.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package cfg
-
-import (
- "crypto/tls"
- "net/http"
-)
-
-type Config struct {
- BindAddress string
- Mux http.Handler
- TLS *tls.Config
-}
-
-func New(addr string, options ...Option) *Config {
- if addr == "" {
- addr = ":0"
- }
-
- c := &Config{
- BindAddress: addr,
- Mux: http.DefaultServeMux,
- }
- for _, option := range options {
- option(c)
- }
- return c
-}
-
-func (c *Config) Run(server *http.Server) error {
- if c.TLS != nil {
- return server.ListenAndServeTLS("", "")
- }
- return server.ListenAndServe()
-}
diff --git a/pkg/cfg/mux.go b/pkg/cfg/mux.go
deleted file mode 100644
index 6c6f4375..00000000
--- a/pkg/cfg/mux.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package cfg
-
-import (
- "net/http"
-)
-
-func WithMux(mux http.Handler) Option {
- return func(config *Config) {
- config.Mux = mux
- }
-}
diff --git a/pkg/cfg/option.go b/pkg/cfg/option.go
deleted file mode 100644
index 0f3e87d8..00000000
--- a/pkg/cfg/option.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package cfg
-
-type Option func(*Config)
diff --git a/pkg/cfg/tls.go b/pkg/cfg/tls.go
deleted file mode 100644
index bce6e186..00000000
--- a/pkg/cfg/tls.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package cfg
-
-import (
- "context"
- "crypto/tls"
- "crypto/x509"
- "encoding/pem"
- "io/ioutil"
- "net/http"
- "os"
- "path/filepath"
-
- "github.com/caddyserver/certmagic"
- "github.com/xlgmokha/x/pkg/x"
- "go.uber.org/zap"
-)
-
-func WithSelfSigned(cert, key string) Option {
- certificate := x.Must(tls.LoadX509KeyPair(cert, key))
-
- return func(config *Config) {
- config.TLS = &tls.Config{
- MinVersion: tls.VersionTLS13,
- Certificates: []tls.Certificate{certificate},
- }
- }
-}
-
-func WithTLS(domainNames []string) Option {
- directoryURL := "https://localhost:8081/acme/acme/directory"
- storage := &certmagic.FileStorage{
- Path: filepath.Join(x.Must(os.Getwd()), "/tmp/cache"),
- }
- var cache *certmagic.Cache
- cache = certmagic.NewCache(certmagic.CacheOptions{
- GetConfigForCert: func(cert certmagic.Certificate) (*certmagic.Config, error) {
- return certmagic.New(cache, certmagic.Config{
- Logger: x.Must(zap.NewProduction()),
- OnDemand: new(certmagic.OnDemandConfig),
- Storage: storage,
- }), nil
- },
- })
- roots := x.Must(x509.SystemCertPool())
- roots.AddCert(func() *x509.Certificate {
- block, _ := pem.Decode(x.Must(ioutil.ReadFile(
- filepath.Join(x.Must(os.Getwd()), "/tmp/step/certs/root_ca.crt"),
- )))
- return x.Must(x509.ParseCertificate(block.Bytes))
- }())
- magic := certmagic.New(cache, certmagic.Config{
- Logger: x.Must(zap.NewProduction()),
- OnDemand: new(certmagic.OnDemandConfig),
- Storage: storage,
- })
- issuer := certmagic.NewACMEIssuer(magic, certmagic.ACMEIssuer{
- Agreed: true,
- Email: "email@example.com",
- CA: directoryURL,
- TestCA: directoryURL,
- TrustedRoots: roots,
- })
- magic.Issuers = []certmagic.Issuer{issuer}
-
- if err := http.ListenAndServe(":80", issuer.HTTPChallengeHandler(http.DefaultServeMux)); err != nil {
- return func(*Config) {}
- }
-
- x.Check(magic.ManageSync(context.Background(), domainNames))
-
- return func(config *Config) {
- config.TLS = magic.TLSConfig()
- config.TLS.NextProtos = append([]string{"h2", "http/1.1"}, config.TLS.NextProtos...)
- }
-}
diff --git a/pkg/policies/policies_test.go b/pkg/policies/policies_test.go
index 24ef6c68..9dc98bcd 100644
--- a/pkg/policies/policies_test.go
+++ b/pkg/policies/policies_test.go
@@ -6,7 +6,7 @@ import (
"github.com/cedar-policy/cedar-go"
"github.com/stretchr/testify/assert"
- "gitlab.com/mokhax/spike/pkg/gid"
+ "gitlab.com/gitlab-org/software-supply-chain-security/authorization/authz.d/pkg/gid"
)
func build(f func(*cedar.Request)) *cedar.Request {
diff --git a/pkg/prxy/prxy.go b/pkg/prxy/prxy.go
deleted file mode 100644
index 43565bd3..00000000
--- a/pkg/prxy/prxy.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package prxy
-
-import (
- "fmt"
- "net"
- "net/http"
- "net/http/httputil"
- "net/url"
-
- "github.com/xlgmokha/x/pkg/log"
- "github.com/xlgmokha/x/pkg/x"
-)
-
-func New(routes map[string]string) http.Handler {
- mapped := map[string]*url.URL{}
- for source, destination := range routes {
- mapped[source] = x.Must(url.Parse(destination))
- }
-
- return &httputil.ReverseProxy{
- Rewrite: func(r *httputil.ProxyRequest) {
- host, _, err := net.SplitHostPort(r.In.Host)
- if err != nil {
- log.WithFields(r.In.Context(), log.Fields{"error": err})
- return
- }
-
- destination := mapped[host]
- r.SetXForwarded()
- r.SetURL(destination)
- },
- Transport: http.DefaultTransport,
- FlushInterval: -1,
- ErrorLog: nil,
- ModifyResponse: func(r *http.Response) error {
- r.Header.Add("Via", fmt.Sprintf("%v gtwy", r.Proto))
- return nil
- },
- ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
- log.WithFields(r.Context(), log.Fields{"error": err})
- },
- }
-}
diff --git a/pkg/prxy/prxy_test.go b/pkg/prxy/prxy_test.go
deleted file mode 100644
index 6f37974e..00000000
--- a/pkg/prxy/prxy_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package prxy
-
-import (
- "net/http"
- "net/http/httptest"
- "net/url"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "github.com/xlgmokha/x/pkg/x"
- "gitlab.com/mokhax/spike/pkg/test"
-)
-
-func TestProxy(t *testing.T) {
- t.Run("http://idp.test", func(t *testing.T) {
- var lastIdPRequest *http.Request
- var lastUiRequest *http.Request
-
- idp := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- lastIdPRequest = r
- w.WriteHeader(http.StatusOK)
- }))
- defer idp.Close()
-
- ui := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- lastUiRequest = r
- w.WriteHeader(http.StatusTeapot)
- }))
- defer ui.Close()
-
- subject := New(map[string]string{
- "idp.test": idp.URL,
- "ui.test": ui.URL,
- })
-
- r, w := test.RequestResponse("GET", "http://idp.test:8080/saml/new")
-
- subject.ServeHTTP(w, r)
-
- url := x.Must(url.Parse(idp.URL))
-
- assert.Nil(t, lastUiRequest)
- assert.Equal(t, http.StatusOK, w.Code)
-
- require.NotNil(t, lastIdPRequest)
- assert.Equal(t, url.Host, lastIdPRequest.Host)
- })
-}
diff --git a/pkg/rpc/ability.pb.go b/pkg/rpc/ability.pb.go
index 48dd0b24..939719fc 100644
--- a/pkg/rpc/ability.pb.go
+++ b/pkg/rpc/ability.pb.go
@@ -129,7 +129,7 @@ var File_ability_proto protoreflect.FileDescriptor
const file_ability_proto_rawDesc = "" +
"\n" +
- "\rability.proto\x12\tauthx.rpc\"d\n" +
+ "\rability.proto\x12\tauthz.rpc\"d\n" +
"\fAllowRequest\x12\x18\n" +
"\asubject\x18\x01 \x01(\tR\asubject\x12\x1e\n" +
"\n" +
@@ -140,7 +140,7 @@ const file_ability_proto_rawDesc = "" +
"AllowReply\x12\x16\n" +
"\x06result\x18\x01 \x01(\bR\x06result2F\n" +
"\aAbility\x12;\n" +
- "\aAllowed\x12\x17.authx.rpc.AllowRequest\x1a\x15.authx.rpc.AllowReply\"\x00B!Z\x1fgitlab.com/mokhax/spike/pkg/rpcb\x06proto3"
+ "\aAllowed\x12\x17.authz.rpc.AllowRequest\x1a\x15.authz.rpc.AllowReply\"\x00B\tZ\apkg/rpcb\x06proto3"
var (
file_ability_proto_rawDescOnce sync.Once
@@ -156,12 +156,12 @@ func file_ability_proto_rawDescGZIP() []byte {
var file_ability_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_ability_proto_goTypes = []any{
- (*AllowRequest)(nil), // 0: authx.rpc.AllowRequest
- (*AllowReply)(nil), // 1: authx.rpc.AllowReply
+ (*AllowRequest)(nil), // 0: authz.rpc.AllowRequest
+ (*AllowReply)(nil), // 1: authz.rpc.AllowReply
}
var file_ability_proto_depIdxs = []int32{
- 0, // 0: authx.rpc.Ability.Allowed:input_type -> authx.rpc.AllowRequest
- 1, // 1: authx.rpc.Ability.Allowed:output_type -> authx.rpc.AllowReply
+ 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
diff --git a/pkg/rpc/gitlab.com/mokhax/spike/pkg/rpc/ability.twirp.go b/pkg/rpc/ability.twirp.go
index ea2c3d17..f5a33296 100644
--- a/pkg/rpc/gitlab.com/mokhax/spike/pkg/rpc/ability.twirp.go
+++ b/pkg/rpc/ability.twirp.go
@@ -68,7 +68,7 @@ func NewAbilityProtobufClient(baseURL string, client HTTPClient, opts ...twirp.C
// Build method URLs: <baseURL>[<prefix>]/<package>.<Service>/<Method>
serviceURL := sanitizeBaseURL(baseURL)
- serviceURL += baseServicePath(pathPrefix, "authx.rpc", "Ability")
+ serviceURL += baseServicePath(pathPrefix, "authz.rpc", "Ability")
urls := [1]string{
serviceURL + "Allowed",
}
@@ -82,7 +82,7 @@ func NewAbilityProtobufClient(baseURL string, client HTTPClient, opts ...twirp.C
}
func (c *abilityProtobufClient) Allowed(ctx context.Context, in *AllowRequest) (*AllowReply, error) {
- ctx = ctxsetters.WithPackageName(ctx, "authx.rpc")
+ ctx = ctxsetters.WithPackageName(ctx, "authz.rpc")
ctx = ctxsetters.WithServiceName(ctx, "Ability")
ctx = ctxsetters.WithMethodName(ctx, "Allowed")
caller := c.callAllowed
@@ -160,7 +160,7 @@ func NewAbilityJSONClient(baseURL string, client HTTPClient, opts ...twirp.Clien
// Build method URLs: <baseURL>[<prefix>]/<package>.<Service>/<Method>
serviceURL := sanitizeBaseURL(baseURL)
- serviceURL += baseServicePath(pathPrefix, "authx.rpc", "Ability")
+ serviceURL += baseServicePath(pathPrefix, "authz.rpc", "Ability")
urls := [1]string{
serviceURL + "Allowed",
}
@@ -174,7 +174,7 @@ func NewAbilityJSONClient(baseURL string, client HTTPClient, opts ...twirp.Clien
}
func (c *abilityJSONClient) Allowed(ctx context.Context, in *AllowRequest) (*AllowReply, error) {
- ctx = ctxsetters.WithPackageName(ctx, "authx.rpc")
+ ctx = ctxsetters.WithPackageName(ctx, "authz.rpc")
ctx = ctxsetters.WithServiceName(ctx, "Ability")
ctx = ctxsetters.WithMethodName(ctx, "Allowed")
caller := c.callAllowed
@@ -281,11 +281,11 @@ func (s *abilityServer) handleRequestBodyError(ctx context.Context, resp http.Re
// 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/authx.rpc.Ability/"
+const AbilityPathPrefix = "/twirp/authz.rpc.Ability/"
func (s *abilityServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
ctx := req.Context()
- ctx = ctxsetters.WithPackageName(ctx, "authx.rpc")
+ ctx = ctxsetters.WithPackageName(ctx, "authz.rpc")
ctx = ctxsetters.WithServiceName(ctx, "Ability")
ctx = ctxsetters.WithResponseWriter(ctx, resp)
@@ -304,7 +304,7 @@ func (s *abilityServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
// Verify path format: [<prefix>]/<package>.<Service>/<Method>
prefix, pkgService, method := parseTwirpPath(req.URL.Path)
- if pkgService != "authx.rpc.Ability" {
+ 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
@@ -518,7 +518,7 @@ func (s *abilityServer) ProtocGenTwirpVersion() string {
// 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, "authx.rpc", "Ability")
+ return baseServicePath(s.pathPrefix, "authz.rpc", "Ability")
}
// =====
@@ -1087,19 +1087,18 @@ func callClientError(ctx context.Context, h *twirp.ClientHooks, err twirp.Error)
}
var twirpFileDescriptor0 = []byte{
- // 216 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0xbd, 0x4e, 0xc3, 0x30,
- 0x14, 0x46, 0x29, 0x48, 0x4d, 0x7b, 0x05, 0x8b, 0x25, 0xc0, 0xea, 0xc0, 0x4f, 0xc4, 0xc0, 0x64,
- 0x4b, 0x30, 0x32, 0x95, 0x81, 0x07, 0xc8, 0xc8, 0x66, 0xbb, 0x57, 0xad, 0x89, 0x83, 0x2f, 0xfe,
- 0x11, 0xcd, 0xdb, 0x23, 0x39, 0x21, 0x8a, 0xd4, 0xf1, 0xf8, 0xc8, 0xfa, 0x8e, 0x2e, 0x5c, 0x29,
- 0x6d, 0x9d, 0x4d, 0xbd, 0xa0, 0xe0, 0x93, 0x67, 0x6b, 0x95, 0xd3, 0xe1, 0x28, 0x02, 0x99, 0x7a,
- 0x07, 0x97, 0x5b, 0xe7, 0xfc, 0x6f, 0x83, 0x3f, 0x19, 0x63, 0x62, 0x1c, 0xaa, 0x98, 0xf5, 0x17,
- 0x9a, 0xc4, 0x17, 0x0f, 0x8b, 0xe7, 0x75, 0xf3, 0x8f, 0xec, 0x0e, 0x80, 0x30, 0x74, 0x36, 0x46,
- 0xeb, 0xbf, 0xf9, 0x79, 0x91, 0xb3, 0x17, 0xb6, 0x81, 0x55, 0xc0, 0xe8, 0x73, 0x30, 0xc8, 0x2f,
- 0x8a, 0x9d, 0xb8, 0x7e, 0x02, 0x18, 0x57, 0xc8, 0xf5, 0xec, 0x06, 0x96, 0x01, 0x63, 0x76, 0xc3,
- 0xc4, 0xaa, 0x19, 0xe9, 0xe5, 0x03, 0xaa, 0xed, 0xd0, 0xc9, 0xde, 0xa0, 0x2a, 0x1f, 0x70, 0xc7,
- 0x6e, 0xc5, 0x54, 0x2b, 0xe6, 0xa9, 0x9b, 0xeb, 0x53, 0x41, 0xae, 0xaf, 0xcf, 0xde, 0x1f, 0x3f,
- 0xef, 0xf7, 0x36, 0x39, 0xa5, 0x85, 0xf1, 0x9d, 0xec, 0x7c, 0x7b, 0x50, 0x47, 0x19, 0xc9, 0xb6,
- 0x28, 0xa9, 0xdd, 0xcb, 0x40, 0x46, 0x2f, 0xcb, 0x21, 0x5e, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff,
- 0xe2, 0x96, 0x42, 0xb1, 0x19, 0x01, 0x00, 0x00,
+ // 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/pkg/rpc/ability_grpc.pb.go b/pkg/rpc/ability_grpc.pb.go
deleted file mode 100644
index 4d74cc41..00000000
--- a/pkg/rpc/ability_grpc.pb.go
+++ /dev/null
@@ -1,121 +0,0 @@
-// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
-// versions:
-// - protoc-gen-go-grpc v1.5.1
-// - protoc v3.19.6
-// source: ability.proto
-
-package rpc
-
-import (
- context "context"
- grpc "google.golang.org/grpc"
- codes "google.golang.org/grpc/codes"
- status "google.golang.org/grpc/status"
-)
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the grpc package it is being compiled against.
-// Requires gRPC-Go v1.64.0 or later.
-const _ = grpc.SupportPackageIsVersion9
-
-const (
- Ability_Allowed_FullMethodName = "/authx.rpc.Ability/Allowed"
-)
-
-// AbilityClient is the client API for Ability service.
-//
-// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
-type AbilityClient interface {
- Allowed(ctx context.Context, in *AllowRequest, opts ...grpc.CallOption) (*AllowReply, error)
-}
-
-type abilityClient struct {
- cc grpc.ClientConnInterface
-}
-
-func NewAbilityClient(cc grpc.ClientConnInterface) AbilityClient {
- return &abilityClient{cc}
-}
-
-func (c *abilityClient) Allowed(ctx context.Context, in *AllowRequest, opts ...grpc.CallOption) (*AllowReply, error) {
- cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
- out := new(AllowReply)
- err := c.cc.Invoke(ctx, Ability_Allowed_FullMethodName, in, out, cOpts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-// AbilityServer is the server API for Ability service.
-// All implementations must embed UnimplementedAbilityServer
-// for forward compatibility.
-type AbilityServer interface {
- Allowed(context.Context, *AllowRequest) (*AllowReply, error)
- mustEmbedUnimplementedAbilityServer()
-}
-
-// UnimplementedAbilityServer must be embedded to have
-// forward compatible implementations.
-//
-// NOTE: this should be embedded by value instead of pointer to avoid a nil
-// pointer dereference when methods are called.
-type UnimplementedAbilityServer struct{}
-
-func (UnimplementedAbilityServer) Allowed(context.Context, *AllowRequest) (*AllowReply, error) {
- return nil, status.Errorf(codes.Unimplemented, "method Allowed not implemented")
-}
-func (UnimplementedAbilityServer) mustEmbedUnimplementedAbilityServer() {}
-func (UnimplementedAbilityServer) testEmbeddedByValue() {}
-
-// UnsafeAbilityServer may be embedded to opt out of forward compatibility for this service.
-// Use of this interface is not recommended, as added methods to AbilityServer will
-// result in compilation errors.
-type UnsafeAbilityServer interface {
- mustEmbedUnimplementedAbilityServer()
-}
-
-func RegisterAbilityServer(s grpc.ServiceRegistrar, srv AbilityServer) {
- // If the following call pancis, it indicates UnimplementedAbilityServer was
- // embedded by pointer and is nil. This will cause panics if an
- // unimplemented method is ever invoked, so we test this at initialization
- // time to prevent it from happening at runtime later due to I/O.
- if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
- t.testEmbeddedByValue()
- }
- s.RegisterService(&Ability_ServiceDesc, srv)
-}
-
-func _Ability_Allowed_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AllowRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AbilityServer).Allowed(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: Ability_Allowed_FullMethodName,
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AbilityServer).Allowed(ctx, req.(*AllowRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-// Ability_ServiceDesc is the grpc.ServiceDesc for Ability service.
-// It's only intended for direct use with grpc.RegisterService,
-// and not to be introspected or modified (even as a copy)
-var Ability_ServiceDesc = grpc.ServiceDesc{
- ServiceName: "authx.rpc.Ability",
- HandlerType: (*AbilityServer)(nil),
- Methods: []grpc.MethodDesc{
- {
- MethodName: "Allowed",
- Handler: _Ability_Allowed_Handler,
- },
- },
- Streams: []grpc.StreamDesc{},
- Metadata: "ability.proto",
-}
diff --git a/pkg/rpc/ability_service.go b/pkg/rpc/ability_service.go
index 18327d52..db2e8fab 100644
--- a/pkg/rpc/ability_service.go
+++ b/pkg/rpc/ability_service.go
@@ -4,12 +4,11 @@ import (
context "context"
"github.com/cedar-policy/cedar-go"
- "gitlab.com/mokhax/spike/pkg/gid"
- "gitlab.com/mokhax/spike/pkg/policies"
+ "gitlab.com/gitlab-org/software-supply-chain-security/authorization/authz.d/pkg/gid"
+ "gitlab.com/gitlab-org/software-supply-chain-security/authorization/authz.d/pkg/policies"
)
type AbilityService struct {
- UnimplementedAbilityServer
}
func NewAbilityService() *AbilityService {
diff --git a/pkg/rpc/server.go b/pkg/rpc/server.go
index 08246b5b..a37df9fc 100644
--- a/pkg/rpc/server.go
+++ b/pkg/rpc/server.go
@@ -1,11 +1,21 @@
package rpc
import (
- grpc "google.golang.org/grpc"
+ fmt "fmt"
+ http "net/http"
)
-func New(options ...grpc.ServerOption) *grpc.Server {
- server := grpc.NewServer(options...)
- RegisterAbilityServer(server, NewAbilityService())
- return server
+func New() http.Handler {
+ mux := http.NewServeMux()
+ for _, handler := range handlers() {
+ fmt.Printf("Registering : %v\n", handler.PathPrefix())
+ mux.Handle(handler.PathPrefix(), handler)
+ }
+ return mux
+}
+
+func handlers() []TwirpServer {
+ return []TwirpServer{
+ NewAbilityServer(NewAbilityService()),
+ }
}
diff --git a/pkg/rpc/server_test.go b/pkg/rpc/server_test.go
index da60f86a..fd6e6237 100644
--- a/pkg/rpc/server_test.go
+++ b/pkg/rpc/server_test.go
@@ -1,35 +1,19 @@
package rpc
import (
- "net"
+ http "net/http"
+ "net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- grpc "google.golang.org/grpc"
- "google.golang.org/grpc/credentials/insecure"
)
func TestServer(t *testing.T) {
- listener, err := net.Listen("tcp", "localhost:0")
- require.NoError(t, err)
- defer listener.Close()
+ srv := httptest.NewServer(New())
+ defer srv.Close()
- server := New()
- defer server.Stop()
-
- go func() {
- require.NoError(t, server.Serve(listener))
- }()
-
- connection, err := grpc.NewClient(
- listener.Addr().String(),
- grpc.WithTransportCredentials(insecure.NewCredentials()),
- )
- require.NoError(t, err)
-
- defer connection.Close()
- client := NewAbilityClient(connection)
+ client := NewAbilityProtobufClient(srv.URL, &http.Client{})
t.Run("forbids", func(t *testing.T) {
reply, err := client.Allowed(t.Context(), &AllowRequest{
diff --git a/pkg/srv/srv.go b/pkg/srv/srv.go
deleted file mode 100644
index e7189406..00000000
--- a/pkg/srv/srv.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package srv
-
-import (
- "log"
- "net/http"
- "time"
-
- "gitlab.com/mokhax/spike/pkg/cfg"
-)
-
-func New(c *cfg.Config) *http.Server {
- return &http.Server{
- Addr: c.BindAddress,
- Handler: c.Mux,
- TLSConfig: c.TLS,
- ReadHeaderTimeout: 10 * time.Second,
- ReadTimeout: 30 * time.Second,
- WriteTimeout: 30 * time.Second,
- IdleTimeout: 30 * time.Second,
- ErrorLog: log.Default(),
- }
-}
-
-func Run(c *cfg.Config) error {
- return c.Run(New(c))
-}
diff --git a/pkg/test/test.go b/pkg/test/test.go
deleted file mode 100644
index 9963323a..00000000
--- a/pkg/test/test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package test
-
-import (
- "context"
- "io"
- "net/http"
- "net/http/httptest"
-)
-
-type RequestOption func(*http.Request) *http.Request
-
-func Request(method, target string, options ...RequestOption) *http.Request {
- request := httptest.NewRequest(method, target, nil)
- for _, option := range options {
- request = option(request)
- }
- return request
-}
-
-func RequestResponse(method, target string, options ...RequestOption) (*http.Request, *httptest.ResponseRecorder) {
- return Request(method, target, options...), httptest.NewRecorder()
-}
-
-func WithRequestHeader(key, value string) RequestOption {
- return func(r *http.Request) *http.Request {
- r.Header.Set(key, value)
- return r
- }
-}
-
-func WithRequestBody(body io.ReadCloser) RequestOption {
- return func(r *http.Request) *http.Request {
- r.Body = body
- return r
- }
-}
-
-func WithContext(ctx context.Context) RequestOption {
- return func(r *http.Request) *http.Request {
- return r.WithContext(ctx)
- }
-}
-
-func WithCookie(cookie *http.Cookie) RequestOption {
- return func(r *http.Request) *http.Request {
- r.AddCookie(cookie)
- return r
- }
-}
diff --git a/protos/ability.proto b/protos/ability.proto
index b440abcc..b58f5027 100644
--- a/protos/ability.proto
+++ b/protos/ability.proto
@@ -1,7 +1,7 @@
syntax = "proto3";
-package authx.rpc;
-option go_package = "gitlab.com/mokhax/spike/pkg/rpc";
+package authz.rpc;
+option go_package = "pkg/rpc";
service Ability {
diff --git a/script/cibuild b/script/cibuild
deleted file mode 100755
index b3da3e15..00000000
--- a/script/cibuild
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env ruby
-
-fork do
- exec "mage servers"
-end
-
-# Let the servers boot up
-sleep 1
-
-test_pid = fork do
- exec "mage test"
-end
-
-Process.wait(test_pid)
-Process.kill('TERM', 0)
diff --git a/test/e2e_test.go b/test/e2e_test.go
deleted file mode 100644
index a36049e3..00000000
--- a/test/e2e_test.go
+++ /dev/null
@@ -1,329 +0,0 @@
-package main
-
-import (
- "bytes"
- "context"
- "encoding/base64"
- "net/http"
- "net/url"
- "strings"
- "testing"
- "time"
-
- "github.com/playwright-community/playwright-go"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "github.com/xlgmokha/x/pkg/env"
- "github.com/xlgmokha/x/pkg/serde"
- "github.com/xlgmokha/x/pkg/x"
- "golang.org/x/oauth2"
-)
-
-func TestAuthx(t *testing.T) {
- if env.Fetch("SKIP_E2E", "") != "" {
- t.Skip()
- }
-
- _ = playwright.Install()
-
- pw := x.Must(playwright.Run())
- browser := x.Must(pw.Firefox.Launch(playwright.BrowserTypeLaunchOptions{
- Headless: playwright.Bool(env.Fetch("HEADLESS", "true") == "true"),
- SlowMo: playwright.Float(1000),
- }))
- page := x.Must(browser.NewPage())
-
- client := &http.Client{Timeout: 2 * time.Second}
-
- defer func() {
- x.Check(browser.Close())
- x.Check(pw.Stop())
- }()
-
- t.Run("SAML", func(t *testing.T) {
- for _, url := range []string{"http://idp.example.com:8080/saml/metadata.xml", "http://ui.example.com:8080/saml/metadata.xml"} {
- t.Run("GET "+url, func(t *testing.T) {
- response := x.Must(http.Get(url))
- assert.Equal(t, http.StatusOK, response.StatusCode)
- })
- }
-
- t.Run("GET http://ui.example.com:8080/saml/new", func(t *testing.T) {
- assert.NoError(t, page.Context().ClearCookies())
- x.Must(page.Goto("http://ui.example.com:8080/saml/new"))
- action := x.Must(page.Locator("#idp-form").GetAttribute("action"))
- assert.Equal(t, "http://idp.example.com:8080/saml/new", action)
- assert.NoError(t, page.Locator("#submit-button").Click())
-
- page.Locator("#username").Fill("root")
- page.Locator("#password").Fill("root")
- assert.NoError(t, page.Locator("#login-button").Click())
-
- action = x.Must(page.Locator("#postback-form").GetAttribute("action"))
- assert.Equal(t, "http://ui.example.com:8080/saml/assertions", action)
- assert.NoError(t, page.Locator("#submit-button").Click())
- assert.Contains(t, x.Must(page.Content()), "Received SAML Response")
-
- t.Run("generates a usable access token", func(t *testing.T) {
- rawToken := x.Must(page.Locator("#access-token").TextContent())
- accessToken := strings.Replace(rawToken, "\"", "", -1)
- assert.NotEmpty(t, accessToken)
-
- t.Run("GET http://api.example.com:8080/projects.json", func(t *testing.T) {
- request := x.Must(http.NewRequestWithContext(t.Context(), "GET", "http://api.example.com:8080/projects.json", nil))
- request.Header.Add("Authorization", "Bearer "+accessToken)
- response := x.Must(client.Do(request))
- require.Equal(t, http.StatusOK, response.StatusCode)
- projects := x.Must(serde.FromJSON[[]map[string]string](response.Body))
- assert.NotNil(t, projects)
- })
- })
-
- t.Run("exchange SAML assertion for access token", func(t *testing.T) {
- samlAssertion := x.Must(page.Locator("#raw-saml-response").TextContent())
- io := bytes.NewBuffer(nil)
- assert.NoError(t, serde.ToJSON(io, map[string]string{
- "assertion": samlAssertion,
- "grant_type": "urn:ietf:params:oauth:grant-type:saml2-bearer",
- }))
- request := x.Must(http.NewRequestWithContext(t.Context(), "POST", "http://idp.example.com:8080/oauth/token", io))
- request.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("client_id:client_secret")))
- request.Header.Add("Content-Type", "application/json ")
- response := x.Must(client.Do(request))
- require.Equal(t, http.StatusOK, response.StatusCode)
- })
- })
- })
-
- t.Run("OIDC", func(t *testing.T) {
- t.Run("GET http://ui.example.com:8080/oidc/new", func(t *testing.T) {
- assert.NoError(t, page.Context().ClearCookies())
- x.Must(page.Goto("http://ui.example.com:8080/oidc/new"))
-
- assert.Contains(t, page.URL(), "http://idp.example.com:8080/sessions/new")
- page.Locator("#username").Fill("root")
- page.Locator("#password").Fill("root")
- assert.NoError(t, page.Locator("#login-button").Click())
-
- assert.Contains(t, page.URL(), "http://idp.example.com:8080/oauth/authorize/continue")
- assert.NoError(t, page.Locator("#submit-button").Click())
-
- assert.Contains(t, page.URL(), "http://ui.example.com:8080/oauth/callback")
- content := x.Must(page.Locator("pre").First().InnerText())
- item := x.Must(serde.FromJSON[oauth2.Token](strings.NewReader(content)))
- require.NotEmpty(t, item.AccessToken)
- require.Equal(t, "Bearer", item.TokenType)
- require.NotEmpty(t, item.RefreshToken)
-
- t.Run("GET http://api.example.com:8080/organizations.json", func(t *testing.T) {
- response := x.Must(http.Get("http://api.example.com:8080/organizations.json"))
- assert.Equal(t, http.StatusForbidden, response.StatusCode)
- })
-
- t.Run("GET http://api.example.com:8080/organizations.json with Authorization", func(t *testing.T) {
- request := x.Must(http.NewRequestWithContext(t.Context(), "GET", "http://api.example.com:8080/organizations.json", nil))
- request.Header.Add("Authorization", "Bearer "+item.AccessToken)
- response := x.Must(client.Do(request))
- require.Equal(t, http.StatusOK, response.StatusCode)
- organizations := x.Must(serde.FromJSON[[]map[string]string](response.Body))
- assert.NotNil(t, organizations)
- })
-
- t.Run("GET http://api.example.com:8080/groups.json", func(t *testing.T) {
- response := x.Must(http.Get("http://api.example.com:8080/groups.json"))
- assert.Equal(t, http.StatusForbidden, response.StatusCode)
- })
-
- t.Run("GET http://api.example.com:8080/groups.json with Authorization", func(t *testing.T) {
- request := x.Must(http.NewRequestWithContext(t.Context(), "GET", "http://api.example.com:8080/groups.json", nil))
- request.Header.Add("Authorization", "Bearer "+item.AccessToken)
- response := x.Must(client.Do(request))
- require.Equal(t, http.StatusOK, response.StatusCode)
- groups := x.Must(serde.FromJSON[[]map[string]string](response.Body))
- assert.NotNil(t, groups)
- })
-
- t.Run("GET http://api.example.com:8080/projects.json", func(t *testing.T) {
- response := x.Must(http.Get("http://api.example.com:8080/projects.json"))
- assert.Equal(t, http.StatusForbidden, response.StatusCode)
- })
-
- t.Run("GET http://api.example.com:8080/projects.json with Authorization", func(t *testing.T) {
- request := x.Must(http.NewRequestWithContext(t.Context(), "GET", "http://api.example.com:8080/projects.json", nil))
- request.Header.Add("Authorization", "Bearer "+item.AccessToken)
- response := x.Must(client.Do(request))
- require.Equal(t, http.StatusOK, response.StatusCode)
- projects := x.Must(serde.FromJSON[[]map[string]string](response.Body))
- assert.NotNil(t, projects)
- })
-
- t.Run("creates a new project", func(t *testing.T) {
- io := bytes.NewBuffer(nil)
- assert.NoError(t, serde.ToJSON(io, map[string]string{"name": "example"}))
- request := x.Must(http.NewRequestWithContext(t.Context(), "POST", "http://api.example.com:8080/projects", io))
- request.Header.Add("Authorization", "Bearer "+item.AccessToken)
- response := x.Must(client.Do(request))
- require.Equal(t, http.StatusCreated, response.StatusCode)
- project := x.Must(serde.FromJSON[map[string]string](response.Body))
- assert.Equal(t, "example", project["name"])
- })
-
- t.Run("creates another project", func(t *testing.T) {
- io := bytes.NewBuffer(nil)
- assert.NoError(t, serde.ToJSON(io, map[string]string{"name": "example2"}))
- request := x.Must(http.NewRequestWithContext(t.Context(), "POST", "http://api.example.com:8080/projects.json", io))
- request.Header.Add("Authorization", "Bearer "+item.AccessToken)
- response := x.Must(client.Do(request))
- require.Equal(t, http.StatusCreated, response.StatusCode)
- project := x.Must(serde.FromJSON[map[string]string](response.Body))
- assert.Equal(t, "example2", project["name"])
- })
- })
- })
-
- t.Run("OAuth", func(t *testing.T) {
- t.Run("GET /.well-known/oauth-authorization-server", func(t *testing.T) {
- response := x.Must(client.Get("http://idp.example.com:8080/.well-known/oauth-authorization-server"))
- require.Equal(t, http.StatusOK, response.StatusCode)
- metadata := x.Must(serde.FromJSON[map[string]interface{}](response.Body))
- assert.Equal(t, "http://idp.example.com:8080/.well-known/oauth-authorization-server", metadata["issuer"])
- assert.Equal(t, "http://idp.example.com:8080/oauth/authorize", metadata["authorization_endpoint"])
- assert.Equal(t, "http://idp.example.com:8080/oauth/token", metadata["token_endpoint"])
- // assert.NotEmpty(t, metadata["jwks_uri"])
- // assert.NotEmpty(t, metadata["registration_endpoint"])
- assert.NotEmpty(t, metadata["scopes_supported"])
- assert.NotEmpty(t, metadata["response_types_supported"])
- assert.NotEmpty(t, metadata["response_modes_supported"])
- assert.NotEmpty(t, metadata["grant_types_supported"])
- assert.NotEmpty(t, metadata["token_endpoint_auth_methods_supported"])
- assert.NotEmpty(t, metadata["token_endpoint_auth_signing_alg_values_supported"])
- // assert.NotEmpty(t, metadata["service_documentation"])
- assert.NotEmpty(t, metadata["ui_locales_supported"])
- // assert.NotEmpty(t, metadata["op_policy_uri"])
- // assert.NotEmpty(t, metadata["op_tos_uri"])
- assert.NotEmpty(t, metadata["revocation_endpoint"])
- assert.NotEmpty(t, metadata["revocation_endpoint_auth_methods_supported"])
- assert.NotEmpty(t, metadata["revocation_endpoint_auth_signing_alg_values_supported"])
- assert.NotEmpty(t, metadata["introspection_endpoint"])
- assert.NotEmpty(t, metadata["introspection_endpoint_auth_methods_supported"])
- assert.NotEmpty(t, metadata["introspection_endpoint_auth_signing_alg_values_supported"])
- // assert.NotEmpty(t, metadata["code_challenge_methods_supported"])
- })
-
- t.Run("GET /.well-known/openid-configuration", func(t *testing.T) {
- response := x.Must(client.Get("http://idp.example.com:8080/.well-known/openid-configuration"))
- require.Equal(t, http.StatusOK, response.StatusCode)
- metadata := x.Must(serde.FromJSON[map[string]interface{}](response.Body))
- assert.Equal(t, "http://idp.example.com:8080/.well-known/oauth-authorization-server", metadata["issuer"])
- assert.Equal(t, "http://idp.example.com:8080/oauth/authorize", metadata["authorization_endpoint"])
- assert.Equal(t, "http://idp.example.com:8080/oauth/token", metadata["token_endpoint"])
- assert.NotEmpty(t, metadata["userinfo_endpoint"])
- // assert.NotEmpty(t, metadata["jwks_uri"])
- // assert.NotEmpty(t, metadata["registration_endpoint"])
- assert.NotEmpty(t, metadata["scopes_supported"])
- assert.NotEmpty(t, metadata["response_types_supported"])
- assert.NotEmpty(t, metadata["response_modes_supported"])
- assert.NotEmpty(t, metadata["grant_types_supported"])
- // assert.NotEmpty(t, metadata["acr_values_supported"])
- assert.NotEmpty(t, metadata["subject_types_supported"])
- assert.NotEmpty(t, metadata["id_token_signing_alg_values_supported"])
- // assert.NotEmpty(t, metadata["id_token_encryption_alg_values_supported"])
- // assert.NotEmpty(t, metadata["id_token_encryption_enc_values_supported"])
- assert.NotEmpty(t, metadata["userinfo_signing_alg_values_supported"])
- // assert.NotEmpty(t, metadata["userinfo_encryption_alg_values_supported"])
- // assert.NotEmpty(t, metadata["userinfo_encryption_enc_values_supported"])
- assert.NotEmpty(t, metadata["request_object_signing_alg_values_supported"])
- // assert.NotEmpty(t, metadata["request_object_encryption_alg_values_supported"])
- // assert.NotEmpty(t, metadata["request_object_encryption_enc_values_supported"])
- assert.NotEmpty(t, metadata["token_endpoint_auth_methods_supported"])
- // assert.NotEmpty(t, metadata["token_endpoint_auth_signing_alg_values_supported"])
- // assert.NotEmpty(t, metadata["display_values_supported"])
- assert.NotEmpty(t, metadata["claim_types_supported"])
- assert.NotEmpty(t, metadata["claims_supported"])
- // assert.NotEmpty(t, metadata["service_documentation"])
- // assert.NotEmpty(t, metadata["claims_locales_supported"])
- assert.NotEmpty(t, metadata["ui_locales_supported"])
- // assert.True(t, metadata["claims_parameter_supported"])
- // assert.True(t, metadata["request_parameter_supported"])
- // assert.True(t, metadata["request_uri_parameter_supported"])
- // assert.True(t, metadata["require_request_uri_registration"])
- // assert.NotEmpty(t, metadata["op_policy_uri"])
- // assert.NotEmpty(t, metadata["op_tos_uri"])
- })
-
- t.Run("authorization code grant", func(t *testing.T) {
- conf := &oauth2.Config{
- ClientID: "client_id",
- ClientSecret: "client_secret",
- Scopes: []string{"openid"},
- Endpoint: oauth2.Endpoint{
- TokenURL: "http://idp.example.com:8080/oauth/token",
- AuthURL: "http://idp.example.com:8080/oauth/authorize",
- },
- }
-
- authURL := conf.AuthCodeURL(
- "state",
- oauth2.SetAuthURLParam("client_id", "client_id"),
- oauth2.SetAuthURLParam("scope", "openid"),
- oauth2.SetAuthURLParam("redirect_uri", "http://example.org/oauth/callback"),
- oauth2.SetAuthURLParam("response_type", "code"),
- oauth2.SetAuthURLParam("response_mode", "fragment"),
- )
- assert.NoError(t, page.Context().ClearCookies())
- x.Must(page.Goto(authURL))
-
- page.Locator("#username").Fill("root")
- page.Locator("#password").Fill("root")
- assert.NoError(t, page.Locator("#login-button").Click())
-
- assert.NoError(t, page.Locator("#submit-button").Click())
-
- uri := x.Must(url.Parse(page.URL()))
- values := x.Must(url.ParseQuery(uri.Fragment))
- code := values.Get("code")
-
- ctx := t.Context()
- ctx = context.WithValue(ctx, oauth2.HTTPClient, client)
- credentials := x.Must(conf.Exchange(ctx, code))
- assert.NotEmpty(t, credentials.AccessToken)
- assert.Equal(t, "Bearer", credentials.TokenType)
- assert.NotEmpty(t, credentials.RefreshToken)
-
- t.Run("cannot re-use the same authorization grant", func(t *testing.T) {
- newCredentials, err := conf.Exchange(ctx, code)
-
- assert.Error(t, err)
- assert.Empty(t, newCredentials)
- })
-
- t.Run("token is usable against REST API", func(t *testing.T) {
- client := conf.Client(ctx, credentials)
- response := x.Must(client.Get("http://api.example.com:8080/projects.json"))
- require.Equal(t, http.StatusOK, response.StatusCode)
- projects := x.Must(serde.FromJSON[[]map[string]string](response.Body))
- assert.NotNil(t, projects)
-
- io := bytes.NewBuffer(nil)
- assert.NoError(t, serde.ToJSON(io, map[string]string{"name": "foo"}))
- response = x.Must(client.Post("http://api.example.com:8080/projects", "application/json", io))
- require.Equal(t, http.StatusCreated, response.StatusCode)
- project := x.Must(serde.FromJSON[map[string]string](response.Body))
- assert.Equal(t, "foo", project["name"])
- })
-
- t.Run("token can be introspected", func(t *testing.T) {
- client := conf.Client(ctx, credentials)
-
- io := bytes.NewBuffer(nil)
- assert.NoError(t, serde.ToJSON(io, map[string]string{"token": credentials.AccessToken}))
- response := x.Must(client.Post("http://idp.example.com:8080/oauth/introspect", "application/json", io))
- require.Equal(t, http.StatusOK, response.StatusCode)
-
- claims := x.Must(serde.FromJSON[map[string]interface{}](response.Body))
- assert.Equal(t, true, claims["active"])
- assert.Equal(t, "gid://example/User/1", claims["sub"])
- })
- })
- })
-}