From 2e2bff7667c5cf7d7860b4a1f21a2efd1ccfd1d7 Mon Sep 17 00:00:00 2001 From: Guilherme Cassolato Date: Mon, 2 Nov 2020 18:47:30 +0100 Subject: [PATCH] PoC from Ostia repo becomes Authorino https://github.com/3scale/ostia/pull/76 --- .gitignore | 1 + .gitmodules | 12 + Dockerfile | 14 + Gemfile | 13 + Gemfile.lock | 93 + README.md | 297 ++- Rakefile | 37 + examples/config.yml | 67 + examples/docker-compose.yaml | 82 + examples/envoy/Dockerfile | 9 + examples/envoy/envoy.yaml | 88 + examples/keycloak-realm.json | 2093 +++++++++++++++++ examples/upstream/Dockerfile | 16 + examples/upstream/Gemfile | 3 + examples/upstream/Gemfile.lock | 13 + examples/upstream/config.ru | 32 + lib/grpc/envoy/api/v2/core/address_pb.rb | 67 + lib/grpc/envoy/api/v2/core/backoff_pb.rb | 26 + lib/grpc/envoy/api/v2/core/base_pb.rb | 165 ++ lib/grpc/envoy/api/v2/core/http_uri_pb.rb | 29 + .../envoy/api/v2/core/socket_option_pb.rb | 37 + lib/grpc/envoy/config/core/v3/address_pb.rb | 74 + lib/grpc/envoy/config/core/v3/backoff_pb.rb | 26 + lib/grpc/envoy/config/core/v3/base_pb.rb | 168 ++ lib/grpc/envoy/config/core/v3/http_uri_pb.rb | 29 + .../envoy/config/core/v3/socket_option_pb.rb | 37 + .../service/auth/v2/attribute_context_pb.rb | 57 + .../envoy/service/auth/v2/external_auth_pb.rb | 45 + .../auth/v2/external_auth_services_pb.rb | 37 + .../service/auth/v3/attribute_context_pb.rb | 59 + .../envoy/service/auth/v3/external_auth_pb.rb | 50 + .../auth/v3/external_auth_services_pb.rb | 37 + lib/grpc/envoy/type/http_status_pb.rb | 79 + lib/grpc/envoy/type/percent_pb.rb | 30 + lib/grpc/envoy/type/semantic_version_pb.rb | 21 + lib/grpc/envoy/type/v3/http_status_pb.rb | 82 + lib/grpc/envoy/type/v3/percent_pb.rb | 33 + lib/grpc/envoy/type/v3/semantic_version_pb.rb | 24 + lib/grpc/udpa/annotations/migrate_pb.rb | 27 + lib/grpc/udpa/annotations/status_pb.rb | 26 + lib/grpc/udpa/annotations/versioning_pb.rb | 18 + pocs/README.md | 22 + pocs/{opa => envoy-opa-authz}/README.md | 0 .../docker-compose.yaml | 0 .../front-envoy/Dockerfile | 0 .../front-envoy/config.yaml | 0 .../front-envoy/run_envoy.sh | 0 .../opa-service/jwks.json | 0 .../opa-service/policy.rego | 0 .../upstream-service/Dockerfile | 0 .../upstream-service/server.py | 0 pocs/keycloak-ruby/tmp/pids/.keep | 0 pocs/keycloak-ruby/vendor/.keep | 0 .../.browserslistrc | 0 .../.gitignore | 0 .../Gemfile | 0 .../Gemfile.lock | 0 .../README.md | 0 .../Rakefile | 0 .../app/assets/config/manifest.js | 0 .../app/assets/images/.keep | 0 .../app/assets/stylesheets/application.css | 0 .../controllers/api/services_controller.rb | 0 .../app/controllers/application_controller.rb | 0 .../app/controllers/concerns/.keep | 0 .../controllers/concerns/authentication.rb | 0 .../app/controllers/concerns/authorization.rb | 0 .../app/controllers/concerns/session_token.rb | 0 .../app/controllers/sessions_controller.rb | 0 .../app/helpers/application_helper.rb | 0 .../app/javascript/packs/application.js | 0 .../app/jobs/application_job.rb | 0 .../app/models/concerns/.keep | 0 .../app/views/api/services/index.html.erb | 0 .../app/views/api/services/new.html.erb | 0 .../app/views/api/services/show.html.erb | 0 .../app/views/layouts/application.html.erb | 0 .../app/views/sessions/show.html.erb | 0 .../babel.config.js | 0 .../bin/bundle | 0 .../bin/rails | 0 .../bin/rake | 0 .../bin/setup | 0 .../bin/spring | 0 .../bin/webpack | 0 .../bin/webpack-dev-server | 0 .../bin/yarn | 0 .../config.ru | 0 .../config/application.rb | 0 .../config/boot.rb | 0 .../config/credentials.yml.enc | 0 .../config/environment.rb | 0 .../config/environments/development.rb | 0 .../config/environments/production.rb | 0 .../config/environments/test.rb | 0 .../application_controller_renderer.rb | 0 .../config/initializers/assets.rb | 0 .../initializers/backtrace_silencers.rb | 0 .../initializers/content_security_policy.rb | 0 .../config/initializers/cookies_serializer.rb | 0 .../initializers/filter_parameter_logging.rb | 0 .../config/initializers/inflections.rb | 0 .../config/initializers/mime_types.rb | 0 .../config/initializers/wrap_parameters.rb | 0 .../config/keycloak.yml | 0 .../config/locales/en.yml | 0 .../config/puma.rb | 0 .../config/routes.rb | 0 .../config/spring.rb | 0 .../config/webpack/development.js | 0 .../config/webpack/environment.js | 0 .../config/webpack/production.js | 0 .../config/webpack/test.js | 0 .../config/webpacker.yml | 0 .../lib/assets/.keep | 0 .../lib/keycloak_adapter.rb | 0 .../lib/keycloak_adapter/adapter.rb | 0 .../keycloak_adapter/oidc_configuration.rb | 0 .../lib/keycloak_adapter/parsed_jwt.rb | 0 .../lib/keycloak_adapter/redirect_uri.rb | 0 .../lib/tasks/.keep | 0 .../log/.keep | 0 .../package.json | 0 .../postcss.config.js | 0 .../public/403.html | 0 .../public/404.html | 0 .../public/422.html | 0 .../public/500.html | 0 .../public/apple-touch-icon-precomposed.png | 0 .../public/apple-touch-icon.png | 0 .../public/favicon.ico | 0 .../public/robots.txt | 0 .../test/application_system_test_case.rb | 0 .../test/controllers/.keep | 0 .../test/fixtures/.keep | 0 .../test/fixtures/files/.keep | 0 .../test/helpers/.keep | 0 .../test/integration/.keep | 0 .../test/models/.keep | 0 .../test/system/.keep | 0 .../test/test_helper.rb | 0 .../tmp => ruby-keycloak-authz/vendor}/.keep | 0 .../yarn.lock | 0 src/auth_service.rb | 73 + src/auth_service/context.rb | 47 + src/auth_service/policy_registry.rb | 19 + src/config.rb | 43 + src/config/authorization.rb | 12 + src/config/authorization/jwt.rb | 11 + src/config/authorization/opa.rb | 107 + src/config/authorization/response.rb | 7 + src/config/identity.rb | 22 + src/config/identity/oidc.rb | 89 + src/config/metadata.rb | 7 + src/config/metadata/user_info.rb | 29 + src/config/service.rb | 21 + src/main.rb | 47 + src/response_interceptor.rb | 11 + test/auth_service_test.rb | 23 + test/config/authorization_test.rb | 35 + test/fixtures/config.yml | 179 ++ test/fixtures/opa_input.json | 77 + test/fixtures/opa_input_forbidden.json | 81 + test/fixtures/request.json | 49 + test/test_helper.rb | 19 + vendor/envoy-xds-grpc/data-plane-api | 1 + vendor/envoy-xds-grpc/googleapis | 1 + vendor/envoy-xds-grpc/protoc-gen-validate | 1 + vendor/envoy-xds-grpc/protos | 23 + vendor/envoy-xds-grpc/udpa | 1 + 170 files changed, 5209 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 100644 Dockerfile create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 Rakefile create mode 100644 examples/config.yml create mode 100644 examples/docker-compose.yaml create mode 100644 examples/envoy/Dockerfile create mode 100644 examples/envoy/envoy.yaml create mode 100644 examples/keycloak-realm.json create mode 100644 examples/upstream/Dockerfile create mode 100644 examples/upstream/Gemfile create mode 100644 examples/upstream/Gemfile.lock create mode 100644 examples/upstream/config.ru create mode 100644 lib/grpc/envoy/api/v2/core/address_pb.rb create mode 100644 lib/grpc/envoy/api/v2/core/backoff_pb.rb create mode 100644 lib/grpc/envoy/api/v2/core/base_pb.rb create mode 100644 lib/grpc/envoy/api/v2/core/http_uri_pb.rb create mode 100644 lib/grpc/envoy/api/v2/core/socket_option_pb.rb create mode 100644 lib/grpc/envoy/config/core/v3/address_pb.rb create mode 100644 lib/grpc/envoy/config/core/v3/backoff_pb.rb create mode 100644 lib/grpc/envoy/config/core/v3/base_pb.rb create mode 100644 lib/grpc/envoy/config/core/v3/http_uri_pb.rb create mode 100644 lib/grpc/envoy/config/core/v3/socket_option_pb.rb create mode 100644 lib/grpc/envoy/service/auth/v2/attribute_context_pb.rb create mode 100644 lib/grpc/envoy/service/auth/v2/external_auth_pb.rb create mode 100644 lib/grpc/envoy/service/auth/v2/external_auth_services_pb.rb create mode 100644 lib/grpc/envoy/service/auth/v3/attribute_context_pb.rb create mode 100644 lib/grpc/envoy/service/auth/v3/external_auth_pb.rb create mode 100644 lib/grpc/envoy/service/auth/v3/external_auth_services_pb.rb create mode 100644 lib/grpc/envoy/type/http_status_pb.rb create mode 100644 lib/grpc/envoy/type/percent_pb.rb create mode 100644 lib/grpc/envoy/type/semantic_version_pb.rb create mode 100644 lib/grpc/envoy/type/v3/http_status_pb.rb create mode 100644 lib/grpc/envoy/type/v3/percent_pb.rb create mode 100644 lib/grpc/envoy/type/v3/semantic_version_pb.rb create mode 100644 lib/grpc/udpa/annotations/migrate_pb.rb create mode 100644 lib/grpc/udpa/annotations/status_pb.rb create mode 100644 lib/grpc/udpa/annotations/versioning_pb.rb create mode 100644 pocs/README.md rename pocs/{opa => envoy-opa-authz}/README.md (100%) rename pocs/{opa => envoy-opa-authz}/docker-compose.yaml (100%) rename pocs/{opa => envoy-opa-authz}/front-envoy/Dockerfile (100%) rename pocs/{opa => envoy-opa-authz}/front-envoy/config.yaml (100%) rename pocs/{opa => envoy-opa-authz}/front-envoy/run_envoy.sh (100%) rename pocs/{opa => envoy-opa-authz}/opa-service/jwks.json (100%) rename pocs/{opa => envoy-opa-authz}/opa-service/policy.rego (100%) rename pocs/{opa => envoy-opa-authz}/upstream-service/Dockerfile (100%) rename pocs/{opa => envoy-opa-authz}/upstream-service/server.py (100%) delete mode 100644 pocs/keycloak-ruby/tmp/pids/.keep delete mode 100644 pocs/keycloak-ruby/vendor/.keep rename pocs/{keycloak-ruby => ruby-keycloak-authz}/.browserslistrc (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/.gitignore (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/Gemfile (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/Gemfile.lock (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/README.md (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/Rakefile (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/assets/config/manifest.js (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/assets/images/.keep (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/assets/stylesheets/application.css (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/controllers/api/services_controller.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/controllers/application_controller.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/controllers/concerns/.keep (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/controllers/concerns/authentication.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/controllers/concerns/authorization.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/controllers/concerns/session_token.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/controllers/sessions_controller.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/helpers/application_helper.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/javascript/packs/application.js (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/jobs/application_job.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/models/concerns/.keep (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/views/api/services/index.html.erb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/views/api/services/new.html.erb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/views/api/services/show.html.erb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/views/layouts/application.html.erb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/app/views/sessions/show.html.erb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/babel.config.js (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/bin/bundle (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/bin/rails (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/bin/rake (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/bin/setup (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/bin/spring (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/bin/webpack (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/bin/webpack-dev-server (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/bin/yarn (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config.ru (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/application.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/boot.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/credentials.yml.enc (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/environment.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/environments/development.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/environments/production.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/environments/test.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/initializers/application_controller_renderer.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/initializers/assets.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/initializers/backtrace_silencers.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/initializers/content_security_policy.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/initializers/cookies_serializer.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/initializers/filter_parameter_logging.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/initializers/inflections.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/initializers/mime_types.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/initializers/wrap_parameters.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/keycloak.yml (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/locales/en.yml (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/puma.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/routes.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/spring.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/webpack/development.js (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/webpack/environment.js (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/webpack/production.js (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/webpack/test.js (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/config/webpacker.yml (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/lib/assets/.keep (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/lib/keycloak_adapter.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/lib/keycloak_adapter/adapter.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/lib/keycloak_adapter/oidc_configuration.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/lib/keycloak_adapter/parsed_jwt.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/lib/keycloak_adapter/redirect_uri.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/lib/tasks/.keep (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/log/.keep (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/package.json (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/postcss.config.js (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/public/403.html (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/public/404.html (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/public/422.html (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/public/500.html (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/public/apple-touch-icon-precomposed.png (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/public/apple-touch-icon.png (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/public/favicon.ico (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/public/robots.txt (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/test/application_system_test_case.rb (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/test/controllers/.keep (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/test/fixtures/.keep (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/test/fixtures/files/.keep (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/test/helpers/.keep (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/test/integration/.keep (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/test/models/.keep (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/test/system/.keep (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/test/test_helper.rb (100%) rename pocs/{keycloak-ruby/tmp => ruby-keycloak-authz/vendor}/.keep (100%) rename pocs/{keycloak-ruby => ruby-keycloak-authz}/yarn.lock (100%) create mode 100644 src/auth_service.rb create mode 100644 src/auth_service/context.rb create mode 100644 src/auth_service/policy_registry.rb create mode 100644 src/config.rb create mode 100644 src/config/authorization.rb create mode 100644 src/config/authorization/jwt.rb create mode 100644 src/config/authorization/opa.rb create mode 100644 src/config/authorization/response.rb create mode 100644 src/config/identity.rb create mode 100644 src/config/identity/oidc.rb create mode 100644 src/config/metadata.rb create mode 100644 src/config/metadata/user_info.rb create mode 100644 src/config/service.rb create mode 100644 src/main.rb create mode 100644 src/response_interceptor.rb create mode 100644 test/auth_service_test.rb create mode 100644 test/config/authorization_test.rb create mode 100644 test/fixtures/config.yml create mode 100644 test/fixtures/opa_input.json create mode 100644 test/fixtures/opa_input_forbidden.json create mode 100644 test/fixtures/request.json create mode 100644 test/test_helper.rb create mode 160000 vendor/envoy-xds-grpc/data-plane-api create mode 160000 vendor/envoy-xds-grpc/googleapis create mode 160000 vendor/envoy-xds-grpc/protoc-gen-validate create mode 100644 vendor/envoy-xds-grpc/protos create mode 160000 vendor/envoy-xds-grpc/udpa diff --git a/.gitignore b/.gitignore index a9a5aecf..39e144d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ tmp +.ruby-version diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..b3ff5389 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,12 @@ +[submodule "envoy-xds-grpc/data-plane-api"] + path = vendor/envoy-xds-grpc/data-plane-api + url = https://github.com/envoyproxy/data-plane-api.git +[submodule "envoy-xds-grpc/googleapis"] + path = vendor/envoy-xds-grpc/googleapis + url = https://github.com/googleapis/googleapis.git +[submodule "envoy-xds-grpc/protoc-gen-validate"] + path = vendor/envoy-xds-grpc/protoc-gen-validate + url = https://github.com/lyft/protoc-gen-validate.git +[submodule "envoy-xds-grpc/udpa"] + path = vendor/envoy-xds-grpc/udpa + url = https://github.com/cncf/udpa.git diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..04637102 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM ruby:2.6 + +RUN gem update bundler \ + && bundle config --global frozen 1 + +WORKDIR /usr/src/app + +COPY Gemfile Gemfile.lock ./ +RUN bundle install + +COPY . . + +ENTRYPOINT [ "sh", "-c" ] +CMD ["exec rake start"] diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..03bbebf4 --- /dev/null +++ b/Gemfile @@ -0,0 +1,13 @@ +source "https://rubygems.org" + +gem "grpc" +gem "grpc-tools" + +gem "google-protobuf" + +gem "rack" +gem 'openid_connect' + +gem "pry-byebug" + +gem 'rake' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000..96c47469 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,93 @@ +GEM + remote: https://rubygems.org/ + specs: + activemodel (6.0.3.4) + activesupport (= 6.0.3.4) + activesupport (6.0.3.4) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + zeitwerk (~> 2.2, >= 2.2.2) + aes_key_wrap (1.1.0) + attr_required (1.0.1) + bindata (2.4.8) + byebug (11.1.3) + coderay (1.1.3) + concurrent-ruby (1.1.7) + google-protobuf (3.13.0) + googleapis-common-protos-types (1.0.5) + google-protobuf (~> 3.11) + grpc (1.32.0) + google-protobuf (~> 3.13) + googleapis-common-protos-types (~> 1.0) + grpc-tools (1.32.0) + httpclient (2.8.3) + i18n (1.8.5) + concurrent-ruby (~> 1.0) + json-jwt (1.13.0) + activesupport (>= 4.2) + aes_key_wrap + bindata + mail (2.7.1) + mini_mime (>= 0.1.1) + method_source (1.0.0) + mini_mime (1.0.2) + minitest (5.14.2) + openid_connect (1.2.0) + activemodel + attr_required (>= 1.0.0) + json-jwt (>= 1.5.0) + rack-oauth2 (>= 1.6.1) + swd (>= 1.0.0) + tzinfo + validate_email + validate_url + webfinger (>= 1.0.1) + pry (0.13.1) + coderay (~> 1.1) + method_source (~> 1.0) + pry-byebug (3.9.0) + byebug (~> 11.0) + pry (~> 0.13.0) + public_suffix (4.0.6) + rack (2.2.3) + rack-oauth2 (1.16.0) + activesupport + attr_required + httpclient + json-jwt (>= 1.11.0) + rack (>= 2.1.0) + rake (13.0.1) + swd (1.2.0) + activesupport (>= 3) + attr_required (>= 0.0.5) + httpclient (>= 2.4) + thread_safe (0.3.6) + tzinfo (1.2.7) + thread_safe (~> 0.1) + validate_email (0.1.6) + activemodel (>= 3.0) + mail (>= 2.2.5) + validate_url (1.0.13) + activemodel (>= 3.0.0) + public_suffix + webfinger (1.1.0) + activesupport + httpclient (>= 2.4) + zeitwerk (2.4.0) + +PLATFORMS + ruby + +DEPENDENCIES + google-protobuf + grpc + grpc-tools + openid_connect + pry-byebug + rack + rake + +BUNDLED WITH + 2.1.4 diff --git a/README.md b/README.md index 7fbd0635..088722a6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,298 @@ # Welcome to Authorino! -Authorino is the home of the 3scale AuthN/AuthZ initiative. +Authorino is an AuthN/AuthZ proxy that implements [Envoy’s external authorization](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/ext_authz) +gRPC protocol. It adds protection to your cloud-native APIs with: +- User authentication (OIDC, user/passwd, mTLS) +- Ad-hoc metadata addition to the authorization payload (user info, resource metadata, web hooks) +- Authorization policy enforcement (built-in and external authorization services, JWT claims, OPA, Keycloak) + +**Current stage:** Proof of Concept + +## How it works + + +``` + ┌────────────┐ ┌─────┐ ┌─────────┐ ┌────────┐ + │API consumer│ │Envoy│ │Authorino│ │Upstream│ + └─────┬──────┘ └──┬──┘ └────┬────┘ └───┬────┘ + │ 1. HTTP request │ │ │ + │ ──────────────────>│ │ │ + │ │ │ │ + │ │ 2. gRPC │ │ + │ │─────────────────>│ │ + │ │ │ │ + │ │ ────┐ │ + │ │ │ 3. Verify identity │ + │ │ <───┘ │ + │ │ │ │ + │ │ ────┐ │ + │ │ │ 4. Add metadata │ + │ │ <───┘ │ + │ │ │ │ + │ │ ────┐ + │ │ │ 5. Evaluate policies + │ │ <───┘ + │ │ │ │ + │ │ 6. OK │ │ + │ │<─────────────────│ │ + │ │ │ │ + │ │ 7. HTTP request │ + │ │───────────────────────────────────────────> + │ │ │ │ + │ │ 8. HTTP response │ + │ <─────────────────────────────────────────────────────────────── + ┌─────┴──────┐ ┌──┴──┐ ┌────┴────┐ ┌───┴────┐ + │API consumer│ │Envoy│ │Authorino│ │Upstream│ + └────────────┘ └─────┘ └─────────┘ └────────┘ +``` + +Authorino is deployed including configuration for steps 3, 4 and 5, for one or more upstream APIs. Then... +1. An _API consumer_ sends a request to the _Envoy_ endpoint, including the `Authorization` and `Host` HTTP headers +2. The Envoy proxy establishes fast gRPC connection with _Authorino_ carrying data of the HTTP request +3. Authorino verifies the identity of the the original requestor, where at least one authentication method/provider should match +4. Authorino integrates external sources to add metadata to the authorization payload, such as user info, attributes of the requested resource and payload-mutating web hooks +5. Authorino dispatches authorization policy evaluation to one or more configured authorization services +6. Authorino and Envoy settle the authorization protocol with either a `200 OK`, `403 Forbidden` or `404 Not found` response +7. If authorized, Envoy redirects to the requested _Upstream_ +8. The Upstream serves the requested resource + +## Features + +Authorino will support at least 3 different authentication methods (i.e., OIDC, user/passwd and mTLS), plus ad-hoc +additions to the authorization payload (e.g., user info, resource metadata, web hooks), and combination of multiple +authorization services (JWT claims, OPA, Keycloak), all driven by configuration. Authorino will also handle caching of +user credentials, permissions, revocations, etc. + +| Feature | Stage | +| -------------------------------------------- | ----------- | +| Multitenancy (multiple upstreams) | POC | +| Identity verification | +| - OpenID Connect (OIDC) | POC | +| - User/passwd | Planned | +| - mTLS | Planned | +| Ad-hoc metadata | +| - OIDC user info | POC | +| - Resource attributes | Planned | +| - Web hooks | In analysis | +| Authorization services | +| - OPA inline Rego policies | POC | +| - OPA simple pattern matching | In analysis | +| - Keycloak (UMA-compliant Authorization API) | In analysis | +| - JWT claims | Planned | +| Caching | +| - OID config | POC | +| - JWKS | POC | +| - Authorization policies | Planned | +| - Revoked access tokens | Planned | +| - Repeated requests | In analysis | + +#### OpenID Connect (OIDC) +Authorino automatically discovers OIDC configurations for the registered issuers and verifies authorization JSON Web +Tokens (JWTs) provided by the API consumers on every request. + +Authorino also automatically fetches the JSON Web Key Sets (JWKS) used to verify the JWT, matching the `kid` (easy key +rotation). + + +``` + ┌─────────┐ ┌───────────┐ + │Authorino│ │OIDC Issuer│ + └────┬────┘ └─────┬─────┘ + │ OIDC discovery │ + │────────────────────────>│ + │ │ + │ Well-Known config │ + │<────────────────────────│ + │ │ + │ Req OIDC certs │ + │────────────────────────>│ + │ │ + │ JWKS │ + │<────────────────────────│ + │ │ + ────┐ │ + │ Verify JWT signature│ + <───┘ │ + │ │ + ────┐ │ + │ Validate JWT │ + <───┘ │ + ┌────┴────┐ ┌─────┴─────┐ + │Authorino│ │OIDC Issuer│ + └─────────┘ └───────────┘ +``` + +#### OPA +You can model authorization policies in [Rego language](https://www.openpolicyagent.org/docs/latest/policy-language/) and +add them as part of the configuration of your protected APIs. Authorino will keep track of changes to the policies and +automatically register them the OPA server. + + +``` + ┌─────────┐ ┌───┐ + │Authorino│ │OPA│ + └────┬────┘ └─┬─┘ + · · +Boot-time: │ Register policy │ + │───────────────────────>│ + │<───────────────────────│ + │ │ + · · +Request-time: │Get document with input │ + │───────────────────────>│ + │ │ + │ ────┐ + │ │ Evaluate policy + │ <───┘ + │ │ + │ 200 OK │ + │<───────────────────────│ + ┌────┴────┐ ┌─┴─┐ + │Authorino│ │OPA│ + └─────────┘ └───┘ +``` + +## Usage + +There are 2 main use cases for Authorino: +- A. protecting APIs +- B. protecting resources and scopes of the APIM system + +We are currently working on the features to support use case A and, at the same time, planning for soon having Authorino +configured and deployed with Red Hat 3scale to support use case B. The latter will allow API providers to configure +access control over resources of the API management system in the same fashion they do for their managed APIs. + +To use Authorino to protect your APIs with OIDC and OPA, please consider the following requirements and deployment options. + +#### Requirements + +1. At least one upstream API (i.e., the one API you want to protect) +2. [Envoy](https://www.envoyproxy.io) proxy managing the HTTP connections to the upstream service and with the [External Authorization Filter](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/ext_authz) configured pointing to Authorino (default port: 50051). +3. For OpenID Connect, an authority that can issue JWTs +4. OPA server (default port: 8181) + +#### Deploy Authorino on Docker + +``` +docker run -v './path/to/config.yml:/usr/src/app/config.yml' -e 'CONFIG=/usr/src/app/config.yml' -p '50051:50051' 3scale/authorino:latest +``` + +#### Deploy Authorino on a Kubernetes cluster + +> TODO + +#### Configuring Authorino + +Authorino configuration is one YAML file and a couple of environment variables: + +| | | +| -------- | --------------------------------------------------------------------------------------- | +| `CONFIG` | Path to the Authorino YAML config | +| `PORT` | TCP Port that Authorino will listen for gRPC call from the Envoy proxy (default: 50051) | + +The structure of the YAML config is something like the following: + +```yaml +:: + enabled: true + identity: # -- list of authentication modes and settings + metadata: # -- list of metadata sources + authorization: # -- list of authorization services and settings + +:: + ... +``` + +#### Inline Rego policies + +For the inline Rego policies in your OPA authorization config, the following objects are available in every document: +- `http_request` +- `identity` +- `metadata` +- `resource` (soon) +- `path` (Array) + +## Check the examples and try it out + +Try the [example](examples) on your Docker environment. + +#### Requirements + +1. Docker + +#### 1. Clone the repo + +Start by cloning the repo: + +```shell +git clone git@github.com:3scale/authorino.git +``` + +#### 2. Run the services + +```shell +cd authorino/examples +docker-compose up --build -d +``` + +You'll get the following components up and running: + +- **Upstream app** + Just a simple rack application that echos back in a JSON whatever is gets in the request. You can control the response by passing the custom HTTP headers X-Echo-Status and X-Echo-Message (both optional). +- **Envoy proxy** + Configured w/ the http filters ext_authz and ratelimit. +- **External AuthN/AuthZ proxy** + The core of this PoC. It implements Envoy's external auth gRPC protocol, verifies identities (only OIDC supported so far), fetches metadata (OIDC user info) and delegates policy evaluation to configured PDPs. +- **OPA service** + An actual Policy Decision Point (PDP) configured in the architecture. +- **Keycloak** + To issue OIDC access tokens. + Admin console: http://localhost:8080/auth/admin (admin/p) + Available users: + - john/p (member) + - jane/p (admin) + +#### 3. Try out with John (member) +```shell +export ACCESS_TOKEN_JOHN=$(curl -k -d 'grant_type=password' -d 'client_id=demo' -d 'username=john' -d 'password=p' "http://localhost:8080/auth/realms/ostia/protocol/openid-connect/token" | jq -r '.access_token') + +curl -H 'Host: upstream:3000' -H "Authorization: Bearer $ACCESS_TOKEN_JOHN" http://localhost:8000/pets -v # 200 OK +curl -H 'Host: upstream:3000' -H "Authorization: Bearer $ACCESS_TOKEN_JOHN" http://localhost:8000/pets/stats -v # 403 Forbidden +``` + +#### 4. Try out with Jane (admin) +```shell +export ACCESS_TOKEN_JANE=$(curl -k -d 'grant_type=password' -d 'client_id=demo' -d 'username=jane' -d 'password=p' "http://localhost:8080/auth/realms/ostia/protocol/openid-connect/token" | jq -r '.access_token') + +curl -H 'Host: upstream:3000' -H "Authorization: Bearer $ACCESS_TOKEN_JANE" http://localhost:8000/pets -v # 200 OK +curl -H 'Host: upstream:3000' -H "Authorization: Bearer $ACCESS_TOKEN_JANE" http://localhost:8000/pets/stats -v # 200 OK + +curl -H 'Host: unknown:1234' -H "Authorization: Bearer $ACCESS_TOKEN_JANE" http://localhost:8000/pets -v # 404 Not Found +``` + +#### 5. Shut down and clean up +``` +docker-compose down +``` diff --git a/Rakefile b/Rakefile new file mode 100644 index 00000000..5f05af93 --- /dev/null +++ b/Rakefile @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +desc "Generates the code that implements the gRPC protocol for Envoy's ext_authz filter" +task :generate do + include_packages = %w[googleapis protoc-gen-validate data-plane-api udpa] + include_packages_args = include_packages.map { |package| "-I./vendor/envoy-xds-grpc/#{package}" }.join(' ') + sh "grpc_tools_ruby_protoc #{include_packages_args} @./vendor/envoy-xds-grpc/protos --ruby_out=lib/grpc --grpc_out=lib/grpc" +end + +task default: %i[generate start] + +desc 'Runs the application' +task :start do + ruby "-Ilib/grpc -Isrc src/main.rb" +end + +desc 'Loads the application source' +task :app do + $LOAD_PATH << 'src' << 'lib/grpc' + require 'main' +end + +desc 'Launches a console with the application source loaded' +task console: %i[app] do + require 'pry' + Pry.start +end + +desc 'Run the tests' +task :test do |_task, test_files| + files = test_files.to_a.flat_map(&:split) + files = Dir['test/**/*_test.rb'] if files.empty? + files.each do |file| + puts "Running test #{file}" + ruby "-Ilib/grpc -Itest -Isrc #{file}" + end +end diff --git a/examples/config.yml b/examples/config.yml new file mode 100644 index 00000000..9ca732ab --- /dev/null +++ b/examples/config.yml @@ -0,0 +1,67 @@ +upstream:3000: + enabled: true + + identity: + - oidc: + name: keycloak + endpoint: http://keycloak:8080/auth/realms/ostia + + metadata: + - userinfo: + oidc: keycloak + client_id: authorino + client_secret: 2e5246f2-f4ef-4d55-8225-36e725071dee + + authorization: + - opa: + uuid: 8fa79d93-0f93-4e23-8c2a-666be266cad1 + endpoint: 'http://opa:8181' + rego: | + allow { + http_request.method == "GET" + path = ["pets"] + } + + allow { + http_request.method == "POST" + path = ["pets"] + } + + allow { + http_request.method == "GET" + own_resource + } + + allow { + http_request.method == "PUT" + own_resource + } + + allow { + http_request.method == "DELETE" + own_resource + } + + allow { + http_request.method == "GET" + path = ["pets", "stats"] + is_admin + } + + own_resource { + some petid + path = ["pets", petid] + subject := object.get(identity, "sub", object.get(identity, "username", "")) + subject == object.get(resource, "owner", "") + } + + is_admin { + identity.realm_access.roles[_] == "admin" + } + - jwt: + enabled: false + match: + http: + path: '/api/*' + claim: + aud: api diff --git a/examples/docker-compose.yaml b/examples/docker-compose.yaml new file mode 100644 index 00000000..a64f3ace --- /dev/null +++ b/examples/docker-compose.yaml @@ -0,0 +1,82 @@ +version: "3.7" +services: + + upstream: + build: + context: upstream + dockerfile: Dockerfile + environment: + PORT: 3000 + networks: + envoymesh: + aliases: + - upstream + expose: + - "3000" + + envoy: + build: + context: envoy + dockerfile: Dockerfile + volumes: + - ./envoy/envoy.yaml:/etc/envoy.yaml + networks: + - envoymesh + environment: + LOG_LEVEL: debug + COMPONENT_LOG_LEVEL: filter:trace,http:debug,router:debug + NGINX_HTTP_PORT_NUMBER: 8000 + expose: + - "8000" + - "8001" + ports: + - "8000:8000" + - "8001:8001" + + authorino: + build: + context: ../ + dockerfile: Dockerfile + depends_on: + - opa + networks: + envoymesh: + aliases: + - auth + expose: + - "50051" + + opa: + image: openpolicyagent/opa + ports: + - "8181:8181" + expose: + - "8181" + command: + - run + - --server + - --log-level=debug + - --log-format=json-pretty + - --set=decision_logs.console=true + networks: + envoymesh: + aliases: + - opa + + keycloak: + image: jboss/keycloak + environment: + KEYCLOAK_USER: admin + KEYCLOAK_PASSWORD: p + KEYCLOAK_IMPORT: /tmp/import-realm.json -Dkeycloak.profile.feature.upload_scripts=enabled + volumes: + - ./keycloak-realm.json:/tmp/import-realm.json + ports: + - "8080:8080" + networks: + envoymesh: + aliases: + - keycloak + +networks: + envoymesh: {} diff --git a/examples/envoy/Dockerfile b/examples/envoy/Dockerfile new file mode 100644 index 00000000..c674dfeb --- /dev/null +++ b/examples/envoy/Dockerfile @@ -0,0 +1,9 @@ +FROM envoyproxy/envoy-dev:latest + +RUN apt-get update && apt-get -q install -y \ + curl +CMD /usr/local/bin/envoy \ + --config-path /etc/envoy.yaml \ + --service-cluster front-proxy \ + --log-level ${LOG_LEVEL} \ + --component-log-level ${COMPONENT_LOG_LEVEL} diff --git a/examples/envoy/envoy.yaml b/examples/envoy/envoy.yaml new file mode 100644 index 00000000..2cca6c85 --- /dev/null +++ b/examples/envoy/envoy.yaml @@ -0,0 +1,88 @@ +static_resources: + listeners: + - address: + socket_address: + address: 0.0.0.0 + port_value: 8000 + filter_chains: + - filters: + + - name: envoy.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: local + route_config: + name: local_route + virtual_hosts: + - name: static_response + domains: ['*'] + routes: + - match: + prefix: "/direct" + direct_response: + status: 200 + body: + inline_string: 'direct' + - match: { prefix: / } + route: + cluster: upstream + http_filters: + - name: envoy.filters.http.ext_authz + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz + failure_mode_allow: false + include_peer_certificate: true + grpc_service: + envoy_grpc: + cluster_name: external_auth + - name: envoy.filters.http.router + typed_config: {} + clusters: + - name: docker_host + connect_timeout: 0.25s + type: strict_dns + lb_policy: round_robin + load_assignment: + cluster_name: docker_host + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: host.docker.internal + port_value: 9292 + + - name: external_auth + connect_timeout: 0.25s + type: strict_dns + lb_policy: round_robin + http2_protocol_options: {} + load_assignment: + cluster_name: external_auth + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: authorino + port_value: 50051 + - name: upstream + connect_timeout: 0.25s + type: strict_dns + lb_policy: round_robin + load_assignment: + cluster_name: upstream + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: upstream + port_value: 3000 + +admin: + access_log_path: "/dev/null" + address: + socket_address: + address: 0.0.0.0 + port_value: 8001 diff --git a/examples/keycloak-realm.json b/examples/keycloak-realm.json new file mode 100644 index 00000000..d3ab73fe --- /dev/null +++ b/examples/keycloak-realm.json @@ -0,0 +1,2093 @@ +{ + "id": "ostia", + "realm": "ostia", + "notBefore": 0, + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "users" : [ + { + "username": "john", + "firstName": "John", + "lastName": "Doe", + "email": "john@ostia.io", + "emailVerified": true, + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "p" + } + ], + "realmRoles": ["offline_access", "uma_authorization", "member"], + "applicationRoles": { + "realm-management": ["realm-admin"], + "account": ["manage-account"] + } + }, + { + "username": "jane", + "firstName": "Jane", + "lastName": "Smith", + "email": "jane@ostia.io", + "emailVerified": true, + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "p" + } + ], + "realmRoles": ["offline_access", "uma_authorization", "member", "admin"], + "applicationRoles": { + "realm-management": ["realm-admin"], + "account": ["manage-account"] + } + } + ], + "roles": { + "realm": [ + { + "id": "016eb144-6e56-40cc-8193-298a7ef61bc7", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "ostia", + "attributes": {} + }, + { + "id": "25377c8b-5dd7-4c86-82e5-c386e068a3be", + "name": "member", + "composite": false, + "clientRole": false, + "containerId": "ostia", + "attributes": {} + }, + { + "id": "101ae66f-b0c7-4277-bc13-5c582d31d0e5", + "name": "uma_authorization", + "composite": false, + "clientRole": false, + "containerId": "ostia", + "attributes": {} + }, + { + "id": "345fb849-2035-46ac-8954-27bb0ba12af5", + "name": "admin", + "composite": false, + "clientRole": false, + "containerId": "ostia", + "attributes": {} + } + ], + "client": { + "realm-management": [ + { + "id": "951b80a7-86ff-4e6b-ab32-6e042113d3dc", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "bedff86b-46a2-43b3-938e-4ab7ab6069bf", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "d92c966d-ac1a-47af-a25b-3543119dcb17", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "4d0430b2-69eb-44b8-8f93-bb4613cb45f1", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "75004375-c646-438f-8365-65236aa27b24", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "5125a1f4-e0df-4342-a592-7795b9b5d89c", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-realms", + "manage-users", + "impersonation", + "view-realm", + "view-authorization", + "create-client", + "view-identity-providers", + "manage-identity-providers", + "manage-authorization", + "query-users", + "manage-clients", + "view-events", + "manage-events", + "view-users", + "view-clients", + "query-clients", + "query-groups", + "manage-realm" + ] + } + }, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "a29117cf-29e4-4c34-89d8-f0117f710718", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "35bcc1d0-36dd-4e43-b353-409c542b06d1", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "8c06ab30-7ae8-41c4-8e0d-ff1973aa2783", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "c165a20f-6e90-4806-a5be-6ea97aed763c", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "74b888bb-75c0-4e18-989a-1307ea6ec3a5", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "72cdbda7-dc4c-4c3c-8639-a24450e9babf", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "e5d517d2-8a03-49cc-a10c-a7e1a90159ae", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "fa861fe4-fa20-471d-8254-54931f06c9af", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "c805a86f-4db6-4fe0-a0dd-36e21dafb0e4", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-users", + "query-groups" + ] + } + }, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "e00a1f87-f535-41de-acb4-20ccc940a670", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-clients" + ] + } + }, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "8d9a23bd-5095-4da6-bc09-3a1d6da8dfae", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "dee61f03-4f19-4d94-a30a-d67c9bf07abd", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + }, + { + "id": "bf316526-d595-47f4-93e8-71cdcadb6d01", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "attributes": {} + } + ], + "security-admin-console": [], + "admin-cli": [], + "account-console": [], + "broker": [], + "authorino": [], + "demo": [], + "account": [ + { + "id": "6c31eba4-c225-4bfc-bb60-dabd5e245921", + "name": "view-profile", + "composite": false, + "clientRole": true, + "containerId": "b3361fc5-ea92-4062-b287-75720b547773", + "attributes": {} + }, + { + "id": "8501bbef-26c2-4913-ac5a-8233598828b8", + "name": "manage-account", + "composite": false, + "clientRole": true, + "containerId": "b3361fc5-ea92-4062-b287-75720b547773", + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRoles": [ + "offline_access", + "uma_authorization", + "member" + ], + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpSupportedApplications": [ + "FreeOTP", + "Google Authenticator" + ], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": [ + "manage-account" + ] + } + ] + }, + "clients": [ + { + "id": "b3361fc5-ea92-4062-b287-75720b547773", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/ostia/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "defaultRoles": [ + "view-profile", + "manage-account" + ], + "redirectUris": [ + "/realms/ostia/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "role_list", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "0a714b9a-44ab-4507-8afa-17167e9e9869", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/ostia/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [ + "/realms/ostia/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "816cc033-4303-4ca1-9431-3ec54b342961", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "role_list", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "504b354d-5bd9-42bd-98a9-5d21bbd45e14", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "role_list", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "e5bea24d-75e6-4e7d-8fef-d4815844fc1f", + "clientId": "authorino", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "2e5246f2-f4ef-4d55-8225-36e725071dee", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "role_list", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "ff50f9d3-8bd5-47c8-8462-df347d438478", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "role_list", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "a75a6474-5e2d-4d49-bbb4-e9e3382e8e4b", + "clientId": "demo", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "role_list", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "95f343a0-1634-46ba-9df0-6216165ed1c5", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "role_list", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "e6840039-7892-4bb1-b743-49507bc2fda7", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/ostia/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [ + "/admin/ostia/console/*" + ], + "webOrigins": [ + "+" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "1b551264-c66e-43a9-87a3-36b9bdb704c3", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "role_list", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + } + ], + "clientScopes": [ + { + "id": "5c386b64-0fc2-4dcd-896f-e1cc3a52cb5b", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "c44d9617-d453-4a0b-852e-81d6883ffd07", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "5f2f929d-c516-42f2-8cbb-6d7717b5c1ae", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "d93e084d-c3d8-4262-8ce6-008842441a22", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "b862aff5-b990-47e9-8db7-dfb576db0beb", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "ef7b7529-e518-4b01-966a-4c896db57e20", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "b21988b2-d265-44d9-b364-85ae57c2f821", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "userinfo.token.claim": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + }, + { + "id": "880ba475-d67d-4208-87a4-ff74f010aec3", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "630b7369-04d1-43db-b7b0-4e83bddb0659", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "9810572c-351b-4c9d-bbb0-01d7b84f3637", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "dda8c9bf-6d41-4f03-8329-ec3eb4550896", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "ede87d4d-8e28-4173-8fcc-96c2de4861d1", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "bd3cfac8-4d6b-4a40-9598-b66759e7b782", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "eea34fa2-0521-4db7-9f61-b3afe60d85d4", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "22dda322-717a-4c2d-ad6c-fac1171eb683", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "68871a2a-4e5a-40da-ba33-918d22581897", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "String" + } + }, + { + "id": "3238b3bf-5229-4b57-bc79-036c8f364a9b", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "d1d1ddac-f12a-4de2-bdd5-2e6d1b777113", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "75dbba09-4f26-43b6-ac89-6b65f6545cc8", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "6971666d-ec04-46e6-a7ef-7cda28f75d62", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "ed3f245a-7708-4660-8a64-ce71518ff4a4", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "a0c47cee-2d4f-41b3-9ad4-a71ff7c22ad7", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "6d37a45e-dfc0-4df9-8862-10f088f5e646", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "71b16b64-3014-4476-8edd-2056f091eb0b", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "1de96838-05b2-4c25-bca2-ca9567e9848c", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "8461ddf6-eba6-4dba-832e-5ef9e1faf078", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "d7b619a9-f039-42dc-8617-401ab2e5d1e7", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "bc64a675-1e23-415e-b996-560147dacf4f", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "85f88734-8e7b-4bc5-b069-aa31324bb422", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "609dfdbc-f292-4741-80f6-d9fc71112d56", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "2948e81e-3441-4e4c-b222-fdde6f51259c", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "6b62f18d-4e68-4057-9bad-13ac21859601", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "33d32aa3-bd75-40a1-9f2b-dc1f643c142c", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "id": "20c56af4-1d3c-4df6-a1e6-f084c5e2702e", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "6ab84f70-032d-40a8-87a1-f87c4108dcb3", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + } + ], + "defaultDefaultClientScopes": [ + "web-origins", + "email", + "roles", + "role_list", + "profile" + ], + "defaultOptionalClientScopes": [ + "address", + "offline_access", + "phone", + "microprofile-jwt" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "de2eedaf-44fc-4528-b697-862aadad7d43", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "saml-role-list-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-full-name-mapper", + "oidc-usermodel-property-mapper", + "saml-user-attribute-mapper", + "oidc-address-mapper", + "oidc-sha256-pairwise-sub-mapper", + "saml-user-property-mapper" + ] + } + }, + { + "id": "ace22f54-6185-4188-aabd-fe218f95b06d", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "e119866f-b7d4-4014-a3d5-ff5d50a75865", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "id": "d7084729-2d49-4d96-849f-c08f242e0e9d", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-usermodel-property-mapper", + "oidc-address-mapper", + "saml-user-property-mapper", + "oidc-full-name-mapper", + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-usermodel-attribute-mapper", + "saml-user-attribute-mapper" + ] + } + }, + { + "id": "1ebc41b7-1efd-4be9-99d8-1e003286f3c2", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "8020a77e-6a63-4400-8a71-3735bbe30833", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "c02cb812-0268-4d95-8028-98a01e7618ae", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "id": "8d44c253-fc37-4321-8afc-85b5a616500b", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "1d3fec4f-37df-427f-849d-2b608d0575bc", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS256" + ] + } + }, + { + "id": "be3ac094-cad8-4090-aa6e-d8fcc273dcf1", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "2333bcc7-b919-4966-8833-8cae94773bdd", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "id": "d272d490-b415-41ae-9a67-7b083933fad3", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 20, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "1c209af1-cfa0-4caf-89a6-3035dfd09b4b", + "alias": "Authentication Options", + "description": "Authentication options.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "basic-auth", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "basic-auth-otp", + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "requirement": "DISABLED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "cfe3a232-2947-43a0-bc2d-b33d08ef7c82", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "2fa2ec0b-afa3-40ca-89b8-3c5adfde78fd", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-otp", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "8db429e5-fe33-4585-a579-6d48130e014c", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "440dfb40-41fa-4132-8c14-4f112481f3fd", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "Account verification options", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "9050fc51-4eec-4fb5-b2a0-2dc317814c62", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-otp", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "4a255355-8b01-4f6c-923b-0940b2b9fdd3", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 20, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "4b730175-64c9-41a6-809c-e986256ff0b5", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 20, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "6a4d0bf3-eb4f-4680-81a0-550deddac12e", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "identity-provider-redirector", + "requirement": "ALTERNATIVE", + "priority": 25, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 30, + "flowAlias": "forms", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "6cdd2931-8d36-4264-8709-6c967547f014", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-jwt", + "requirement": "ALTERNATIVE", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-secret-jwt", + "requirement": "ALTERNATIVE", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-x509", + "requirement": "ALTERNATIVE", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "9b36f39a-90e3-4963-b94a-2926393f487f", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-password", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 30, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "37147aa5-d006-4985-9e2b-b5a6ea59aafe", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "31e9b6be-935d-4971-9686-e6ec3d108bfe", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "User creation or linking", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "9f16c1c8-756e-4bcf-a19c-34441b81afe8", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 20, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "d6513ab5-ba10-4cee-9a24-8000a53c9f8b", + "alias": "http challenge", + "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "no-cookie-redirect", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "Authentication Options", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "79841a41-132b-413b-ba45-9477c9b2b5ca", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "requirement": "REQUIRED", + "priority": 10, + "flowAlias": "registration form", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "87df7291-a59a-412f-8fc0-42b3b0e81bf4", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-profile-action", + "requirement": "REQUIRED", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-password-action", + "requirement": "REQUIRED", + "priority": 50, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-recaptcha-action", + "requirement": "DISABLED", + "priority": 60, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "19a6dc67-ae9c-4a6c-aca1-d1f404858083", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-credential-email", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-password", + "requirement": "REQUIRED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 40, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "91f6a3ce-97ff-4416-af17-036c94856421", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "9cb37d71-cf2c-4d55-a9a5-935321a075fd", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "bd78987a-03d9-4658-a4ed-bbb248e77ea9", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "terms_and_conditions", + "name": "Terms and Conditions", + "providerId": "terms_and_conditions", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "clientOfflineSessionMaxLifespan": "0", + "clientSessionIdleTimeout": "0", + "clientSessionMaxLifespan": "0", + "clientOfflineSessionIdleTimeout": "0" + }, + "keycloakVersion": "11.0.2", + "userManagedAccessAllowed": false +} diff --git a/examples/upstream/Dockerfile b/examples/upstream/Dockerfile new file mode 100644 index 00000000..73684859 --- /dev/null +++ b/examples/upstream/Dockerfile @@ -0,0 +1,16 @@ +FROM ruby:2.7 + +RUN bundle config --global frozen 1 + +WORKDIR /usr/src/app + +ENV PORT=8080 \ + ENVIRONMENT=production + +COPY Gemfile Gemfile.lock ./ +RUN bundle install + +COPY . . + +ENTRYPOINT [ "sh", "-c" ] +CMD ["exec rackup --port $PORT --env $ENVIRONMENT"] diff --git a/examples/upstream/Gemfile b/examples/upstream/Gemfile new file mode 100644 index 00000000..689b9ac2 --- /dev/null +++ b/examples/upstream/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gem 'rack' diff --git a/examples/upstream/Gemfile.lock b/examples/upstream/Gemfile.lock new file mode 100644 index 00000000..8ea378e0 --- /dev/null +++ b/examples/upstream/Gemfile.lock @@ -0,0 +1,13 @@ +GEM + remote: https://rubygems.org/ + specs: + rack (2.2.2) + +PLATFORMS + ruby + +DEPENDENCIES + rack + +BUNDLED WITH + 2.1.4 diff --git a/examples/upstream/config.ru b/examples/upstream/config.ru new file mode 100644 index 00000000..67e65200 --- /dev/null +++ b/examples/upstream/config.ru @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'json' +require 'securerandom' + +class RackApp + def call(env) + request = Rack::Request.new(env) + request_method = request.request_method + request_path = request.path + request_query_string = request.query_string + request_query_string = nil if request_query_string.empty? + request_headers = env.select { |header, _| header.start_with?('HTTP_') || header == 'CONTENT_LENGTH' || header == 'CONTENT_TYPE' } + response_status = request_headers['HTTP_X_ECHO_STATUS']&.to_i || 200 + response_message = request_headers['HTTP_X_ECHO_MESSAGE'] + response_content_type = response_message ? 'text/plain' : 'application/json' + response_message ||= JSON.pretty_generate( + method: request_method, + path: request_path, + query_string: request_query_string, + body: request.body.read, + headers: request_headers, + uuid: SecureRandom.uuid + ) + + puts "[#{Time.now}] #{request_method} #{[request_path, request_query_string].compact.join('?')} => #{response_status}" + + [response_status, { 'Content-Type' => response_content_type }, [response_message]] + end +end + +run RackApp.new diff --git a/lib/grpc/envoy/api/v2/core/address_pb.rb b/lib/grpc/envoy/api/v2/core/address_pb.rb new file mode 100644 index 00000000..f5fa6bce --- /dev/null +++ b/lib/grpc/envoy/api/v2/core/address_pb.rb @@ -0,0 +1,67 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/api/v2/core/address.proto + +require 'google/protobuf' + +require 'envoy/api/v2/core/socket_option_pb' +require 'google/protobuf/wrappers_pb' +require 'udpa/annotations/migrate_pb' +require 'udpa/annotations/status_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/api/v2/core/address.proto", :syntax => :proto3) do + add_message "envoy.api.v2.core.Pipe" do + optional :path, :string, 1 + optional :mode, :uint32, 2 + end + add_message "envoy.api.v2.core.SocketAddress" do + optional :protocol, :enum, 1, "envoy.api.v2.core.SocketAddress.Protocol" + optional :address, :string, 2 + optional :resolver_name, :string, 5 + optional :ipv4_compat, :bool, 6 + oneof :port_specifier do + optional :port_value, :uint32, 3 + optional :named_port, :string, 4 + end + end + add_enum "envoy.api.v2.core.SocketAddress.Protocol" do + value :TCP, 0 + value :UDP, 1 + end + add_message "envoy.api.v2.core.TcpKeepalive" do + optional :keepalive_probes, :message, 1, "google.protobuf.UInt32Value" + optional :keepalive_time, :message, 2, "google.protobuf.UInt32Value" + optional :keepalive_interval, :message, 3, "google.protobuf.UInt32Value" + end + add_message "envoy.api.v2.core.BindConfig" do + optional :source_address, :message, 1, "envoy.api.v2.core.SocketAddress" + optional :freebind, :message, 2, "google.protobuf.BoolValue" + repeated :socket_options, :message, 3, "envoy.api.v2.core.SocketOption" + end + add_message "envoy.api.v2.core.Address" do + oneof :address do + optional :socket_address, :message, 1, "envoy.api.v2.core.SocketAddress" + optional :pipe, :message, 2, "envoy.api.v2.core.Pipe" + end + end + add_message "envoy.api.v2.core.CidrRange" do + optional :address_prefix, :string, 1 + optional :prefix_len, :message, 2, "google.protobuf.UInt32Value" + end + end +end + +module Envoy + module Api + module V2 + module Core + Pipe = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.Pipe").msgclass + SocketAddress = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.SocketAddress").msgclass + SocketAddress::Protocol = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.SocketAddress.Protocol").enummodule + TcpKeepalive = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.TcpKeepalive").msgclass + BindConfig = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.BindConfig").msgclass + Address = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.Address").msgclass + CidrRange = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.CidrRange").msgclass + end + end + end +end diff --git a/lib/grpc/envoy/api/v2/core/backoff_pb.rb b/lib/grpc/envoy/api/v2/core/backoff_pb.rb new file mode 100644 index 00000000..06b7a37d --- /dev/null +++ b/lib/grpc/envoy/api/v2/core/backoff_pb.rb @@ -0,0 +1,26 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/api/v2/core/backoff.proto + +require 'google/protobuf' + +require 'google/protobuf/duration_pb' +require 'udpa/annotations/migrate_pb' +require 'udpa/annotations/status_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/api/v2/core/backoff.proto", :syntax => :proto3) do + add_message "envoy.api.v2.core.BackoffStrategy" do + optional :base_interval, :message, 1, "google.protobuf.Duration" + optional :max_interval, :message, 2, "google.protobuf.Duration" + end + end +end + +module Envoy + module Api + module V2 + module Core + BackoffStrategy = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.BackoffStrategy").msgclass + end + end + end +end diff --git a/lib/grpc/envoy/api/v2/core/base_pb.rb b/lib/grpc/envoy/api/v2/core/base_pb.rb new file mode 100644 index 00000000..4d6494ca --- /dev/null +++ b/lib/grpc/envoy/api/v2/core/base_pb.rb @@ -0,0 +1,165 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/api/v2/core/base.proto + +require 'google/protobuf' + +require 'envoy/api/v2/core/address_pb' +require 'envoy/api/v2/core/backoff_pb' +require 'envoy/api/v2/core/http_uri_pb' +require 'envoy/type/percent_pb' +require 'envoy/type/semantic_version_pb' +require 'google/protobuf/any_pb' +require 'google/protobuf/duration_pb' +require 'google/protobuf/struct_pb' +require 'google/protobuf/wrappers_pb' +require 'udpa/annotations/migrate_pb' +require 'udpa/annotations/status_pb' +require 'envoy/api/v2/core/socket_option_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/api/v2/core/base.proto", :syntax => :proto3) do + add_message "envoy.api.v2.core.Locality" do + optional :region, :string, 1 + optional :zone, :string, 2 + optional :sub_zone, :string, 3 + end + add_message "envoy.api.v2.core.BuildVersion" do + optional :version, :message, 1, "envoy.type.SemanticVersion" + optional :metadata, :message, 2, "google.protobuf.Struct" + end + add_message "envoy.api.v2.core.Extension" do + optional :name, :string, 1 + optional :category, :string, 2 + optional :type_descriptor, :string, 3 + optional :version, :message, 4, "envoy.api.v2.core.BuildVersion" + optional :disabled, :bool, 5 + end + add_message "envoy.api.v2.core.Node" do + optional :id, :string, 1 + optional :cluster, :string, 2 + optional :metadata, :message, 3, "google.protobuf.Struct" + optional :locality, :message, 4, "envoy.api.v2.core.Locality" + optional :build_version, :string, 5 + optional :user_agent_name, :string, 6 + repeated :extensions, :message, 9, "envoy.api.v2.core.Extension" + repeated :client_features, :string, 10 + repeated :listening_addresses, :message, 11, "envoy.api.v2.core.Address" + oneof :user_agent_version_type do + optional :user_agent_version, :string, 7 + optional :user_agent_build_version, :message, 8, "envoy.api.v2.core.BuildVersion" + end + end + add_message "envoy.api.v2.core.Metadata" do + map :filter_metadata, :string, :message, 1, "google.protobuf.Struct" + end + add_message "envoy.api.v2.core.RuntimeUInt32" do + optional :default_value, :uint32, 2 + optional :runtime_key, :string, 3 + end + add_message "envoy.api.v2.core.RuntimeDouble" do + optional :default_value, :double, 1 + optional :runtime_key, :string, 2 + end + add_message "envoy.api.v2.core.RuntimeFeatureFlag" do + optional :default_value, :message, 1, "google.protobuf.BoolValue" + optional :runtime_key, :string, 2 + end + add_message "envoy.api.v2.core.HeaderValue" do + optional :key, :string, 1 + optional :value, :string, 2 + end + add_message "envoy.api.v2.core.HeaderValueOption" do + optional :header, :message, 1, "envoy.api.v2.core.HeaderValue" + optional :append, :message, 2, "google.protobuf.BoolValue" + end + add_message "envoy.api.v2.core.HeaderMap" do + repeated :headers, :message, 1, "envoy.api.v2.core.HeaderValue" + end + add_message "envoy.api.v2.core.DataSource" do + oneof :specifier do + optional :filename, :string, 1 + optional :inline_bytes, :bytes, 2 + optional :inline_string, :string, 3 + end + end + add_message "envoy.api.v2.core.RetryPolicy" do + optional :retry_back_off, :message, 1, "envoy.api.v2.core.BackoffStrategy" + optional :num_retries, :message, 2, "google.protobuf.UInt32Value" + end + add_message "envoy.api.v2.core.RemoteDataSource" do + optional :http_uri, :message, 1, "envoy.api.v2.core.HttpUri" + optional :sha256, :string, 2 + optional :retry_policy, :message, 3, "envoy.api.v2.core.RetryPolicy" + end + add_message "envoy.api.v2.core.AsyncDataSource" do + oneof :specifier do + optional :local, :message, 1, "envoy.api.v2.core.DataSource" + optional :remote, :message, 2, "envoy.api.v2.core.RemoteDataSource" + end + end + add_message "envoy.api.v2.core.TransportSocket" do + optional :name, :string, 1 + oneof :config_type do + optional :config, :message, 2, "google.protobuf.Struct" + optional :typed_config, :message, 3, "google.protobuf.Any" + end + end + add_message "envoy.api.v2.core.RuntimeFractionalPercent" do + optional :default_value, :message, 1, "envoy.type.FractionalPercent" + optional :runtime_key, :string, 2 + end + add_message "envoy.api.v2.core.ControlPlane" do + optional :identifier, :string, 1 + end + add_enum "envoy.api.v2.core.RoutingPriority" do + value :DEFAULT, 0 + value :HIGH, 1 + end + add_enum "envoy.api.v2.core.RequestMethod" do + value :METHOD_UNSPECIFIED, 0 + value :GET, 1 + value :HEAD, 2 + value :POST, 3 + value :PUT, 4 + value :DELETE, 5 + value :CONNECT, 6 + value :OPTIONS, 7 + value :TRACE, 8 + value :PATCH, 9 + end + add_enum "envoy.api.v2.core.TrafficDirection" do + value :UNSPECIFIED, 0 + value :INBOUND, 1 + value :OUTBOUND, 2 + end + end +end + +module Envoy + module Api + module V2 + module Core + Locality = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.Locality").msgclass + BuildVersion = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.BuildVersion").msgclass + Extension = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.Extension").msgclass + Node = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.Node").msgclass + Metadata = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.Metadata").msgclass + RuntimeUInt32 = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.RuntimeUInt32").msgclass + RuntimeDouble = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.RuntimeDouble").msgclass + RuntimeFeatureFlag = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.RuntimeFeatureFlag").msgclass + HeaderValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.HeaderValue").msgclass + HeaderValueOption = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.HeaderValueOption").msgclass + HeaderMap = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.HeaderMap").msgclass + DataSource = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.DataSource").msgclass + RetryPolicy = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.RetryPolicy").msgclass + RemoteDataSource = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.RemoteDataSource").msgclass + AsyncDataSource = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.AsyncDataSource").msgclass + TransportSocket = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.TransportSocket").msgclass + RuntimeFractionalPercent = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.RuntimeFractionalPercent").msgclass + ControlPlane = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.ControlPlane").msgclass + RoutingPriority = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.RoutingPriority").enummodule + RequestMethod = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.RequestMethod").enummodule + TrafficDirection = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.TrafficDirection").enummodule + end + end + end +end diff --git a/lib/grpc/envoy/api/v2/core/http_uri_pb.rb b/lib/grpc/envoy/api/v2/core/http_uri_pb.rb new file mode 100644 index 00000000..5bf935e6 --- /dev/null +++ b/lib/grpc/envoy/api/v2/core/http_uri_pb.rb @@ -0,0 +1,29 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/api/v2/core/http_uri.proto + +require 'google/protobuf' + +require 'google/protobuf/duration_pb' +require 'udpa/annotations/migrate_pb' +require 'udpa/annotations/status_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/api/v2/core/http_uri.proto", :syntax => :proto3) do + add_message "envoy.api.v2.core.HttpUri" do + optional :uri, :string, 1 + optional :timeout, :message, 3, "google.protobuf.Duration" + oneof :http_upstream_type do + optional :cluster, :string, 2 + end + end + end +end + +module Envoy + module Api + module V2 + module Core + HttpUri = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.HttpUri").msgclass + end + end + end +end diff --git a/lib/grpc/envoy/api/v2/core/socket_option_pb.rb b/lib/grpc/envoy/api/v2/core/socket_option_pb.rb new file mode 100644 index 00000000..0fe70b9a --- /dev/null +++ b/lib/grpc/envoy/api/v2/core/socket_option_pb.rb @@ -0,0 +1,37 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/api/v2/core/socket_option.proto + +require 'google/protobuf' + +require 'udpa/annotations/migrate_pb' +require 'udpa/annotations/status_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/api/v2/core/socket_option.proto", :syntax => :proto3) do + add_message "envoy.api.v2.core.SocketOption" do + optional :description, :string, 1 + optional :level, :int64, 2 + optional :name, :int64, 3 + optional :state, :enum, 6, "envoy.api.v2.core.SocketOption.SocketState" + oneof :value do + optional :int_value, :int64, 4 + optional :buf_value, :bytes, 5 + end + end + add_enum "envoy.api.v2.core.SocketOption.SocketState" do + value :STATE_PREBIND, 0 + value :STATE_BOUND, 1 + value :STATE_LISTENING, 2 + end + end +end + +module Envoy + module Api + module V2 + module Core + SocketOption = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.SocketOption").msgclass + SocketOption::SocketState = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.api.v2.core.SocketOption.SocketState").enummodule + end + end + end +end diff --git a/lib/grpc/envoy/config/core/v3/address_pb.rb b/lib/grpc/envoy/config/core/v3/address_pb.rb new file mode 100644 index 00000000..4de74157 --- /dev/null +++ b/lib/grpc/envoy/config/core/v3/address_pb.rb @@ -0,0 +1,74 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/config/core/v3/address.proto + +require 'google/protobuf' + +require 'envoy/config/core/v3/socket_option_pb' +require 'google/protobuf/wrappers_pb' +require 'udpa/annotations/status_pb' +require 'udpa/annotations/versioning_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/config/core/v3/address.proto", :syntax => :proto3) do + add_message "envoy.config.core.v3.Pipe" do + optional :path, :string, 1 + optional :mode, :uint32, 2 + end + add_message "envoy.config.core.v3.EnvoyInternalAddress" do + oneof :address_name_specifier do + optional :server_listener_name, :string, 1 + end + end + add_message "envoy.config.core.v3.SocketAddress" do + optional :protocol, :enum, 1, "envoy.config.core.v3.SocketAddress.Protocol" + optional :address, :string, 2 + optional :resolver_name, :string, 5 + optional :ipv4_compat, :bool, 6 + oneof :port_specifier do + optional :port_value, :uint32, 3 + optional :named_port, :string, 4 + end + end + add_enum "envoy.config.core.v3.SocketAddress.Protocol" do + value :TCP, 0 + value :UDP, 1 + end + add_message "envoy.config.core.v3.TcpKeepalive" do + optional :keepalive_probes, :message, 1, "google.protobuf.UInt32Value" + optional :keepalive_time, :message, 2, "google.protobuf.UInt32Value" + optional :keepalive_interval, :message, 3, "google.protobuf.UInt32Value" + end + add_message "envoy.config.core.v3.BindConfig" do + optional :source_address, :message, 1, "envoy.config.core.v3.SocketAddress" + optional :freebind, :message, 2, "google.protobuf.BoolValue" + repeated :socket_options, :message, 3, "envoy.config.core.v3.SocketOption" + end + add_message "envoy.config.core.v3.Address" do + oneof :address do + optional :socket_address, :message, 1, "envoy.config.core.v3.SocketAddress" + optional :pipe, :message, 2, "envoy.config.core.v3.Pipe" + optional :envoy_internal_address, :message, 3, "envoy.config.core.v3.EnvoyInternalAddress" + end + end + add_message "envoy.config.core.v3.CidrRange" do + optional :address_prefix, :string, 1 + optional :prefix_len, :message, 2, "google.protobuf.UInt32Value" + end + end +end + +module Envoy + module Config + module Core + module V3 + Pipe = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.Pipe").msgclass + EnvoyInternalAddress = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.EnvoyInternalAddress").msgclass + SocketAddress = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.SocketAddress").msgclass + SocketAddress::Protocol = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.SocketAddress.Protocol").enummodule + TcpKeepalive = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.TcpKeepalive").msgclass + BindConfig = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.BindConfig").msgclass + Address = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.Address").msgclass + CidrRange = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.CidrRange").msgclass + end + end + end +end diff --git a/lib/grpc/envoy/config/core/v3/backoff_pb.rb b/lib/grpc/envoy/config/core/v3/backoff_pb.rb new file mode 100644 index 00000000..7912f1c7 --- /dev/null +++ b/lib/grpc/envoy/config/core/v3/backoff_pb.rb @@ -0,0 +1,26 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/config/core/v3/backoff.proto + +require 'google/protobuf' + +require 'google/protobuf/duration_pb' +require 'udpa/annotations/status_pb' +require 'udpa/annotations/versioning_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/config/core/v3/backoff.proto", :syntax => :proto3) do + add_message "envoy.config.core.v3.BackoffStrategy" do + optional :base_interval, :message, 1, "google.protobuf.Duration" + optional :max_interval, :message, 2, "google.protobuf.Duration" + end + end +end + +module Envoy + module Config + module Core + module V3 + BackoffStrategy = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.BackoffStrategy").msgclass + end + end + end +end diff --git a/lib/grpc/envoy/config/core/v3/base_pb.rb b/lib/grpc/envoy/config/core/v3/base_pb.rb new file mode 100644 index 00000000..e02f8b83 --- /dev/null +++ b/lib/grpc/envoy/config/core/v3/base_pb.rb @@ -0,0 +1,168 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/config/core/v3/base.proto + +require 'google/protobuf' + +require 'envoy/config/core/v3/address_pb' +require 'envoy/config/core/v3/backoff_pb' +require 'envoy/config/core/v3/http_uri_pb' +require 'envoy/type/v3/percent_pb' +require 'envoy/type/v3/semantic_version_pb' +require 'google/protobuf/any_pb' +require 'google/protobuf/duration_pb' +require 'google/protobuf/struct_pb' +require 'google/protobuf/wrappers_pb' +require 'udpa/annotations/migrate_pb' +require 'udpa/annotations/status_pb' +require 'udpa/annotations/versioning_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/config/core/v3/base.proto", :syntax => :proto3) do + add_message "envoy.config.core.v3.Locality" do + optional :region, :string, 1 + optional :zone, :string, 2 + optional :sub_zone, :string, 3 + end + add_message "envoy.config.core.v3.BuildVersion" do + optional :version, :message, 1, "envoy.type.v3.SemanticVersion" + optional :metadata, :message, 2, "google.protobuf.Struct" + end + add_message "envoy.config.core.v3.Extension" do + optional :name, :string, 1 + optional :category, :string, 2 + optional :type_descriptor, :string, 3 + optional :version, :message, 4, "envoy.config.core.v3.BuildVersion" + optional :disabled, :bool, 5 + end + add_message "envoy.config.core.v3.Node" do + optional :id, :string, 1 + optional :cluster, :string, 2 + optional :metadata, :message, 3, "google.protobuf.Struct" + optional :locality, :message, 4, "envoy.config.core.v3.Locality" + optional :user_agent_name, :string, 6 + repeated :extensions, :message, 9, "envoy.config.core.v3.Extension" + repeated :client_features, :string, 10 + repeated :listening_addresses, :message, 11, "envoy.config.core.v3.Address" + oneof :user_agent_version_type do + optional :user_agent_version, :string, 7 + optional :user_agent_build_version, :message, 8, "envoy.config.core.v3.BuildVersion" + end + end + add_message "envoy.config.core.v3.Metadata" do + map :filter_metadata, :string, :message, 1, "google.protobuf.Struct" + end + add_message "envoy.config.core.v3.RuntimeUInt32" do + optional :default_value, :uint32, 2 + optional :runtime_key, :string, 3 + end + add_message "envoy.config.core.v3.RuntimePercent" do + optional :default_value, :message, 1, "envoy.type.v3.Percent" + optional :runtime_key, :string, 2 + end + add_message "envoy.config.core.v3.RuntimeDouble" do + optional :default_value, :double, 1 + optional :runtime_key, :string, 2 + end + add_message "envoy.config.core.v3.RuntimeFeatureFlag" do + optional :default_value, :message, 1, "google.protobuf.BoolValue" + optional :runtime_key, :string, 2 + end + add_message "envoy.config.core.v3.HeaderValue" do + optional :key, :string, 1 + optional :value, :string, 2 + end + add_message "envoy.config.core.v3.HeaderValueOption" do + optional :header, :message, 1, "envoy.config.core.v3.HeaderValue" + optional :append, :message, 2, "google.protobuf.BoolValue" + end + add_message "envoy.config.core.v3.HeaderMap" do + repeated :headers, :message, 1, "envoy.config.core.v3.HeaderValue" + end + add_message "envoy.config.core.v3.DataSource" do + oneof :specifier do + optional :filename, :string, 1 + optional :inline_bytes, :bytes, 2 + optional :inline_string, :string, 3 + end + end + add_message "envoy.config.core.v3.RetryPolicy" do + optional :retry_back_off, :message, 1, "envoy.config.core.v3.BackoffStrategy" + optional :num_retries, :message, 2, "google.protobuf.UInt32Value" + end + add_message "envoy.config.core.v3.RemoteDataSource" do + optional :http_uri, :message, 1, "envoy.config.core.v3.HttpUri" + optional :sha256, :string, 2 + optional :retry_policy, :message, 3, "envoy.config.core.v3.RetryPolicy" + end + add_message "envoy.config.core.v3.AsyncDataSource" do + oneof :specifier do + optional :local, :message, 1, "envoy.config.core.v3.DataSource" + optional :remote, :message, 2, "envoy.config.core.v3.RemoteDataSource" + end + end + add_message "envoy.config.core.v3.TransportSocket" do + optional :name, :string, 1 + oneof :config_type do + optional :typed_config, :message, 3, "google.protobuf.Any" + end + end + add_message "envoy.config.core.v3.RuntimeFractionalPercent" do + optional :default_value, :message, 1, "envoy.type.v3.FractionalPercent" + optional :runtime_key, :string, 2 + end + add_message "envoy.config.core.v3.ControlPlane" do + optional :identifier, :string, 1 + end + add_enum "envoy.config.core.v3.RoutingPriority" do + value :DEFAULT, 0 + value :HIGH, 1 + end + add_enum "envoy.config.core.v3.RequestMethod" do + value :METHOD_UNSPECIFIED, 0 + value :GET, 1 + value :HEAD, 2 + value :POST, 3 + value :PUT, 4 + value :DELETE, 5 + value :CONNECT, 6 + value :OPTIONS, 7 + value :TRACE, 8 + value :PATCH, 9 + end + add_enum "envoy.config.core.v3.TrafficDirection" do + value :UNSPECIFIED, 0 + value :INBOUND, 1 + value :OUTBOUND, 2 + end + end +end + +module Envoy + module Config + module Core + module V3 + Locality = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.Locality").msgclass + BuildVersion = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.BuildVersion").msgclass + Extension = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.Extension").msgclass + Node = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.Node").msgclass + Metadata = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.Metadata").msgclass + RuntimeUInt32 = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.RuntimeUInt32").msgclass + RuntimePercent = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.RuntimePercent").msgclass + RuntimeDouble = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.RuntimeDouble").msgclass + RuntimeFeatureFlag = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.RuntimeFeatureFlag").msgclass + HeaderValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.HeaderValue").msgclass + HeaderValueOption = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.HeaderValueOption").msgclass + HeaderMap = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.HeaderMap").msgclass + DataSource = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.DataSource").msgclass + RetryPolicy = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.RetryPolicy").msgclass + RemoteDataSource = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.RemoteDataSource").msgclass + AsyncDataSource = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.AsyncDataSource").msgclass + TransportSocket = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.TransportSocket").msgclass + RuntimeFractionalPercent = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.RuntimeFractionalPercent").msgclass + ControlPlane = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.ControlPlane").msgclass + RoutingPriority = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.RoutingPriority").enummodule + RequestMethod = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.RequestMethod").enummodule + TrafficDirection = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.TrafficDirection").enummodule + end + end + end +end diff --git a/lib/grpc/envoy/config/core/v3/http_uri_pb.rb b/lib/grpc/envoy/config/core/v3/http_uri_pb.rb new file mode 100644 index 00000000..5dd3495d --- /dev/null +++ b/lib/grpc/envoy/config/core/v3/http_uri_pb.rb @@ -0,0 +1,29 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/config/core/v3/http_uri.proto + +require 'google/protobuf' + +require 'google/protobuf/duration_pb' +require 'udpa/annotations/status_pb' +require 'udpa/annotations/versioning_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/config/core/v3/http_uri.proto", :syntax => :proto3) do + add_message "envoy.config.core.v3.HttpUri" do + optional :uri, :string, 1 + optional :timeout, :message, 3, "google.protobuf.Duration" + oneof :http_upstream_type do + optional :cluster, :string, 2 + end + end + end +end + +module Envoy + module Config + module Core + module V3 + HttpUri = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.HttpUri").msgclass + end + end + end +end diff --git a/lib/grpc/envoy/config/core/v3/socket_option_pb.rb b/lib/grpc/envoy/config/core/v3/socket_option_pb.rb new file mode 100644 index 00000000..27f61116 --- /dev/null +++ b/lib/grpc/envoy/config/core/v3/socket_option_pb.rb @@ -0,0 +1,37 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/config/core/v3/socket_option.proto + +require 'google/protobuf' + +require 'udpa/annotations/status_pb' +require 'udpa/annotations/versioning_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/config/core/v3/socket_option.proto", :syntax => :proto3) do + add_message "envoy.config.core.v3.SocketOption" do + optional :description, :string, 1 + optional :level, :int64, 2 + optional :name, :int64, 3 + optional :state, :enum, 6, "envoy.config.core.v3.SocketOption.SocketState" + oneof :value do + optional :int_value, :int64, 4 + optional :buf_value, :bytes, 5 + end + end + add_enum "envoy.config.core.v3.SocketOption.SocketState" do + value :STATE_PREBIND, 0 + value :STATE_BOUND, 1 + value :STATE_LISTENING, 2 + end + end +end + +module Envoy + module Config + module Core + module V3 + SocketOption = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.SocketOption").msgclass + SocketOption::SocketState = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.config.core.v3.SocketOption.SocketState").enummodule + end + end + end +end diff --git a/lib/grpc/envoy/service/auth/v2/attribute_context_pb.rb b/lib/grpc/envoy/service/auth/v2/attribute_context_pb.rb new file mode 100644 index 00000000..cb8b977c --- /dev/null +++ b/lib/grpc/envoy/service/auth/v2/attribute_context_pb.rb @@ -0,0 +1,57 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/service/auth/v2/attribute_context.proto + +require 'google/protobuf' + +require 'envoy/api/v2/core/address_pb' +require 'envoy/api/v2/core/base_pb' +require 'google/protobuf/timestamp_pb' +require 'udpa/annotations/status_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/service/auth/v2/attribute_context.proto", :syntax => :proto3) do + add_message "envoy.service.auth.v2.AttributeContext" do + optional :source, :message, 1, "envoy.service.auth.v2.AttributeContext.Peer" + optional :destination, :message, 2, "envoy.service.auth.v2.AttributeContext.Peer" + optional :request, :message, 4, "envoy.service.auth.v2.AttributeContext.Request" + map :context_extensions, :string, :string, 10 + optional :metadata_context, :message, 11, "envoy.api.v2.core.Metadata" + end + add_message "envoy.service.auth.v2.AttributeContext.Peer" do + optional :address, :message, 1, "envoy.api.v2.core.Address" + optional :service, :string, 2 + map :labels, :string, :string, 3 + optional :principal, :string, 4 + optional :certificate, :string, 5 + end + add_message "envoy.service.auth.v2.AttributeContext.Request" do + optional :time, :message, 1, "google.protobuf.Timestamp" + optional :http, :message, 2, "envoy.service.auth.v2.AttributeContext.HttpRequest" + end + add_message "envoy.service.auth.v2.AttributeContext.HttpRequest" do + optional :id, :string, 1 + optional :method, :string, 2 + map :headers, :string, :string, 3 + optional :path, :string, 4 + optional :host, :string, 5 + optional :scheme, :string, 6 + optional :query, :string, 7 + optional :fragment, :string, 8 + optional :size, :int64, 9 + optional :protocol, :string, 10 + optional :body, :string, 11 + end + end +end + +module Envoy + module Service + module Auth + module V2 + AttributeContext = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v2.AttributeContext").msgclass + AttributeContext::Peer = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v2.AttributeContext.Peer").msgclass + AttributeContext::Request = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v2.AttributeContext.Request").msgclass + AttributeContext::HttpRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v2.AttributeContext.HttpRequest").msgclass + end + end + end +end diff --git a/lib/grpc/envoy/service/auth/v2/external_auth_pb.rb b/lib/grpc/envoy/service/auth/v2/external_auth_pb.rb new file mode 100644 index 00000000..4c1cae7e --- /dev/null +++ b/lib/grpc/envoy/service/auth/v2/external_auth_pb.rb @@ -0,0 +1,45 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/service/auth/v2/external_auth.proto + +require 'google/protobuf' + +require 'envoy/api/v2/core/base_pb' +require 'envoy/service/auth/v2/attribute_context_pb' +require 'envoy/type/http_status_pb' +require 'google/rpc/status_pb' +require 'udpa/annotations/status_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/service/auth/v2/external_auth.proto", :syntax => :proto3) do + add_message "envoy.service.auth.v2.CheckRequest" do + optional :attributes, :message, 1, "envoy.service.auth.v2.AttributeContext" + end + add_message "envoy.service.auth.v2.DeniedHttpResponse" do + optional :status, :message, 1, "envoy.type.HttpStatus" + repeated :headers, :message, 2, "envoy.api.v2.core.HeaderValueOption" + optional :body, :string, 3 + end + add_message "envoy.service.auth.v2.OkHttpResponse" do + repeated :headers, :message, 2, "envoy.api.v2.core.HeaderValueOption" + end + add_message "envoy.service.auth.v2.CheckResponse" do + optional :status, :message, 1, "google.rpc.Status" + oneof :http_response do + optional :denied_response, :message, 2, "envoy.service.auth.v2.DeniedHttpResponse" + optional :ok_response, :message, 3, "envoy.service.auth.v2.OkHttpResponse" + end + end + end +end + +module Envoy + module Service + module Auth + module V2 + CheckRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v2.CheckRequest").msgclass + DeniedHttpResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v2.DeniedHttpResponse").msgclass + OkHttpResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v2.OkHttpResponse").msgclass + CheckResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v2.CheckResponse").msgclass + end + end + end +end diff --git a/lib/grpc/envoy/service/auth/v2/external_auth_services_pb.rb b/lib/grpc/envoy/service/auth/v2/external_auth_services_pb.rb new file mode 100644 index 00000000..5c8a7cd9 --- /dev/null +++ b/lib/grpc/envoy/service/auth/v2/external_auth_services_pb.rb @@ -0,0 +1,37 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# Source: envoy/service/auth/v2/external_auth.proto for package 'envoy.service.auth.v2' + +require 'grpc' +require 'envoy/service/auth/v2/external_auth_pb' + +module Envoy + module Service + module Auth + module V2 + module Authorization + # [#protodoc-title: Authorization Service ] + # + # The authorization service request messages used by external authorization :ref:`network filter + # ` and :ref:`HTTP filter `. + # + # A generic interface for performing authorization check on incoming + # requests to a networked service. + class Service + + include GRPC::GenericService + + self.marshal_class_method = :encode + self.unmarshal_class_method = :decode + self.service_name = 'envoy.service.auth.v2.Authorization' + + # Performs authorization check based on the attributes associated with the + # incoming request, and returns status `OK` or not `OK`. + rpc :Check, ::Envoy::Service::Auth::V2::CheckRequest, ::Envoy::Service::Auth::V2::CheckResponse + end + + Stub = Service.rpc_stub_class + end + end + end + end +end diff --git a/lib/grpc/envoy/service/auth/v3/attribute_context_pb.rb b/lib/grpc/envoy/service/auth/v3/attribute_context_pb.rb new file mode 100644 index 00000000..9aa7e5ca --- /dev/null +++ b/lib/grpc/envoy/service/auth/v3/attribute_context_pb.rb @@ -0,0 +1,59 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/service/auth/v3/attribute_context.proto + +require 'google/protobuf' + +require 'envoy/config/core/v3/address_pb' +require 'envoy/config/core/v3/base_pb' +require 'google/protobuf/timestamp_pb' +require 'udpa/annotations/status_pb' +require 'udpa/annotations/versioning_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/service/auth/v3/attribute_context.proto", :syntax => :proto3) do + add_message "envoy.service.auth.v3.AttributeContext" do + optional :source, :message, 1, "envoy.service.auth.v3.AttributeContext.Peer" + optional :destination, :message, 2, "envoy.service.auth.v3.AttributeContext.Peer" + optional :request, :message, 4, "envoy.service.auth.v3.AttributeContext.Request" + map :context_extensions, :string, :string, 10 + optional :metadata_context, :message, 11, "envoy.config.core.v3.Metadata" + end + add_message "envoy.service.auth.v3.AttributeContext.Peer" do + optional :address, :message, 1, "envoy.config.core.v3.Address" + optional :service, :string, 2 + map :labels, :string, :string, 3 + optional :principal, :string, 4 + optional :certificate, :string, 5 + end + add_message "envoy.service.auth.v3.AttributeContext.Request" do + optional :time, :message, 1, "google.protobuf.Timestamp" + optional :http, :message, 2, "envoy.service.auth.v3.AttributeContext.HttpRequest" + end + add_message "envoy.service.auth.v3.AttributeContext.HttpRequest" do + optional :id, :string, 1 + optional :method, :string, 2 + map :headers, :string, :string, 3 + optional :path, :string, 4 + optional :host, :string, 5 + optional :scheme, :string, 6 + optional :query, :string, 7 + optional :fragment, :string, 8 + optional :size, :int64, 9 + optional :protocol, :string, 10 + optional :body, :string, 11 + optional :raw_body, :bytes, 12 + end + end +end + +module Envoy + module Service + module Auth + module V3 + AttributeContext = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v3.AttributeContext").msgclass + AttributeContext::Peer = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v3.AttributeContext.Peer").msgclass + AttributeContext::Request = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v3.AttributeContext.Request").msgclass + AttributeContext::HttpRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v3.AttributeContext.HttpRequest").msgclass + end + end + end +end diff --git a/lib/grpc/envoy/service/auth/v3/external_auth_pb.rb b/lib/grpc/envoy/service/auth/v3/external_auth_pb.rb new file mode 100644 index 00000000..bc4eb005 --- /dev/null +++ b/lib/grpc/envoy/service/auth/v3/external_auth_pb.rb @@ -0,0 +1,50 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/service/auth/v3/external_auth.proto + +require 'google/protobuf' + +require 'envoy/config/core/v3/base_pb' +require 'envoy/service/auth/v3/attribute_context_pb' +require 'envoy/type/v3/http_status_pb' +require 'google/protobuf/struct_pb' +require 'google/rpc/status_pb' +require 'udpa/annotations/status_pb' +require 'udpa/annotations/versioning_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/service/auth/v3/external_auth.proto", :syntax => :proto3) do + add_message "envoy.service.auth.v3.CheckRequest" do + optional :attributes, :message, 1, "envoy.service.auth.v3.AttributeContext" + end + add_message "envoy.service.auth.v3.DeniedHttpResponse" do + optional :status, :message, 1, "envoy.type.v3.HttpStatus" + repeated :headers, :message, 2, "envoy.config.core.v3.HeaderValueOption" + optional :body, :string, 3 + end + add_message "envoy.service.auth.v3.OkHttpResponse" do + repeated :headers, :message, 2, "envoy.config.core.v3.HeaderValueOption" + repeated :headers_to_remove, :string, 5 + optional :dynamic_metadata, :message, 3, "google.protobuf.Struct" + end + add_message "envoy.service.auth.v3.CheckResponse" do + optional :status, :message, 1, "google.rpc.Status" + optional :dynamic_metadata, :message, 4, "google.protobuf.Struct" + oneof :http_response do + optional :denied_response, :message, 2, "envoy.service.auth.v3.DeniedHttpResponse" + optional :ok_response, :message, 3, "envoy.service.auth.v3.OkHttpResponse" + end + end + end +end + +module Envoy + module Service + module Auth + module V3 + CheckRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v3.CheckRequest").msgclass + DeniedHttpResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v3.DeniedHttpResponse").msgclass + OkHttpResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v3.OkHttpResponse").msgclass + CheckResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.service.auth.v3.CheckResponse").msgclass + end + end + end +end diff --git a/lib/grpc/envoy/service/auth/v3/external_auth_services_pb.rb b/lib/grpc/envoy/service/auth/v3/external_auth_services_pb.rb new file mode 100644 index 00000000..4088c441 --- /dev/null +++ b/lib/grpc/envoy/service/auth/v3/external_auth_services_pb.rb @@ -0,0 +1,37 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# Source: envoy/service/auth/v3/external_auth.proto for package 'envoy.service.auth.v3' + +require 'grpc' +require 'envoy/service/auth/v3/external_auth_pb' + +module Envoy + module Service + module Auth + module V3 + module Authorization + # [#protodoc-title: Authorization Service ] + # + # The authorization service request messages used by external authorization :ref:`network filter + # ` and :ref:`HTTP filter `. + # + # A generic interface for performing authorization check on incoming + # requests to a networked service. + class Service + + include GRPC::GenericService + + self.marshal_class_method = :encode + self.unmarshal_class_method = :decode + self.service_name = 'envoy.service.auth.v3.Authorization' + + # Performs authorization check based on the attributes associated with the + # incoming request, and returns status `OK` or not `OK`. + rpc :Check, ::Envoy::Service::Auth::V3::CheckRequest, ::Envoy::Service::Auth::V3::CheckResponse + end + + Stub = Service.rpc_stub_class + end + end + end + end +end diff --git a/lib/grpc/envoy/type/http_status_pb.rb b/lib/grpc/envoy/type/http_status_pb.rb new file mode 100644 index 00000000..78dbbab1 --- /dev/null +++ b/lib/grpc/envoy/type/http_status_pb.rb @@ -0,0 +1,79 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/type/http_status.proto + +require 'google/protobuf' + +require 'udpa/annotations/status_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/type/http_status.proto", :syntax => :proto3) do + add_message "envoy.type.HttpStatus" do + optional :code, :enum, 1, "envoy.type.StatusCode" + end + add_enum "envoy.type.StatusCode" do + value :Empty, 0 + value :Continue, 100 + value :OK, 200 + value :Created, 201 + value :Accepted, 202 + value :NonAuthoritativeInformation, 203 + value :NoContent, 204 + value :ResetContent, 205 + value :PartialContent, 206 + value :MultiStatus, 207 + value :AlreadyReported, 208 + value :IMUsed, 226 + value :MultipleChoices, 300 + value :MovedPermanently, 301 + value :Found, 302 + value :SeeOther, 303 + value :NotModified, 304 + value :UseProxy, 305 + value :TemporaryRedirect, 307 + value :PermanentRedirect, 308 + value :BadRequest, 400 + value :Unauthorized, 401 + value :PaymentRequired, 402 + value :Forbidden, 403 + value :NotFound, 404 + value :MethodNotAllowed, 405 + value :NotAcceptable, 406 + value :ProxyAuthenticationRequired, 407 + value :RequestTimeout, 408 + value :Conflict, 409 + value :Gone, 410 + value :LengthRequired, 411 + value :PreconditionFailed, 412 + value :PayloadTooLarge, 413 + value :URITooLong, 414 + value :UnsupportedMediaType, 415 + value :RangeNotSatisfiable, 416 + value :ExpectationFailed, 417 + value :MisdirectedRequest, 421 + value :UnprocessableEntity, 422 + value :Locked, 423 + value :FailedDependency, 424 + value :UpgradeRequired, 426 + value :PreconditionRequired, 428 + value :TooManyRequests, 429 + value :RequestHeaderFieldsTooLarge, 431 + value :InternalServerError, 500 + value :NotImplemented, 501 + value :BadGateway, 502 + value :ServiceUnavailable, 503 + value :GatewayTimeout, 504 + value :HTTPVersionNotSupported, 505 + value :VariantAlsoNegotiates, 506 + value :InsufficientStorage, 507 + value :LoopDetected, 508 + value :NotExtended, 510 + value :NetworkAuthenticationRequired, 511 + end + end +end + +module Envoy + module Type + HttpStatus = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.type.HttpStatus").msgclass + StatusCode = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.type.StatusCode").enummodule + end +end diff --git a/lib/grpc/envoy/type/percent_pb.rb b/lib/grpc/envoy/type/percent_pb.rb new file mode 100644 index 00000000..22118251 --- /dev/null +++ b/lib/grpc/envoy/type/percent_pb.rb @@ -0,0 +1,30 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/type/percent.proto + +require 'google/protobuf' + +require 'udpa/annotations/status_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/type/percent.proto", :syntax => :proto3) do + add_message "envoy.type.Percent" do + optional :value, :double, 1 + end + add_message "envoy.type.FractionalPercent" do + optional :numerator, :uint32, 1 + optional :denominator, :enum, 2, "envoy.type.FractionalPercent.DenominatorType" + end + add_enum "envoy.type.FractionalPercent.DenominatorType" do + value :HUNDRED, 0 + value :TEN_THOUSAND, 1 + value :MILLION, 2 + end + end +end + +module Envoy + module Type + Percent = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.type.Percent").msgclass + FractionalPercent = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.type.FractionalPercent").msgclass + FractionalPercent::DenominatorType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.type.FractionalPercent.DenominatorType").enummodule + end +end diff --git a/lib/grpc/envoy/type/semantic_version_pb.rb b/lib/grpc/envoy/type/semantic_version_pb.rb new file mode 100644 index 00000000..75f1c68e --- /dev/null +++ b/lib/grpc/envoy/type/semantic_version_pb.rb @@ -0,0 +1,21 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/type/semantic_version.proto + +require 'google/protobuf' + +require 'udpa/annotations/status_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/type/semantic_version.proto", :syntax => :proto3) do + add_message "envoy.type.SemanticVersion" do + optional :major_number, :uint32, 1 + optional :minor_number, :uint32, 2 + optional :patch, :uint32, 3 + end + end +end + +module Envoy + module Type + SemanticVersion = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.type.SemanticVersion").msgclass + end +end diff --git a/lib/grpc/envoy/type/v3/http_status_pb.rb b/lib/grpc/envoy/type/v3/http_status_pb.rb new file mode 100644 index 00000000..b51153e7 --- /dev/null +++ b/lib/grpc/envoy/type/v3/http_status_pb.rb @@ -0,0 +1,82 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/type/v3/http_status.proto + +require 'google/protobuf' + +require 'udpa/annotations/status_pb' +require 'udpa/annotations/versioning_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/type/v3/http_status.proto", :syntax => :proto3) do + add_message "envoy.type.v3.HttpStatus" do + optional :code, :enum, 1, "envoy.type.v3.StatusCode" + end + add_enum "envoy.type.v3.StatusCode" do + value :Empty, 0 + value :Continue, 100 + value :OK, 200 + value :Created, 201 + value :Accepted, 202 + value :NonAuthoritativeInformation, 203 + value :NoContent, 204 + value :ResetContent, 205 + value :PartialContent, 206 + value :MultiStatus, 207 + value :AlreadyReported, 208 + value :IMUsed, 226 + value :MultipleChoices, 300 + value :MovedPermanently, 301 + value :Found, 302 + value :SeeOther, 303 + value :NotModified, 304 + value :UseProxy, 305 + value :TemporaryRedirect, 307 + value :PermanentRedirect, 308 + value :BadRequest, 400 + value :Unauthorized, 401 + value :PaymentRequired, 402 + value :Forbidden, 403 + value :NotFound, 404 + value :MethodNotAllowed, 405 + value :NotAcceptable, 406 + value :ProxyAuthenticationRequired, 407 + value :RequestTimeout, 408 + value :Conflict, 409 + value :Gone, 410 + value :LengthRequired, 411 + value :PreconditionFailed, 412 + value :PayloadTooLarge, 413 + value :URITooLong, 414 + value :UnsupportedMediaType, 415 + value :RangeNotSatisfiable, 416 + value :ExpectationFailed, 417 + value :MisdirectedRequest, 421 + value :UnprocessableEntity, 422 + value :Locked, 423 + value :FailedDependency, 424 + value :UpgradeRequired, 426 + value :PreconditionRequired, 428 + value :TooManyRequests, 429 + value :RequestHeaderFieldsTooLarge, 431 + value :InternalServerError, 500 + value :NotImplemented, 501 + value :BadGateway, 502 + value :ServiceUnavailable, 503 + value :GatewayTimeout, 504 + value :HTTPVersionNotSupported, 505 + value :VariantAlsoNegotiates, 506 + value :InsufficientStorage, 507 + value :LoopDetected, 508 + value :NotExtended, 510 + value :NetworkAuthenticationRequired, 511 + end + end +end + +module Envoy + module Type + module V3 + HttpStatus = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.type.v3.HttpStatus").msgclass + StatusCode = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.type.v3.StatusCode").enummodule + end + end +end diff --git a/lib/grpc/envoy/type/v3/percent_pb.rb b/lib/grpc/envoy/type/v3/percent_pb.rb new file mode 100644 index 00000000..f1bfd368 --- /dev/null +++ b/lib/grpc/envoy/type/v3/percent_pb.rb @@ -0,0 +1,33 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/type/v3/percent.proto + +require 'google/protobuf' + +require 'udpa/annotations/status_pb' +require 'udpa/annotations/versioning_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/type/v3/percent.proto", :syntax => :proto3) do + add_message "envoy.type.v3.Percent" do + optional :value, :double, 1 + end + add_message "envoy.type.v3.FractionalPercent" do + optional :numerator, :uint32, 1 + optional :denominator, :enum, 2, "envoy.type.v3.FractionalPercent.DenominatorType" + end + add_enum "envoy.type.v3.FractionalPercent.DenominatorType" do + value :HUNDRED, 0 + value :TEN_THOUSAND, 1 + value :MILLION, 2 + end + end +end + +module Envoy + module Type + module V3 + Percent = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.type.v3.Percent").msgclass + FractionalPercent = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.type.v3.FractionalPercent").msgclass + FractionalPercent::DenominatorType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.type.v3.FractionalPercent.DenominatorType").enummodule + end + end +end diff --git a/lib/grpc/envoy/type/v3/semantic_version_pb.rb b/lib/grpc/envoy/type/v3/semantic_version_pb.rb new file mode 100644 index 00000000..5f8260ea --- /dev/null +++ b/lib/grpc/envoy/type/v3/semantic_version_pb.rb @@ -0,0 +1,24 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: envoy/type/v3/semantic_version.proto + +require 'google/protobuf' + +require 'udpa/annotations/status_pb' +require 'udpa/annotations/versioning_pb' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("envoy/type/v3/semantic_version.proto", :syntax => :proto3) do + add_message "envoy.type.v3.SemanticVersion" do + optional :major_number, :uint32, 1 + optional :minor_number, :uint32, 2 + optional :patch, :uint32, 3 + end + end +end + +module Envoy + module Type + module V3 + SemanticVersion = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("envoy.type.v3.SemanticVersion").msgclass + end + end +end diff --git a/lib/grpc/udpa/annotations/migrate_pb.rb b/lib/grpc/udpa/annotations/migrate_pb.rb new file mode 100644 index 00000000..d1157ad3 --- /dev/null +++ b/lib/grpc/udpa/annotations/migrate_pb.rb @@ -0,0 +1,27 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: udpa/annotations/migrate.proto + +require 'google/protobuf' + +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("udpa/annotations/migrate.proto", :syntax => :proto3) do + add_message "udpa.annotations.MigrateAnnotation" do + optional :rename, :string, 1 + end + add_message "udpa.annotations.FieldMigrateAnnotation" do + optional :rename, :string, 1 + optional :oneof_promotion, :string, 2 + end + add_message "udpa.annotations.FileMigrateAnnotation" do + optional :move_to_package, :string, 2 + end + end +end + +module Udpa + module Annotations + MigrateAnnotation = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("udpa.annotations.MigrateAnnotation").msgclass + FieldMigrateAnnotation = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("udpa.annotations.FieldMigrateAnnotation").msgclass + FileMigrateAnnotation = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("udpa.annotations.FileMigrateAnnotation").msgclass + end +end diff --git a/lib/grpc/udpa/annotations/status_pb.rb b/lib/grpc/udpa/annotations/status_pb.rb new file mode 100644 index 00000000..21882c41 --- /dev/null +++ b/lib/grpc/udpa/annotations/status_pb.rb @@ -0,0 +1,26 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: udpa/annotations/status.proto + +require 'google/protobuf' + +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("udpa/annotations/status.proto", :syntax => :proto3) do + add_message "udpa.annotations.StatusAnnotation" do + optional :work_in_progress, :bool, 1 + optional :package_version_status, :enum, 2, "udpa.annotations.PackageVersionStatus" + end + add_enum "udpa.annotations.PackageVersionStatus" do + value :UNKNOWN, 0 + value :FROZEN, 1 + value :ACTIVE, 2 + value :NEXT_MAJOR_VERSION_CANDIDATE, 3 + end + end +end + +module Udpa + module Annotations + StatusAnnotation = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("udpa.annotations.StatusAnnotation").msgclass + PackageVersionStatus = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("udpa.annotations.PackageVersionStatus").enummodule + end +end diff --git a/lib/grpc/udpa/annotations/versioning_pb.rb b/lib/grpc/udpa/annotations/versioning_pb.rb new file mode 100644 index 00000000..a782d411 --- /dev/null +++ b/lib/grpc/udpa/annotations/versioning_pb.rb @@ -0,0 +1,18 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: udpa/annotations/versioning.proto + +require 'google/protobuf' + +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("udpa/annotations/versioning.proto", :syntax => :proto3) do + add_message "udpa.annotations.VersioningAnnotation" do + optional :previous_message_type, :string, 1 + end + end +end + +module Udpa + module Annotations + VersioningAnnotation = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("udpa.annotations.VersioningAnnotation").msgclass + end +end diff --git a/pocs/README.md b/pocs/README.md new file mode 100644 index 00000000..8147b62c --- /dev/null +++ b/pocs/README.md @@ -0,0 +1,22 @@ +# Proofs of Concept + +These are other Proof-of-Concept (PoCs) implementations tried within the scope of 3scale AuthN/AuthZ initiative. + +### Hosted PoCs + +**Keycloak Ruby Adapter** | [ruby-keycloak-authz]
+Sample Ruby On Rails app with an OIDC adapter for identity verification and authorization with Keycloak/Red Hat SSO. + +**Envoy ext_authz + OPA** | [envoy-opa-authz]
+Authentication (OIDC) and authorization with OPA, invoked directly with Envoy ext_authz filter. + +### External PoCs + +**Ostia external authorization** | [https://github.com/3scale/ostia/pull/76]
+The embryo of Authorino as proposed for the 3scale Ostia architecture. + +**Portafly + Keycloak** | [https://github.com/didierofrivia/portafly]
+Demo: using Keycloak to authenticate and authorize requests between a SPA and a node.js resource server. + +**Envoy JWT plugin** | [https://github.com/3scale/gateway-ng-controller/pull/28]
+Envoy proxy extension to implement plug-in OIDC-based authentication with JSON Web Tokens (JWT). diff --git a/pocs/opa/README.md b/pocs/envoy-opa-authz/README.md similarity index 100% rename from pocs/opa/README.md rename to pocs/envoy-opa-authz/README.md diff --git a/pocs/opa/docker-compose.yaml b/pocs/envoy-opa-authz/docker-compose.yaml similarity index 100% rename from pocs/opa/docker-compose.yaml rename to pocs/envoy-opa-authz/docker-compose.yaml diff --git a/pocs/opa/front-envoy/Dockerfile b/pocs/envoy-opa-authz/front-envoy/Dockerfile similarity index 100% rename from pocs/opa/front-envoy/Dockerfile rename to pocs/envoy-opa-authz/front-envoy/Dockerfile diff --git a/pocs/opa/front-envoy/config.yaml b/pocs/envoy-opa-authz/front-envoy/config.yaml similarity index 100% rename from pocs/opa/front-envoy/config.yaml rename to pocs/envoy-opa-authz/front-envoy/config.yaml diff --git a/pocs/opa/front-envoy/run_envoy.sh b/pocs/envoy-opa-authz/front-envoy/run_envoy.sh similarity index 100% rename from pocs/opa/front-envoy/run_envoy.sh rename to pocs/envoy-opa-authz/front-envoy/run_envoy.sh diff --git a/pocs/opa/opa-service/jwks.json b/pocs/envoy-opa-authz/opa-service/jwks.json similarity index 100% rename from pocs/opa/opa-service/jwks.json rename to pocs/envoy-opa-authz/opa-service/jwks.json diff --git a/pocs/opa/opa-service/policy.rego b/pocs/envoy-opa-authz/opa-service/policy.rego similarity index 100% rename from pocs/opa/opa-service/policy.rego rename to pocs/envoy-opa-authz/opa-service/policy.rego diff --git a/pocs/opa/upstream-service/Dockerfile b/pocs/envoy-opa-authz/upstream-service/Dockerfile similarity index 100% rename from pocs/opa/upstream-service/Dockerfile rename to pocs/envoy-opa-authz/upstream-service/Dockerfile diff --git a/pocs/opa/upstream-service/server.py b/pocs/envoy-opa-authz/upstream-service/server.py similarity index 100% rename from pocs/opa/upstream-service/server.py rename to pocs/envoy-opa-authz/upstream-service/server.py diff --git a/pocs/keycloak-ruby/tmp/pids/.keep b/pocs/keycloak-ruby/tmp/pids/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/pocs/keycloak-ruby/vendor/.keep b/pocs/keycloak-ruby/vendor/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/pocs/keycloak-ruby/.browserslistrc b/pocs/ruby-keycloak-authz/.browserslistrc similarity index 100% rename from pocs/keycloak-ruby/.browserslistrc rename to pocs/ruby-keycloak-authz/.browserslistrc diff --git a/pocs/keycloak-ruby/.gitignore b/pocs/ruby-keycloak-authz/.gitignore similarity index 100% rename from pocs/keycloak-ruby/.gitignore rename to pocs/ruby-keycloak-authz/.gitignore diff --git a/pocs/keycloak-ruby/Gemfile b/pocs/ruby-keycloak-authz/Gemfile similarity index 100% rename from pocs/keycloak-ruby/Gemfile rename to pocs/ruby-keycloak-authz/Gemfile diff --git a/pocs/keycloak-ruby/Gemfile.lock b/pocs/ruby-keycloak-authz/Gemfile.lock similarity index 100% rename from pocs/keycloak-ruby/Gemfile.lock rename to pocs/ruby-keycloak-authz/Gemfile.lock diff --git a/pocs/keycloak-ruby/README.md b/pocs/ruby-keycloak-authz/README.md similarity index 100% rename from pocs/keycloak-ruby/README.md rename to pocs/ruby-keycloak-authz/README.md diff --git a/pocs/keycloak-ruby/Rakefile b/pocs/ruby-keycloak-authz/Rakefile similarity index 100% rename from pocs/keycloak-ruby/Rakefile rename to pocs/ruby-keycloak-authz/Rakefile diff --git a/pocs/keycloak-ruby/app/assets/config/manifest.js b/pocs/ruby-keycloak-authz/app/assets/config/manifest.js similarity index 100% rename from pocs/keycloak-ruby/app/assets/config/manifest.js rename to pocs/ruby-keycloak-authz/app/assets/config/manifest.js diff --git a/pocs/keycloak-ruby/app/assets/images/.keep b/pocs/ruby-keycloak-authz/app/assets/images/.keep similarity index 100% rename from pocs/keycloak-ruby/app/assets/images/.keep rename to pocs/ruby-keycloak-authz/app/assets/images/.keep diff --git a/pocs/keycloak-ruby/app/assets/stylesheets/application.css b/pocs/ruby-keycloak-authz/app/assets/stylesheets/application.css similarity index 100% rename from pocs/keycloak-ruby/app/assets/stylesheets/application.css rename to pocs/ruby-keycloak-authz/app/assets/stylesheets/application.css diff --git a/pocs/keycloak-ruby/app/controllers/api/services_controller.rb b/pocs/ruby-keycloak-authz/app/controllers/api/services_controller.rb similarity index 100% rename from pocs/keycloak-ruby/app/controllers/api/services_controller.rb rename to pocs/ruby-keycloak-authz/app/controllers/api/services_controller.rb diff --git a/pocs/keycloak-ruby/app/controllers/application_controller.rb b/pocs/ruby-keycloak-authz/app/controllers/application_controller.rb similarity index 100% rename from pocs/keycloak-ruby/app/controllers/application_controller.rb rename to pocs/ruby-keycloak-authz/app/controllers/application_controller.rb diff --git a/pocs/keycloak-ruby/app/controllers/concerns/.keep b/pocs/ruby-keycloak-authz/app/controllers/concerns/.keep similarity index 100% rename from pocs/keycloak-ruby/app/controllers/concerns/.keep rename to pocs/ruby-keycloak-authz/app/controllers/concerns/.keep diff --git a/pocs/keycloak-ruby/app/controllers/concerns/authentication.rb b/pocs/ruby-keycloak-authz/app/controllers/concerns/authentication.rb similarity index 100% rename from pocs/keycloak-ruby/app/controllers/concerns/authentication.rb rename to pocs/ruby-keycloak-authz/app/controllers/concerns/authentication.rb diff --git a/pocs/keycloak-ruby/app/controllers/concerns/authorization.rb b/pocs/ruby-keycloak-authz/app/controllers/concerns/authorization.rb similarity index 100% rename from pocs/keycloak-ruby/app/controllers/concerns/authorization.rb rename to pocs/ruby-keycloak-authz/app/controllers/concerns/authorization.rb diff --git a/pocs/keycloak-ruby/app/controllers/concerns/session_token.rb b/pocs/ruby-keycloak-authz/app/controllers/concerns/session_token.rb similarity index 100% rename from pocs/keycloak-ruby/app/controllers/concerns/session_token.rb rename to pocs/ruby-keycloak-authz/app/controllers/concerns/session_token.rb diff --git a/pocs/keycloak-ruby/app/controllers/sessions_controller.rb b/pocs/ruby-keycloak-authz/app/controllers/sessions_controller.rb similarity index 100% rename from pocs/keycloak-ruby/app/controllers/sessions_controller.rb rename to pocs/ruby-keycloak-authz/app/controllers/sessions_controller.rb diff --git a/pocs/keycloak-ruby/app/helpers/application_helper.rb b/pocs/ruby-keycloak-authz/app/helpers/application_helper.rb similarity index 100% rename from pocs/keycloak-ruby/app/helpers/application_helper.rb rename to pocs/ruby-keycloak-authz/app/helpers/application_helper.rb diff --git a/pocs/keycloak-ruby/app/javascript/packs/application.js b/pocs/ruby-keycloak-authz/app/javascript/packs/application.js similarity index 100% rename from pocs/keycloak-ruby/app/javascript/packs/application.js rename to pocs/ruby-keycloak-authz/app/javascript/packs/application.js diff --git a/pocs/keycloak-ruby/app/jobs/application_job.rb b/pocs/ruby-keycloak-authz/app/jobs/application_job.rb similarity index 100% rename from pocs/keycloak-ruby/app/jobs/application_job.rb rename to pocs/ruby-keycloak-authz/app/jobs/application_job.rb diff --git a/pocs/keycloak-ruby/app/models/concerns/.keep b/pocs/ruby-keycloak-authz/app/models/concerns/.keep similarity index 100% rename from pocs/keycloak-ruby/app/models/concerns/.keep rename to pocs/ruby-keycloak-authz/app/models/concerns/.keep diff --git a/pocs/keycloak-ruby/app/views/api/services/index.html.erb b/pocs/ruby-keycloak-authz/app/views/api/services/index.html.erb similarity index 100% rename from pocs/keycloak-ruby/app/views/api/services/index.html.erb rename to pocs/ruby-keycloak-authz/app/views/api/services/index.html.erb diff --git a/pocs/keycloak-ruby/app/views/api/services/new.html.erb b/pocs/ruby-keycloak-authz/app/views/api/services/new.html.erb similarity index 100% rename from pocs/keycloak-ruby/app/views/api/services/new.html.erb rename to pocs/ruby-keycloak-authz/app/views/api/services/new.html.erb diff --git a/pocs/keycloak-ruby/app/views/api/services/show.html.erb b/pocs/ruby-keycloak-authz/app/views/api/services/show.html.erb similarity index 100% rename from pocs/keycloak-ruby/app/views/api/services/show.html.erb rename to pocs/ruby-keycloak-authz/app/views/api/services/show.html.erb diff --git a/pocs/keycloak-ruby/app/views/layouts/application.html.erb b/pocs/ruby-keycloak-authz/app/views/layouts/application.html.erb similarity index 100% rename from pocs/keycloak-ruby/app/views/layouts/application.html.erb rename to pocs/ruby-keycloak-authz/app/views/layouts/application.html.erb diff --git a/pocs/keycloak-ruby/app/views/sessions/show.html.erb b/pocs/ruby-keycloak-authz/app/views/sessions/show.html.erb similarity index 100% rename from pocs/keycloak-ruby/app/views/sessions/show.html.erb rename to pocs/ruby-keycloak-authz/app/views/sessions/show.html.erb diff --git a/pocs/keycloak-ruby/babel.config.js b/pocs/ruby-keycloak-authz/babel.config.js similarity index 100% rename from pocs/keycloak-ruby/babel.config.js rename to pocs/ruby-keycloak-authz/babel.config.js diff --git a/pocs/keycloak-ruby/bin/bundle b/pocs/ruby-keycloak-authz/bin/bundle similarity index 100% rename from pocs/keycloak-ruby/bin/bundle rename to pocs/ruby-keycloak-authz/bin/bundle diff --git a/pocs/keycloak-ruby/bin/rails b/pocs/ruby-keycloak-authz/bin/rails similarity index 100% rename from pocs/keycloak-ruby/bin/rails rename to pocs/ruby-keycloak-authz/bin/rails diff --git a/pocs/keycloak-ruby/bin/rake b/pocs/ruby-keycloak-authz/bin/rake similarity index 100% rename from pocs/keycloak-ruby/bin/rake rename to pocs/ruby-keycloak-authz/bin/rake diff --git a/pocs/keycloak-ruby/bin/setup b/pocs/ruby-keycloak-authz/bin/setup similarity index 100% rename from pocs/keycloak-ruby/bin/setup rename to pocs/ruby-keycloak-authz/bin/setup diff --git a/pocs/keycloak-ruby/bin/spring b/pocs/ruby-keycloak-authz/bin/spring similarity index 100% rename from pocs/keycloak-ruby/bin/spring rename to pocs/ruby-keycloak-authz/bin/spring diff --git a/pocs/keycloak-ruby/bin/webpack b/pocs/ruby-keycloak-authz/bin/webpack similarity index 100% rename from pocs/keycloak-ruby/bin/webpack rename to pocs/ruby-keycloak-authz/bin/webpack diff --git a/pocs/keycloak-ruby/bin/webpack-dev-server b/pocs/ruby-keycloak-authz/bin/webpack-dev-server similarity index 100% rename from pocs/keycloak-ruby/bin/webpack-dev-server rename to pocs/ruby-keycloak-authz/bin/webpack-dev-server diff --git a/pocs/keycloak-ruby/bin/yarn b/pocs/ruby-keycloak-authz/bin/yarn similarity index 100% rename from pocs/keycloak-ruby/bin/yarn rename to pocs/ruby-keycloak-authz/bin/yarn diff --git a/pocs/keycloak-ruby/config.ru b/pocs/ruby-keycloak-authz/config.ru similarity index 100% rename from pocs/keycloak-ruby/config.ru rename to pocs/ruby-keycloak-authz/config.ru diff --git a/pocs/keycloak-ruby/config/application.rb b/pocs/ruby-keycloak-authz/config/application.rb similarity index 100% rename from pocs/keycloak-ruby/config/application.rb rename to pocs/ruby-keycloak-authz/config/application.rb diff --git a/pocs/keycloak-ruby/config/boot.rb b/pocs/ruby-keycloak-authz/config/boot.rb similarity index 100% rename from pocs/keycloak-ruby/config/boot.rb rename to pocs/ruby-keycloak-authz/config/boot.rb diff --git a/pocs/keycloak-ruby/config/credentials.yml.enc b/pocs/ruby-keycloak-authz/config/credentials.yml.enc similarity index 100% rename from pocs/keycloak-ruby/config/credentials.yml.enc rename to pocs/ruby-keycloak-authz/config/credentials.yml.enc diff --git a/pocs/keycloak-ruby/config/environment.rb b/pocs/ruby-keycloak-authz/config/environment.rb similarity index 100% rename from pocs/keycloak-ruby/config/environment.rb rename to pocs/ruby-keycloak-authz/config/environment.rb diff --git a/pocs/keycloak-ruby/config/environments/development.rb b/pocs/ruby-keycloak-authz/config/environments/development.rb similarity index 100% rename from pocs/keycloak-ruby/config/environments/development.rb rename to pocs/ruby-keycloak-authz/config/environments/development.rb diff --git a/pocs/keycloak-ruby/config/environments/production.rb b/pocs/ruby-keycloak-authz/config/environments/production.rb similarity index 100% rename from pocs/keycloak-ruby/config/environments/production.rb rename to pocs/ruby-keycloak-authz/config/environments/production.rb diff --git a/pocs/keycloak-ruby/config/environments/test.rb b/pocs/ruby-keycloak-authz/config/environments/test.rb similarity index 100% rename from pocs/keycloak-ruby/config/environments/test.rb rename to pocs/ruby-keycloak-authz/config/environments/test.rb diff --git a/pocs/keycloak-ruby/config/initializers/application_controller_renderer.rb b/pocs/ruby-keycloak-authz/config/initializers/application_controller_renderer.rb similarity index 100% rename from pocs/keycloak-ruby/config/initializers/application_controller_renderer.rb rename to pocs/ruby-keycloak-authz/config/initializers/application_controller_renderer.rb diff --git a/pocs/keycloak-ruby/config/initializers/assets.rb b/pocs/ruby-keycloak-authz/config/initializers/assets.rb similarity index 100% rename from pocs/keycloak-ruby/config/initializers/assets.rb rename to pocs/ruby-keycloak-authz/config/initializers/assets.rb diff --git a/pocs/keycloak-ruby/config/initializers/backtrace_silencers.rb b/pocs/ruby-keycloak-authz/config/initializers/backtrace_silencers.rb similarity index 100% rename from pocs/keycloak-ruby/config/initializers/backtrace_silencers.rb rename to pocs/ruby-keycloak-authz/config/initializers/backtrace_silencers.rb diff --git a/pocs/keycloak-ruby/config/initializers/content_security_policy.rb b/pocs/ruby-keycloak-authz/config/initializers/content_security_policy.rb similarity index 100% rename from pocs/keycloak-ruby/config/initializers/content_security_policy.rb rename to pocs/ruby-keycloak-authz/config/initializers/content_security_policy.rb diff --git a/pocs/keycloak-ruby/config/initializers/cookies_serializer.rb b/pocs/ruby-keycloak-authz/config/initializers/cookies_serializer.rb similarity index 100% rename from pocs/keycloak-ruby/config/initializers/cookies_serializer.rb rename to pocs/ruby-keycloak-authz/config/initializers/cookies_serializer.rb diff --git a/pocs/keycloak-ruby/config/initializers/filter_parameter_logging.rb b/pocs/ruby-keycloak-authz/config/initializers/filter_parameter_logging.rb similarity index 100% rename from pocs/keycloak-ruby/config/initializers/filter_parameter_logging.rb rename to pocs/ruby-keycloak-authz/config/initializers/filter_parameter_logging.rb diff --git a/pocs/keycloak-ruby/config/initializers/inflections.rb b/pocs/ruby-keycloak-authz/config/initializers/inflections.rb similarity index 100% rename from pocs/keycloak-ruby/config/initializers/inflections.rb rename to pocs/ruby-keycloak-authz/config/initializers/inflections.rb diff --git a/pocs/keycloak-ruby/config/initializers/mime_types.rb b/pocs/ruby-keycloak-authz/config/initializers/mime_types.rb similarity index 100% rename from pocs/keycloak-ruby/config/initializers/mime_types.rb rename to pocs/ruby-keycloak-authz/config/initializers/mime_types.rb diff --git a/pocs/keycloak-ruby/config/initializers/wrap_parameters.rb b/pocs/ruby-keycloak-authz/config/initializers/wrap_parameters.rb similarity index 100% rename from pocs/keycloak-ruby/config/initializers/wrap_parameters.rb rename to pocs/ruby-keycloak-authz/config/initializers/wrap_parameters.rb diff --git a/pocs/keycloak-ruby/config/keycloak.yml b/pocs/ruby-keycloak-authz/config/keycloak.yml similarity index 100% rename from pocs/keycloak-ruby/config/keycloak.yml rename to pocs/ruby-keycloak-authz/config/keycloak.yml diff --git a/pocs/keycloak-ruby/config/locales/en.yml b/pocs/ruby-keycloak-authz/config/locales/en.yml similarity index 100% rename from pocs/keycloak-ruby/config/locales/en.yml rename to pocs/ruby-keycloak-authz/config/locales/en.yml diff --git a/pocs/keycloak-ruby/config/puma.rb b/pocs/ruby-keycloak-authz/config/puma.rb similarity index 100% rename from pocs/keycloak-ruby/config/puma.rb rename to pocs/ruby-keycloak-authz/config/puma.rb diff --git a/pocs/keycloak-ruby/config/routes.rb b/pocs/ruby-keycloak-authz/config/routes.rb similarity index 100% rename from pocs/keycloak-ruby/config/routes.rb rename to pocs/ruby-keycloak-authz/config/routes.rb diff --git a/pocs/keycloak-ruby/config/spring.rb b/pocs/ruby-keycloak-authz/config/spring.rb similarity index 100% rename from pocs/keycloak-ruby/config/spring.rb rename to pocs/ruby-keycloak-authz/config/spring.rb diff --git a/pocs/keycloak-ruby/config/webpack/development.js b/pocs/ruby-keycloak-authz/config/webpack/development.js similarity index 100% rename from pocs/keycloak-ruby/config/webpack/development.js rename to pocs/ruby-keycloak-authz/config/webpack/development.js diff --git a/pocs/keycloak-ruby/config/webpack/environment.js b/pocs/ruby-keycloak-authz/config/webpack/environment.js similarity index 100% rename from pocs/keycloak-ruby/config/webpack/environment.js rename to pocs/ruby-keycloak-authz/config/webpack/environment.js diff --git a/pocs/keycloak-ruby/config/webpack/production.js b/pocs/ruby-keycloak-authz/config/webpack/production.js similarity index 100% rename from pocs/keycloak-ruby/config/webpack/production.js rename to pocs/ruby-keycloak-authz/config/webpack/production.js diff --git a/pocs/keycloak-ruby/config/webpack/test.js b/pocs/ruby-keycloak-authz/config/webpack/test.js similarity index 100% rename from pocs/keycloak-ruby/config/webpack/test.js rename to pocs/ruby-keycloak-authz/config/webpack/test.js diff --git a/pocs/keycloak-ruby/config/webpacker.yml b/pocs/ruby-keycloak-authz/config/webpacker.yml similarity index 100% rename from pocs/keycloak-ruby/config/webpacker.yml rename to pocs/ruby-keycloak-authz/config/webpacker.yml diff --git a/pocs/keycloak-ruby/lib/assets/.keep b/pocs/ruby-keycloak-authz/lib/assets/.keep similarity index 100% rename from pocs/keycloak-ruby/lib/assets/.keep rename to pocs/ruby-keycloak-authz/lib/assets/.keep diff --git a/pocs/keycloak-ruby/lib/keycloak_adapter.rb b/pocs/ruby-keycloak-authz/lib/keycloak_adapter.rb similarity index 100% rename from pocs/keycloak-ruby/lib/keycloak_adapter.rb rename to pocs/ruby-keycloak-authz/lib/keycloak_adapter.rb diff --git a/pocs/keycloak-ruby/lib/keycloak_adapter/adapter.rb b/pocs/ruby-keycloak-authz/lib/keycloak_adapter/adapter.rb similarity index 100% rename from pocs/keycloak-ruby/lib/keycloak_adapter/adapter.rb rename to pocs/ruby-keycloak-authz/lib/keycloak_adapter/adapter.rb diff --git a/pocs/keycloak-ruby/lib/keycloak_adapter/oidc_configuration.rb b/pocs/ruby-keycloak-authz/lib/keycloak_adapter/oidc_configuration.rb similarity index 100% rename from pocs/keycloak-ruby/lib/keycloak_adapter/oidc_configuration.rb rename to pocs/ruby-keycloak-authz/lib/keycloak_adapter/oidc_configuration.rb diff --git a/pocs/keycloak-ruby/lib/keycloak_adapter/parsed_jwt.rb b/pocs/ruby-keycloak-authz/lib/keycloak_adapter/parsed_jwt.rb similarity index 100% rename from pocs/keycloak-ruby/lib/keycloak_adapter/parsed_jwt.rb rename to pocs/ruby-keycloak-authz/lib/keycloak_adapter/parsed_jwt.rb diff --git a/pocs/keycloak-ruby/lib/keycloak_adapter/redirect_uri.rb b/pocs/ruby-keycloak-authz/lib/keycloak_adapter/redirect_uri.rb similarity index 100% rename from pocs/keycloak-ruby/lib/keycloak_adapter/redirect_uri.rb rename to pocs/ruby-keycloak-authz/lib/keycloak_adapter/redirect_uri.rb diff --git a/pocs/keycloak-ruby/lib/tasks/.keep b/pocs/ruby-keycloak-authz/lib/tasks/.keep similarity index 100% rename from pocs/keycloak-ruby/lib/tasks/.keep rename to pocs/ruby-keycloak-authz/lib/tasks/.keep diff --git a/pocs/keycloak-ruby/log/.keep b/pocs/ruby-keycloak-authz/log/.keep similarity index 100% rename from pocs/keycloak-ruby/log/.keep rename to pocs/ruby-keycloak-authz/log/.keep diff --git a/pocs/keycloak-ruby/package.json b/pocs/ruby-keycloak-authz/package.json similarity index 100% rename from pocs/keycloak-ruby/package.json rename to pocs/ruby-keycloak-authz/package.json diff --git a/pocs/keycloak-ruby/postcss.config.js b/pocs/ruby-keycloak-authz/postcss.config.js similarity index 100% rename from pocs/keycloak-ruby/postcss.config.js rename to pocs/ruby-keycloak-authz/postcss.config.js diff --git a/pocs/keycloak-ruby/public/403.html b/pocs/ruby-keycloak-authz/public/403.html similarity index 100% rename from pocs/keycloak-ruby/public/403.html rename to pocs/ruby-keycloak-authz/public/403.html diff --git a/pocs/keycloak-ruby/public/404.html b/pocs/ruby-keycloak-authz/public/404.html similarity index 100% rename from pocs/keycloak-ruby/public/404.html rename to pocs/ruby-keycloak-authz/public/404.html diff --git a/pocs/keycloak-ruby/public/422.html b/pocs/ruby-keycloak-authz/public/422.html similarity index 100% rename from pocs/keycloak-ruby/public/422.html rename to pocs/ruby-keycloak-authz/public/422.html diff --git a/pocs/keycloak-ruby/public/500.html b/pocs/ruby-keycloak-authz/public/500.html similarity index 100% rename from pocs/keycloak-ruby/public/500.html rename to pocs/ruby-keycloak-authz/public/500.html diff --git a/pocs/keycloak-ruby/public/apple-touch-icon-precomposed.png b/pocs/ruby-keycloak-authz/public/apple-touch-icon-precomposed.png similarity index 100% rename from pocs/keycloak-ruby/public/apple-touch-icon-precomposed.png rename to pocs/ruby-keycloak-authz/public/apple-touch-icon-precomposed.png diff --git a/pocs/keycloak-ruby/public/apple-touch-icon.png b/pocs/ruby-keycloak-authz/public/apple-touch-icon.png similarity index 100% rename from pocs/keycloak-ruby/public/apple-touch-icon.png rename to pocs/ruby-keycloak-authz/public/apple-touch-icon.png diff --git a/pocs/keycloak-ruby/public/favicon.ico b/pocs/ruby-keycloak-authz/public/favicon.ico similarity index 100% rename from pocs/keycloak-ruby/public/favicon.ico rename to pocs/ruby-keycloak-authz/public/favicon.ico diff --git a/pocs/keycloak-ruby/public/robots.txt b/pocs/ruby-keycloak-authz/public/robots.txt similarity index 100% rename from pocs/keycloak-ruby/public/robots.txt rename to pocs/ruby-keycloak-authz/public/robots.txt diff --git a/pocs/keycloak-ruby/test/application_system_test_case.rb b/pocs/ruby-keycloak-authz/test/application_system_test_case.rb similarity index 100% rename from pocs/keycloak-ruby/test/application_system_test_case.rb rename to pocs/ruby-keycloak-authz/test/application_system_test_case.rb diff --git a/pocs/keycloak-ruby/test/controllers/.keep b/pocs/ruby-keycloak-authz/test/controllers/.keep similarity index 100% rename from pocs/keycloak-ruby/test/controllers/.keep rename to pocs/ruby-keycloak-authz/test/controllers/.keep diff --git a/pocs/keycloak-ruby/test/fixtures/.keep b/pocs/ruby-keycloak-authz/test/fixtures/.keep similarity index 100% rename from pocs/keycloak-ruby/test/fixtures/.keep rename to pocs/ruby-keycloak-authz/test/fixtures/.keep diff --git a/pocs/keycloak-ruby/test/fixtures/files/.keep b/pocs/ruby-keycloak-authz/test/fixtures/files/.keep similarity index 100% rename from pocs/keycloak-ruby/test/fixtures/files/.keep rename to pocs/ruby-keycloak-authz/test/fixtures/files/.keep diff --git a/pocs/keycloak-ruby/test/helpers/.keep b/pocs/ruby-keycloak-authz/test/helpers/.keep similarity index 100% rename from pocs/keycloak-ruby/test/helpers/.keep rename to pocs/ruby-keycloak-authz/test/helpers/.keep diff --git a/pocs/keycloak-ruby/test/integration/.keep b/pocs/ruby-keycloak-authz/test/integration/.keep similarity index 100% rename from pocs/keycloak-ruby/test/integration/.keep rename to pocs/ruby-keycloak-authz/test/integration/.keep diff --git a/pocs/keycloak-ruby/test/models/.keep b/pocs/ruby-keycloak-authz/test/models/.keep similarity index 100% rename from pocs/keycloak-ruby/test/models/.keep rename to pocs/ruby-keycloak-authz/test/models/.keep diff --git a/pocs/keycloak-ruby/test/system/.keep b/pocs/ruby-keycloak-authz/test/system/.keep similarity index 100% rename from pocs/keycloak-ruby/test/system/.keep rename to pocs/ruby-keycloak-authz/test/system/.keep diff --git a/pocs/keycloak-ruby/test/test_helper.rb b/pocs/ruby-keycloak-authz/test/test_helper.rb similarity index 100% rename from pocs/keycloak-ruby/test/test_helper.rb rename to pocs/ruby-keycloak-authz/test/test_helper.rb diff --git a/pocs/keycloak-ruby/tmp/.keep b/pocs/ruby-keycloak-authz/vendor/.keep similarity index 100% rename from pocs/keycloak-ruby/tmp/.keep rename to pocs/ruby-keycloak-authz/vendor/.keep diff --git a/pocs/keycloak-ruby/yarn.lock b/pocs/ruby-keycloak-authz/yarn.lock similarity index 100% rename from pocs/keycloak-ruby/yarn.lock rename to pocs/ruby-keycloak-authz/yarn.lock diff --git a/src/auth_service.rb b/src/auth_service.rb new file mode 100644 index 00000000..8d3eb6d5 --- /dev/null +++ b/src/auth_service.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +class AuthService + def initialize(config) + @config = config + @registry = PolicyRegistry.setup!(config) + end + + attr_reader :config + + include GRPC::GenericService + + self.marshal_class_method = :encode + self.unmarshal_class_method = :decode + self.service_name = 'envoy.service.auth.v2.Authorization' + + # Performs authorization check based on the attributes associated with the incoming request, + # and returns status `OK` or not `OK`. + rpc :Check, Envoy::Service::Auth::V2::CheckRequest, Envoy::Service::Auth::V2::CheckResponse + + def check(req, rest) + GRPC.logger.debug(req.class.name) { req.to_json(emit_defaults: true) } + host = req.attributes.request.http.host + + case service = config.for_host(host) + when Config::Service + context = Context.new(req, service) + context.evaluate! + + if context.valid? + return ok_response(req, service) + else + return denied_response('Not authorized') + end + end + + denied_response('Service not found', status: :not_found) + end + + protected + + RESPONSE_CODES = { + not_found: { grpc: GRPC::Core::StatusCodes::NOT_FOUND, envoy: Envoy::Type::StatusCode::NotFound }, + forbidden: { grpc: GRPC::Core::StatusCodes::PERMISSION_DENIED, envoy: Envoy::Type::StatusCode::Forbidden } + }.freeze + + def ok_response(req, service) + Envoy::Service::Auth::V2::CheckResponse.new( + status: Google::Rpc::Status.new(code: GRPC::Core::StatusCodes::OK), + ok_response: Envoy::Service::Auth::V2::OkHttpResponse.new( + headers: [ + # TODO: add headers + ] + ) + ) + end + + def denied_response(message, status: :forbidden) + Envoy::Service::Auth::V2::CheckResponse.new( + status: Google::Rpc::Status.new(code: RESPONSE_CODES.dig(status, :grpc)), + denied_response: Envoy::Service::Auth::V2::DeniedHttpResponse.new( + status: Envoy::Type::HttpStatus.new(code: RESPONSE_CODES.dig(status, :envoy)), + body: message, + headers: [ + Envoy::Api::V2::Core::HeaderValueOption.new(header: Envoy::Api::V2::Core::HeaderValue.new(key: 'x-ext-auth-reason', value: status.to_s)), + ] + ) + ) + end +end + +require_relative 'auth_service/policy_registry' +require_relative 'auth_service/context' diff --git a/src/auth_service/context.rb b/src/auth_service/context.rb new file mode 100644 index 00000000..71031953 --- /dev/null +++ b/src/auth_service/context.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +class AuthService::Context + attr_reader :identity, :metadata, :authorization + attr_reader :request, :service + + def initialize(request, service) + @request = request + @service = service + @identity = {} + @metadata = {} + @authorization = {} + end + + def evaluate! + proc = ->(obj, result) { result[obj] = obj.call(self) } + + service.identity.each_with_object(identity, &proc) + service.metadata.each_with_object(metadata, &proc) + service.authorization.each_with_object(authorization, &proc) + + @identity.freeze + @metadata.freeze + @authorization.freeze + end + + def authenticated? + identity.values.any? + end + + def authorized? + authorization.select { |config, _| config.enabled? }.values.all?(&:authorized?) + end + + def valid? + authenticated? && authorized? + end + + def to_h + { + request: request, + service: service, + identity: identity.transform_keys(&:name).transform_values(&:to_h), + metadata: metadata.transform_keys{ |key| key.class.to_s.demodulize.underscore } + }.transform_values(&:to_h) + end +end diff --git a/src/auth_service/policy_registry.rb b/src/auth_service/policy_registry.rb new file mode 100644 index 00000000..9eac30c7 --- /dev/null +++ b/src/auth_service/policy_registry.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AuthService::PolicyRegistry + def self.setup!(config) + new(config).setup! + end + + def initialize(config) + @config = config + end + + attr_reader :config + + def setup! + config.each_host.flat_map(&:authorization).map do |authorization| + authorization.try(:register!) + end + end +end diff --git a/src/config.rb b/src/config.rb new file mode 100644 index 00000000..6582f035 --- /dev/null +++ b/src/config.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'yaml/store' +require 'ostruct' + +class Config + def initialize(file) + @store = YAML::Store.new(file) + end + + def for_host(name) + @store.transaction(true) do + case (service = @store.fetch(name, Service::NotFound)) + when Service::NotFound + Service::NotFound + else + Service.new(service) + end + end + end + + def host_names + @store.transaction(true) { @store.roots } + end + + def each_host(&block) + host_names.map(&method(:for_host)).map(&block) + end + + module BuildSubclass + class AmbiguousKeysError < StandardError; end + + def build(object) + name = object.keys.tap { |keys| raise AmbiguousKeysError, keys if keys.size > 1 }.first + + const = constants(false).find { |const| const.to_s.downcase == name.to_s.downcase } + + const_get(const, false).new(object.fetch(name)) + end + end +end + +require_relative 'config/service' diff --git a/src/config/authorization.rb b/src/config/authorization.rb new file mode 100644 index 00000000..672fe817 --- /dev/null +++ b/src/config/authorization.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class Config::Authorization < OpenStruct + extend Config::BuildSubclass + + def enabled? + enabled.nil? || !!enabled + end +end + +require_relative 'authorization/opa' +require_relative 'authorization/jwt' diff --git a/src/config/authorization/jwt.rb b/src/config/authorization/jwt.rb new file mode 100644 index 00000000..d90794eb --- /dev/null +++ b/src/config/authorization/jwt.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require_relative 'response' + +class Config::Authorization::JWT < Config::Authorization + def call(context) + return unless enabled? + + # TODO: Build response + end +end diff --git a/src/config/authorization/opa.rb b/src/config/authorization/opa.rb new file mode 100644 index 00000000..8d10d485 --- /dev/null +++ b/src/config/authorization/opa.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true + +require 'net/http' +require 'uri' +require 'json' + +require_relative 'response' + +class Config::Authorization::OPA < Config::Authorization + DEFAULTS = { + endpoint: 'http://localhost:8181', + data_api: '/v1/data/ostia/authz', + policy_api: '/v1/policies' + } + + class Response < Config::Authorization::Response + def authorized? + result['allow'] + end + end + + class RegisterException < StandardError; end + + def register! + auth_request = Net::HTTP::Put.new(policy_uri, 'Content-Type' => 'text/plain') + auth_request.body = rego_policy + auth_response = Net::HTTP.start(policy_uri.hostname, policy_uri.port) do |http| + http.request(auth_request) + end + + case auth_response + when Net::HTTPOK + JSON.parse(auth_response.body) + else + raise RegisterException, auth_response + end + rescue RegisterException, Net::HTTPError + self.enabled = false + nil + end + + def call(context) + return unless enabled? + + auth_request = Net::HTTP::Post.new(data_uri, 'Content-Type' => 'application/json') + request, identity, metadata = context.to_h.values_at(:request, :identity, :metadata) + auth_request.body = { input: request.merge(context: { identity: identity.values.first, metadata: metadata }) }.to_json + puts "[OPA] #{auth_request.body}" + auth_response = Net::HTTP.start(data_uri.hostname, data_uri.port) do |http| + http.request(auth_request) + end + + case auth_response + when Net::HTTPOK + response_json = case auth_response['content-type'] + when 'application/json' + JSON.parse(auth_response.body) + else + { allowed: true, message: auth_response.body } + end + Response.new(response_json) + end + end + + protected + + DEFAULTS.keys.each do |attribute| + define_method(attribute) do + super() || DEFAULTS[attribute] + end + end + + alias _endpoint endpoint + + def endpoint + URI.parse(_endpoint) + end + + def data_uri + uri = endpoint + uri.path = [data_api, uuid].join('/') + uri + end + + def policy_uri + uri = endpoint + uri.path = [policy_api, uuid].join('/') + uri + end + + def rego_policy + <<~REGO + package ostia.authz["#{uuid}"] + + import input.attributes.request.http as http_request + import input.context.identity + import input.context.metadata + + resource = object.get(input.context, "resource", {}) + path = split(trim_left(http_request.path, "/"), "/") + + default allow = false + + #{rego} + REGO + end +end diff --git a/src/config/authorization/response.rb b/src/config/authorization/response.rb new file mode 100644 index 00000000..2717f03b --- /dev/null +++ b/src/config/authorization/response.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class Config::Authorization::Response < OpenStruct + def authorized? + raise NotImplementedError, __method__ + end +end diff --git a/src/config/identity.rb b/src/config/identity.rb new file mode 100644 index 00000000..b1c1e5b8 --- /dev/null +++ b/src/config/identity.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class Config::Identity < OpenStruct + extend Config::BuildSubclass + + def call(req) + raise NotImplementedError, __method__ + end + + def initialize(hash = {}) + super + self.enabled = !!config if self.enabled.nil? + end + + def self.[](name) + ->(other) do + self === other && other&.name == name + end + end +end + +require_relative 'identity/oidc' diff --git a/src/config/identity/oidc.rb b/src/config/identity/oidc.rb new file mode 100644 index 00000000..9ec0550a --- /dev/null +++ b/src/config/identity/oidc.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require 'openid_connect' + +Module.new do + ### Monkey patch to keep the desired scheme of the issuer instead forcing it into https + + def initialize(uri) + @scheme = uri.scheme + super + end + + attr_reader :scheme + + def endpoint + URI::Generic.build(scheme: scheme, host: host, port: port, path: path) + rescue URI::Error => e + raise SWD::Exception.new(e.message) + end + + prepend_features(::OpenIDConnect::Discovery::Provider::Config::Resource) +end + +# not in the RFC, but keycloak has it +OpenIDConnect::Discovery::Provider::Config::Response.attr_optional :token_introspection_endpoint, :introspection_endpoint +OpenIDConnect::ResponseObject::IdToken.attr_optional :realm_access, :resource_access, :scope, :email_verified, :preferred_username, :email + +class Config::Identity::OIDC < Config::Identity + def config + case config = self[:config] + when nil + discover! + when Hash + self[:config] = OpenStruct.new(config) + else + config + end + end + + def discover! + raise OpenIDConnect::Discovery::DiscoveryFailed unless endpoint + + self[:config] ||= ::OpenIDConnect::Discovery::Provider::Config.discover!(endpoint) + rescue OpenIDConnect::Discovery::DiscoveryFailed + self.enabled = false + nil + end + + class Token + def initialize(token) + @token = token + end + + def decode!(keys) + @decoded = ::OpenIDConnect::ResponseObject::IdToken.decode(@token, keys) + end + + def to_s + @token + end + + delegate :raw_attributes, to: :@decoded, allow_nil: true + alias to_h raw_attributes # because OpenIDConnect::ResponseObject::IdToken#as_json will only return string values + + private def method_missing(symbol, *args, &block) + return super unless @decoded + @decoded.public_send(symbol, *args, &block) + end + end + + def call(context) + id_token = decode_id_token(context.request) + rescue JSON::JWK::Set::KidNotFound, JSON::JWS::VerificationFailed => err + false + end + + def decode_id_token(req) + auth = Rack::Auth::AbstractRequest.new(req.attributes.request.http.to_env) + + case auth.scheme + when 'bearer' + Token.new(auth.params).tap { |t| t.decode!(public_keys) } + end + end + + def public_keys + config&.jwks + end +end diff --git a/src/config/metadata.rb b/src/config/metadata.rb new file mode 100644 index 00000000..32be625b --- /dev/null +++ b/src/config/metadata.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class Config::Metadata < OpenStruct + extend Config::BuildSubclass +end + +require_relative 'metadata/user_info' diff --git a/src/config/metadata/user_info.rb b/src/config/metadata/user_info.rb new file mode 100644 index 00000000..4ab0f256 --- /dev/null +++ b/src/config/metadata/user_info.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'net/http' +require 'json' + +class Config::Metadata::UserInfo < Config::Metadata + def call(context) + finder = Config::Identity::OIDC[self[:oidc]] + oidc = context.service.identity.find { |id| finder === id } or return + id = context.identity.fetch(oidc) { return } + puts id + + uri = URI(oidc.config.token_introspection_endpoint || oidc.config.introspection_endpoint) + uri.user = client_id + uri.password = client_secret + + res = Net::HTTP.post_form uri, + { 'token' => id, 'token_type_hint' => 'requesting_party_token' } + + case res + when Net::HTTPOK + + case res['content-type'] + when 'application/json' + JSON.parse(res.body) + end + end + end +end diff --git a/src/config/service.rb b/src/config/service.rb new file mode 100644 index 00000000..7ce87ba3 --- /dev/null +++ b/src/config/service.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class Config::Service < OpenStruct + NotFound = Object.new + + def initialize(hash = nil) + super + + self.identity = Array(identity).map(&Config::Identity.method(:build)) + self.authorization = Array(authorization).map(&Config::Authorization.method(:build)) + self.metadata = Array(metadata).map(&Config::Metadata.method(:build)) + end + + def enabled? + !!enabled + end +end + +require_relative 'identity' +require_relative 'authorization' +require_relative 'metadata' diff --git a/src/main.rb b/src/main.rb new file mode 100644 index 00000000..66c21f55 --- /dev/null +++ b/src/main.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'rack/auth/basic' +require 'logger' + +require 'envoy/service/auth/v3/external_auth_services_pb' +require 'envoy/service/auth/v2/external_auth_pb' + +require_relative 'config' +require_relative 'auth_service' +require_relative 'response_interceptor' + +module RubyLogger + def logger + LOGGER + end + + LOGGER = Logger.new(STDOUT) +end + +# GRPC is the general RPC module +module GRPC + # Inject the noop #logger if no module-level logger method has been injected. + extend RubyLogger +end + +Envoy::Service::Auth::V2::AttributeContext::HttpRequest.module_eval do + def to_env + headers.to_h.delete_if { |k, _| k.start_with?(':') }.transform_keys { |k| "HTTP_#{k.tr('-', '_').upcase}" } + end +end + +def main + port = "0.0.0.0:#{ENV.fetch('PORT', 50051)}" + config = Config.new(ENV.fetch('CONFIG', 'examples/config.yml')) + server = GRPC::RpcServer.new(interceptors: [ResponseInterceptor.new]) + server.add_http2_port(port, :this_port_is_insecure) + GRPC.logger.info("... running insecurely on #{port}") + server.handle(AuthService.new(config)) + + # Runs the server with SIGHUP, SIGINT and SIGQUIT signal handlers to + # gracefully shutdown. + # User could also choose to run server via call to run_till_terminated + server.run_till_terminated_or_interrupted([1, +'int', +'SIGQUIT']) +end + +main if __FILE__ == $0 diff --git a/src/response_interceptor.rb b/src/response_interceptor.rb new file mode 100644 index 00000000..c439f608 --- /dev/null +++ b/src/response_interceptor.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class ResponseInterceptor < GRPC::ServerInterceptor + def request_response(request:, call:, method:) + GRPC.logger.info("Received request/response call at method #{method}" \ + " with request #{request} for call #{call}") + + GRPC.logger.info("[GRPC::Ok] (#{method.owner.name}.#{method.name})") + yield + end +end diff --git a/test/auth_service_test.rb b/test/auth_service_test.rb new file mode 100644 index 00000000..c3cb9bf5 --- /dev/null +++ b/test/auth_service_test.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'test_helper' +require 'json' + +describe AuthService do + let(:config) { Config.new(file_fixture('config.yml')) } + let(:service) { AuthService.new(config) } + + describe 'request' do + let(:body) { JSON.parse(file_fixture('request.json').read) } + let(:request) { Envoy::Service::Auth::V2::CheckRequest.decode_json body.to_json } + + subject do + service.check(request, GRPC::ActiveCall.allocate) + end + + it 'must respond positively' do + expect(subject).must_be_instance_of(Envoy::Service::Auth::V2::CheckResponse) + expect(subject.ok_response).must_be_instance_of(Envoy::Service::Auth::V2::OkHttpResponse) + end + end +end diff --git a/test/config/authorization_test.rb b/test/config/authorization_test.rb new file mode 100644 index 00000000..bb332fa2 --- /dev/null +++ b/test/config/authorization_test.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'test_helper' +require 'json' + +describe Config::Authorization do + let(:authorization_config) { YAML.load(file_fixture('config.yml').read).dig('localhost:8000', 'authorization') } + + describe 'opa' do + let(:described_class) { Config::Authorization::OPA } + let(:input) { JSON.parse(file_fixture('opa_input_forbidden.json').read)['input'] } + let(:config) { authorization_config.first['opa'] } + let(:context) do + OpenStruct.new(to_h: { + request: input.slice('attributes'), + identity: { 'test' => input.dig('context', 'identity') }, + metadata: input.dig('context', 'metadata') + }) + end + + subject do + described_class.new(config) + end + + before do + subject.register! + end + + it 'wraps the response' do + result = subject.call(context) + expect(result).must_be_instance_of(described_class::Response) + refute(result.authorized?) # user does not have permission + end + end +end diff --git a/test/fixtures/config.yml b/test/fixtures/config.yml new file mode 100644 index 00000000..5f01bdff --- /dev/null +++ b/test/fixtures/config.yml @@ -0,0 +1,179 @@ +localhost:8000: + enabled: true + + identity: + - oidc: + name: test + config: + issuer: http://localhost:8080/auth/realms/test + authorization_endpoint: http://localhost:8080/auth/realms/test/protocol/openid-connect/auth + token_endpoint: http://localhost:8080/auth/realms/test/protocol/openid-connect/token + token_introspection_endpoint: http://localhost:8080/auth/realms/test/protocol/openid-connect/token/introspect + userinfo_endpoint: http://localhost:8080/auth/realms/test/protocol/openid-connect/userinfo + end_session_endpoint: http://localhost:8080/auth/realms/test/protocol/openid-connect/logout + jwks_uri: http://localhost:8080/auth/realms/test/protocol/openid-connect/certs + check_session_iframe: http://localhost:8080/auth/realms/test/protocol/openid-connect/login-status-iframe.html + grant_types_supported: + - authorization_code + - implicit + - refresh_token + - password + - client_credentials + response_types_supported: + - code + - none + - id_token + - token + - id_token token + - code id_token + - code token + - code id_token token + subject_types_supported: + - public + - pairwise + id_token_signing_alg_values_supported: + - PS384 + - ES384 + - RS384 + - HS256 + - HS512 + - ES256 + - RS256 + - HS384 + - ES512 + - PS256 + - PS512 + - RS512 + id_token_encryption_alg_values_supported: + - RSA-OAEP + - RSA1_5 + id_token_encryption_enc_values_supported: + - A128GCM + - A128CBC-HS256 + userinfo_signing_alg_values_supported: + - PS384 + - ES384 + - RS384 + - HS256 + - HS512 + - ES256 + - RS256 + - HS384 + - ES512 + - PS256 + - PS512 + - RS512 + - none + request_object_signing_alg_values_supported: + - PS384 + - ES384 + - RS384 + - HS256 + - HS512 + - ES256 + - RS256 + - HS384 + - ES512 + - PS256 + - PS512 + - RS512 + - none + response_modes_supported: + - query + - fragment + - form_post + registration_endpoint: http://localhost:8080/auth/realms/test/clients-registrations/openid-connect + token_endpoint_auth_methods_supported: + - private_key_jwt + - client_secret_basic + - client_secret_post + - tls_client_auth + - client_secret_jwt + token_endpoint_auth_signing_alg_values_supported: + - PS384 + - ES384 + - RS384 + - HS256 + - HS512 + - ES256 + - RS256 + - HS384 + - ES512 + - PS256 + - PS512 + - RS512 + claims_supported: + - aud + - sub + - iss + - auth_time + - name + - given_name + - family_name + - preferred_username + - email + - acr + claim_types_supported: + - normal + claims_parameter_supported: false + scopes_supported: + - openid + - address + - email + - microprofile-jwt + - offline_access + - phone + - profile + - roles + - web-origins + request_parameter_supported: true + request_uri_parameter_supported: true + code_challenge_methods_supported: + - plain + - S256 + tls_client_certificate_bound_access_tokens: true + introspection_endpoint: http://localhost:8080/auth/realms/test/protocol/openid-connect/token/introspect + jwks: !ruby/array:JSON::JWK::Set + - !ruby/hash:JSON::JWK + kid: PiEqHdnRIDQ1RJUd0-1vYl3TUlzzdx4RuIJVU1KpsgU + kty: RSA + alg: RS256 + use: sig + n: wi5BywZK81v9A4MqYOGD5HCKk9yBjjNuWyT8f40OsmAqertm8HN02qjsTfkw-6uEEHEtUV4_Rlk4ZqprEgKzVGu1il2t4OvByL7nW3OkbKAIoRPIX9fXuL4u_X5eWpwIAgVHrosYcGu0Jnvjnjol_n7UwnMuVw7UW3dg5mfBVAi3C0M_2iiKypUk1zE1OE2fbe2CHPo7yBwbbHGwyajoMHaak6cIyCEEoFAyMGh7QNLniuy9GHy4y6rnn2cpySwxkGZPnKCwUUOEkqKJD2e2E9tIq-FGv-sXRMuavGmbSwUz-0uU-KwpBL3_Fm01EdD8onL1KpxWxSF-mBNbxDR_mQ + e: AQAB + x5c: + - MIIClzCCAX8CBgF1ZWe4ITANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDAR0ZXN0MB4XDTIwMTAyNjE0NTMxN1oXDTMwMTAyNjE0NTQ1N1owDzENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIuQcsGSvNb/QODKmDhg+RwipPcgY4zblsk/H+NDrJgKnq7ZvBzdNqo7E35MPurhBBxLVFeP0ZZOGaqaxICs1RrtYpdreDrwci+51tzpGygCKETyF/X17i+Lv1+XlqcCAIFR66LGHBrtCZ74546Jf5+1MJzLlcO1Ft3YOZnwVQItwtDP9ooisqVJNcxNThNn23tghz6O8gcG2xxsMmo6DB2mpOnCMghBKBQMjBoe0DS54rsvRh8uMuq559nKcksMZBmT5ygsFFDhJKiiQ9nthPbSKvhRr/rF0TLmrxpm0sFM/tLlPisKQS9/xZtNRHQ/KJy9SqcVsUhfpgTW8Q0f5kCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAsrpXLow79P6hVew5ytbckx8PFqkISXWpufn5MqDU5295bAOmmgPQqtQpiSK9MAjkXstvufdFB0WpRUHL8MzcnXXig69JvRR7rL8FbbEF7E+pcVDUPJ9lGOYaFc5GJ221SA4/p0OtQegCUtImW7fBPclNyHgddGyQgpcZqiLfxWpOEXZ20vLMuj5VXYGthplWjphzRUqlSLshxDWjaVYUlaPuQbNPDw8E+qk7+NpERTLAKDVjUqqHdCGtkzkKv3n3sMijn7HuVGv/lf/JqYrFvhvmiLYa2nGlIUSEhNrPbALsW0Fi4N2H6mrpD/bgpEZae+rd5DNvW/n789AdKNDtRw== + x5t: 4mREF7c--JC6_9O_gAFiAFk6NUs + x5t#S256: tJsfWZiBJ61q1LwXV30VUNZHJkROMq5oMp8oIt4DY94 + + metadata: + - userinfo: + oidc: test + client_id: test + client_secret: dfab4c74-4614-4849-abe8-a009bc25a9d6 + + authorization: + - opa: + uuid: 8fa79d93-0f93-4e23-8c2a-666be266cad1 + rego: | + allow { + http_request.method == "GET" + path = ["pets"] + } + + allow { + http_request.method == "GET" + path = ["pets", "stats"] + is_admin + } + + is_admin { + metadata.user_info.roles[_] == "admin" + } + - jwt: + enabled: false + match: + http: + path: '/api/*' + claim: + aud: api diff --git a/test/fixtures/opa_input.json b/test/fixtures/opa_input.json new file mode 100644 index 00000000..e42cdb31 --- /dev/null +++ b/test/fixtures/opa_input.json @@ -0,0 +1,77 @@ +{ + "input": { + "attributes": { + "source": { + "address": { + "socket_address": { + "protocol": "TCP", + "address": "172.18.0.1", + "port_value": 47428, + "named_port": "", + "resolver_name": "", + "ipv4_compat": false + }, + "pipe": null + }, + "service": "", + "labels": {}, + "principal": "", + "certificate": "" + }, + "destination": { + "address": null, + "service": "", + "labels": {}, + "principal": "", + "certificate": "" + }, + "request": { + "time": { + "seconds": 1603367724, + "nanos": 500287999 + }, + "http": { + "id": "8911960713991399467", + "method": "GET", + "headers": { + ":path": "/pets", + ":method": "GET", + "x-request-id": "7a9bc5dd-1672-43ca-a99f-e209b8c725ca", + ":authority": "localhost:8000", + "user-agent": "curl/7.70.0", + "x-forwarded-proto": "http", + "accept": "*/*", + "authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJQaUVxSGRuUklEUTFSSlVkMC0xdllsM1RVbHp6ZHg0UnVJSlZVMUtwc2dVIn0.eyJleHAiOjE2MDM3MjQ0MzMsImlhdCI6MTYwMzcyNDEzMywianRpIjoiMDgwYTMwMTQtZjdkOC00M2RmLWFlZmMtNDc3NDcwNDE2NTQ5IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL3Rlc3QiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiODg4NTZlNWQtMGEzOS00YzE4LTljYjktMWM2NjJlYWFhMDgwIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoib3N0aWEiLCJzZXNzaW9uX3N0YXRlIjoiMjhjNTljMDUtZDBjOC00YWZjLWI4OWYtZDY5YmEyOGVhOTU5IiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJjbGllbnRJZCI6Im9zdGlhIiwiY2xpZW50SG9zdCI6IjE3Mi4xNy4wLjEiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzZXJ2aWNlLWFjY291bnQtb3N0aWEiLCJjbGllbnRBZGRyZXNzIjoiMTcyLjE3LjAuMSJ9.T3CUaX1rqN1qY1wSAeaGxcvc1691cDSWDyKpTXeI1SjsZPMbzPC_pFujxBbk-FTIGlmisr7JEFXuAFsSeHfgoDFQIHoZ9vZP_fhoioLKtBOxB6VgrRT24CDqXi08AGAjPnw86J8Pu2AHwjE0Le1V6b9DXBA155vwaHQ5TbT6XOVHocz7gds3xKsaS_vGVDlFOFArr9F0CsxAilKyfvMeu077025FvB41ICXihumev87qBKhcLrx3ZpQ7A5fNQuMIRttVP_cRP23bayeBBjqq3kfvqgNALveqbH4ymcW-bWsYTEw6gkecm9XS0bOK6tKtdythnE2al0E7uXLwyOedtg" + }, + "path": "/pets", + "host": "localhost:8000", + "scheme": "", + "query": "", + "fragment": "", + "size": 0, + "protocol": "HTTP/1.1", + "body": "" + } + }, + "context_extensions": {}, + "metadata_context": { + "filter_metadata": {} + } + }, + "context": { + "identity": { + "iss": "http://localhost:8080/auth/realms/test", + "sub": "88856e5d-0a39-4c18-9cb9-1c662eaaa080", + "aud": "account", + "exp": 1603724433, + "iat": 1603724133, + "acr": "1", + "azp": "ostia", + "jti": "080a3014-f7d8-43df-aefc-477470416549" + }, + "metadata": { + "user_info": null + } + } + } +} diff --git a/test/fixtures/opa_input_forbidden.json b/test/fixtures/opa_input_forbidden.json new file mode 100644 index 00000000..5d854695 --- /dev/null +++ b/test/fixtures/opa_input_forbidden.json @@ -0,0 +1,81 @@ +{ + "input": { + "attributes": { + "source": { + "address": { + "socket_address": { + "protocol": "TCP", + "address": "172.18.0.1", + "port_value": 47428, + "named_port": "", + "resolver_name": "", + "ipv4_compat": false + }, + "pipe": null + }, + "service": "", + "labels": {}, + "principal": "", + "certificate": "" + }, + "destination": { + "address": null, + "service": "", + "labels": {}, + "principal": "", + "certificate": "" + }, + "request": { + "time": { + "seconds": 1603367724, + "nanos": 500287999 + }, + "http": { + "id": "8911960713991399467", + "method": "GET", + "headers": { + ":path": "/pets/stats", + ":method": "GET", + "x-request-id": "7a9bc5dd-1672-43ca-a99f-e209b8c725ca", + ":authority": "localhost:8000", + "user-agent": "curl/7.70.0", + "x-forwarded-proto": "http", + "accept": "*/*", + "authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJQaUVxSGRuUklEUTFSSlVkMC0xdllsM1RVbHp6ZHg0UnVJSlZVMUtwc2dVIn0.eyJleHAiOjE2MDM3MjQ0MzMsImlhdCI6MTYwMzcyNDEzMywianRpIjoiMDgwYTMwMTQtZjdkOC00M2RmLWFlZmMtNDc3NDcwNDE2NTQ5IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL3Rlc3QiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiODg4NTZlNWQtMGEzOS00YzE4LTljYjktMWM2NjJlYWFhMDgwIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoib3N0aWEiLCJzZXNzaW9uX3N0YXRlIjoiMjhjNTljMDUtZDBjOC00YWZjLWI4OWYtZDY5YmEyOGVhOTU5IiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJjbGllbnRJZCI6Im9zdGlhIiwiY2xpZW50SG9zdCI6IjE3Mi4xNy4wLjEiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzZXJ2aWNlLWFjY291bnQtb3N0aWEiLCJjbGllbnRBZGRyZXNzIjoiMTcyLjE3LjAuMSJ9.T3CUaX1rqN1qY1wSAeaGxcvc1691cDSWDyKpTXeI1SjsZPMbzPC_pFujxBbk-FTIGlmisr7JEFXuAFsSeHfgoDFQIHoZ9vZP_fhoioLKtBOxB6VgrRT24CDqXi08AGAjPnw86J8Pu2AHwjE0Le1V6b9DXBA155vwaHQ5TbT6XOVHocz7gds3xKsaS_vGVDlFOFArr9F0CsxAilKyfvMeu077025FvB41ICXihumev87qBKhcLrx3ZpQ7A5fNQuMIRttVP_cRP23bayeBBjqq3kfvqgNALveqbH4ymcW-bWsYTEw6gkecm9XS0bOK6tKtdythnE2al0E7uXLwyOedtg" + }, + "path": "/pets/stats", + "host": "localhost:8000", + "scheme": "", + "query": "", + "fragment": "", + "size": 0, + "protocol": "HTTP/1.1", + "body": "" + } + }, + "context_extensions": {}, + "metadata_context": { + "filter_metadata": {} + } + }, + "context": { + "identity": { + "iss": "http://localhost:8080/auth/realms/test", + "sub": "88856e5d-0a39-4c18-9cb9-1c662eaaa080", + "aud": "account", + "exp": 1603724433, + "iat": 1603724133, + "acr": "1", + "azp": "ostia", + "jti": "080a3014-f7d8-43df-aefc-477470416549" + }, + "metadata": { + "user_info": { + "roles": [ + "member" + ] + } + } + } + } +} diff --git a/test/fixtures/request.json b/test/fixtures/request.json new file mode 100644 index 00000000..579916e4 --- /dev/null +++ b/test/fixtures/request.json @@ -0,0 +1,49 @@ +{ + "attributes": { + "source": { + "address": { + "socketAddress": { + "protocol": "TCP", + "address": "172.18.0.1", + "portValue": 47428, + "resolverName": "", + "ipv4Compat": false + } + }, + "service": "", + "labels": {}, + "principal": "", + "certificate": "" + }, + "destination": {}, + "request": { + "time": "2020-10-22T11:55:24.500288000Z", + "http": { + "id": "8911960713991399467", + "method": "GET", + "headers": { + ":path": "/pets", + ":method": "GET", + ":authority": "localhost:8000", + "x-request-id": "7a9bc5dd-1672-43ca-a99f-e209b8c725ca", + "user-agent": "curl/7.70.0", + "x-forwarded-proto": "http", + "authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJQaUVxSGRuUklEUTFSSlVkMC0xdllsM1RVbHp6ZHg0UnVJSlZVMUtwc2dVIn0.eyJleHAiOjE2MDM3MjQ0MzMsImlhdCI6MTYwMzcyNDEzMywianRpIjoiMDgwYTMwMTQtZjdkOC00M2RmLWFlZmMtNDc3NDcwNDE2NTQ5IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL3Rlc3QiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiODg4NTZlNWQtMGEzOS00YzE4LTljYjktMWM2NjJlYWFhMDgwIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoib3N0aWEiLCJzZXNzaW9uX3N0YXRlIjoiMjhjNTljMDUtZDBjOC00YWZjLWI4OWYtZDY5YmEyOGVhOTU5IiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJjbGllbnRJZCI6Im9zdGlhIiwiY2xpZW50SG9zdCI6IjE3Mi4xNy4wLjEiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzZXJ2aWNlLWFjY291bnQtb3N0aWEiLCJjbGllbnRBZGRyZXNzIjoiMTcyLjE3LjAuMSJ9.T3CUaX1rqN1qY1wSAeaGxcvc1691cDSWDyKpTXeI1SjsZPMbzPC_pFujxBbk-FTIGlmisr7JEFXuAFsSeHfgoDFQIHoZ9vZP_fhoioLKtBOxB6VgrRT24CDqXi08AGAjPnw86J8Pu2AHwjE0Le1V6b9DXBA155vwaHQ5TbT6XOVHocz7gds3xKsaS_vGVDlFOFArr9F0CsxAilKyfvMeu077025FvB41ICXihumev87qBKhcLrx3ZpQ7A5fNQuMIRttVP_cRP23bayeBBjqq3kfvqgNALveqbH4ymcW-bWsYTEw6gkecm9XS0bOK6tKtdythnE2al0E7uXLwyOedtg", + "accept": "*/*" + }, + "path": "/pets", + "host": "localhost:8000", + "scheme": "", + "query": "", + "fragment": "", + "size": "0", + "protocol": "HTTP/1.1", + "body": "" + } + }, + "contextExtensions": {}, + "metadataContext": { + "filterMetadata": {} + } + } +} diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 00000000..3be0dad5 --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require 'pathname' + +root = Pathname(__dir__).expand_path.dirname + +$LOAD_PATH << root.join('lib/grpc') +$LOAD_PATH << root.join('src') + +Module.new do + def file_fixture(filename) + Pathname.new(File.join(__dir__, "fixtures/#{filename}")) + end + + prepend_features(Minitest::Spec) +end + +require 'main' diff --git a/vendor/envoy-xds-grpc/data-plane-api b/vendor/envoy-xds-grpc/data-plane-api new file mode 160000 index 00000000..4b06e011 --- /dev/null +++ b/vendor/envoy-xds-grpc/data-plane-api @@ -0,0 +1 @@ +Subproject commit 4b06e011e257a534c295bb7dffa1f74aaae7c598 diff --git a/vendor/envoy-xds-grpc/googleapis b/vendor/envoy-xds-grpc/googleapis new file mode 160000 index 00000000..00bbad4d --- /dev/null +++ b/vendor/envoy-xds-grpc/googleapis @@ -0,0 +1 @@ +Subproject commit 00bbad4dfd6633cf4b5f9596c1f93b756bb5c776 diff --git a/vendor/envoy-xds-grpc/protoc-gen-validate b/vendor/envoy-xds-grpc/protoc-gen-validate new file mode 160000 index 00000000..5f20e660 --- /dev/null +++ b/vendor/envoy-xds-grpc/protoc-gen-validate @@ -0,0 +1 @@ +Subproject commit 5f20e660418deff82a3faf68095dca16ba5ad44e diff --git a/vendor/envoy-xds-grpc/protos b/vendor/envoy-xds-grpc/protos new file mode 100644 index 00000000..becca115 --- /dev/null +++ b/vendor/envoy-xds-grpc/protos @@ -0,0 +1,23 @@ +envoy/service/auth/v2/external_auth.proto +envoy/service/auth/v2/attribute_context.proto +envoy/type/semantic_version.proto +envoy/api/v2/core/base.proto +envoy/type/http_status.proto +envoy/api/v2/core/address.proto +envoy/api/v2/core/socket_option.proto +udpa/annotations/migrate.proto +envoy/api/v2/core/backoff.proto +envoy/api/v2/core/http_uri.proto +envoy/type/percent.proto +envoy/config/core/v3/base.proto +envoy/service/auth/v3/external_auth.proto +envoy/service/auth/v3/attribute_context.proto +envoy/type/v3/http_status.proto +envoy/config/core/v3/address.proto +udpa/annotations/status.proto +udpa/annotations/versioning.proto +envoy/config/core/v3/socket_option.proto +envoy/config/core/v3/backoff.proto +envoy/config/core/v3/http_uri.proto +envoy/type/v3/percent.proto +envoy/type/v3/semantic_version.proto diff --git a/vendor/envoy-xds-grpc/udpa b/vendor/envoy-xds-grpc/udpa new file mode 160000 index 00000000..7e6fe051 --- /dev/null +++ b/vendor/envoy-xds-grpc/udpa @@ -0,0 +1 @@ +Subproject commit 7e6fe0510fb53d1053138b61476ec271b519602c