From 03136747dd80e8b9ded81a61e03e72e9c4beac11 Mon Sep 17 00:00:00 2001 From: mo khan Date: Thu, 27 Feb 2025 13:56:52 -0700 Subject: Move src file to bin dir --- README.md | 42 +++++++++++++ bin/idp | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++ bin/sp | 147 +++++++++++++++++++++++++++++++++++++++++++++ src/idp/.gitignore | 1 - src/idp/README.md | 12 ---- src/idp/main.rb | 171 ----------------------------------------------------- src/sp/README.md | 16 ----- src/sp/main.rb | 147 --------------------------------------------- 8 files changed, 360 insertions(+), 347 deletions(-) create mode 100644 README.md create mode 100755 bin/idp create mode 100755 bin/sp delete mode 100644 src/idp/.gitignore delete mode 100644 src/idp/README.md delete mode 100755 src/idp/main.rb delete mode 100644 src/sp/README.md delete mode 100644 src/sp/main.rb diff --git a/README.md b/README.md new file mode 100644 index 0000000..5b02c0e --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# Spike + +1. Thrift + GRPC + 1. idp (headless) + * provide a thrift/grpc endpoint that is the equivalent of `Ability.allowed?(subject, permission, resource)` + 1. gitlab +2. OpenID Connect + OAuth + * two services + 1. idp (with login pages) + * user + * member + * `member_role` + 1. gitlab + * groups + * project + * OpenID transaction to provide authn information to `gitlab-org/gitlab` + * OAuth token introspection endpoint to provide token permissions +3. API Gateway + * using golang reverse proxy and one of the new policy dsl's +4. OPA agent style side car using declarative policy + + +## Identity Provider (SAML IdP) + +This is a tiny SAML Identity Provider for testing out interactions with +a SAML Service Provider + +1. Start the server: + + $ ruby ./bin/idp + +1. Use `http://localhost:8282/metadata.xml` as your SAML IdP Metadata url. + +## Service Provider (SAML SP) + +This is a tiny SAML Service Provider for testing out interactions with a SAML Identity Provider (IdP) + +1. Start the server: + + $ ruby ./bin/sp + +1. Use `http://localhost:8283/metadata.xml` as your SAML SP Metadata url. diff --git a/bin/idp b/bin/idp new file mode 100755 index 0000000..daa15cc --- /dev/null +++ b/bin/idp @@ -0,0 +1,171 @@ +#!/usr/bin/env ruby + +# Start the server by running: +# +# $ ruby main.rb + +require "bundler/inline" +gemfile do + source "https://rubygems.org" + + gem "erb", "~> 4.0" + gem "rack", "~> 3.0" + gem "rackup", "~> 2.0" + gem "saml-kit", "~> 1.0" + gem "webrick", "~> 1.0" +end + +class User + def initialize(attributes) + @attributes = attributes + end + + def name_id_for(name_id_format) + @attributes[:email] + end + + def assertion_attributes_for(request) + { + custom: 'custom attribute' + } + end +end + +class OnDemandRegistry < Saml::Kit::DefaultRegistry + def metadata_for(entity_id) + found = super(entity_id) + return found if found + + register_url(entity_id, verify_ssl: false) + super(entity_id) + end +end + +Saml::Kit.configure do |x| + x.entity_id = "http://localhost:8282/metadata.xml" + x.registry = OnDemandRegistry.new + x.logger = Logger.new("/dev/stderr") +end + +class IdentityProvider + def initialize + @storage = {} + end + + # Download IDP Metadata + # + # GET /metadata.xml + def metadata + xml = Saml::Kit::Metadata.build_xml do |builder| + builder.contact_email = 'hi@example.com' + builder.organization_name = "Acme, Inc" + builder.organization_url = "https://example.com" + builder.build_identity_provider do |x| + x.add_single_sign_on_service("http://localhost:8282/sessions/new", binding: :http_post) + x.name_id_formats = [Saml::Kit::Namespaces::EMAIL_ADDRESS] + x.attributes << :Username + end + end + + [200, { 'Content-Type' => "application/samlmetadata+xml" }, [xml]] + end + + def call(env) + path = env['PATH_INFO'] + case env['REQUEST_METHOD'] + when 'GET' + case path + when "/metadata.xml" + return metadata + when "/sessions/new" + return post_back(Rack::Request.new(env)) + else + return not_found + end + when 'POST' + case path + when "/sessions/new" + return post_back(Rack::Request.new(env)) + else + return not_found + end + end + not_found + end + + private + + def post_back(request) + params = saml_params_from(request) + saml_request = binding_for(request).deserialize(params) + @builder = nil + url, saml_params = saml_request.response_for( + User.new({ email: "example@example.com" }), + binding: :http_post, + relay_state: params[:RelayState] + ) do |builder| + builder.embed_signature = true + @builder = builder + end + template = <<~ERB + + + + +

Recieved SAML Request

+ + +

Sending SAML Response (IdP -> SP)

+ +
+ <%- saml_params.each do |(key, value)| -%> + + <%- end -%> + +
+ + + ERB + erb = ERB.new(template, nil, trim_mode: '-') + html = erb.result(binding) + [200, { 'Content-Type' => "text/html" }, [html]] + end + + + def not_found + [404, {}, []] + end + + def saml_params_from(request) + if request.post? + { + "SAMLRequest" => request.params["SAMLRequest"], + "RelayState" => request.params["RelayState"], + } + else + query_string = request.query_string + on = query_string.include?("&") ? "&" : "&" + Hash[query_string.split(on).map { |x| x.split("=", 2) }].symbolize_keys + end + end + + def binding_for(request) + location = "http://localhost:8282/sessions/new" + if request.post? + Saml::Kit::Bindings::HttpPost + .new(location: location) + else + Saml::Kit::Bindings::HttpRedirect + .new(location: location) + end + end +end + +if __FILE__ == $0 + app = Rack::Builder.new do + use Rack::Reloader + run IdentityProvider.new + end.to_app + + Rackup::Server.start(app: app, Port: 8282) +end diff --git a/bin/sp b/bin/sp new file mode 100755 index 0000000..68a0e3d --- /dev/null +++ b/bin/sp @@ -0,0 +1,147 @@ +#!/usr/bin/env ruby + +# Start the server by running: +# +# $ ruby main.rb + +require "bundler/inline" +gemfile do + source "https://rubygems.org" + + gem "base64", "~> 0.1" + gem "erb", "~> 4.0" + gem "rack", "~> 3.0" + gem "rackup", "~> 2.0" + gem "saml-kit", "~> 1.0" + gem "webrick", "~> 1.0" +end + +class OnDemandRegistry < Saml::Kit::DefaultRegistry + def metadata_for(entity_id) + found = super(entity_id) + return found if found + + register_url(entity_id, verify_ssl: false) + super(entity_id) + end +end + +Saml::Kit.configure do |x| + x.entity_id = "http://localhost:8283/metadata.xml" + x.registry = OnDemandRegistry.new + x.logger = Logger.new("/dev/stderr") +end + +class ServiceProvider + def initialize + @storage = {} + end + + # Download IDP Metadata + # + # GET /metadata.xml + def metadata + xml = Saml::Kit::Metadata.build_xml do |builder| + builder.embed_signature = false + builder.contact_email = 'hi@example.com' + builder.organization_name = "Acme, Inc" + builder.organization_url = "https://example.com" + builder.build_service_provider do |x| + x.name_id_formats = [Saml::Kit::Namespaces::EMAIL_ADDRESS] + x.add_assertion_consumer_service("http://localhost:8283/assertions", binding: :http_post) + end + end + + [200, { 'Content-Type' => "application/samlmetadata+xml" }, [xml]] + end + + def call(env) + path = env['PATH_INFO'] + case env['REQUEST_METHOD'] + when 'GET' + case path + when "/metadata.xml" + return metadata + else + # TODO Generate a post to the IdP + return post_to_idp(Rack::Request.new(env)) + end + when 'POST' + case path + when "/assertions" + # TODO:: Render the SAMLResponse from the IdP + return assertions(Rack::Request.new(env)) + else + return not_found + end + end + not_found + end + + private + + def not_found + [404, {}, []] + end + + def post_to_idp(request) + idp = Saml::Kit.registry.metadata_for('http://localhost:8282/metadata.xml') + relay_state = Base64.strict_encode64(JSON.generate(redirect_to: '/dashboard')) + + @saml_builder = nil + uri, saml_params = idp.login_request_for(binding: :http_post, relay_state: relay_state) do |builder| + @saml_builder = builder + end + + template = <<~ERB + + + + +

Sending SAML Request (SP -> IdP)

+ + +
+ <%- saml_params.each do |(key, value)| -%> + + <%- end -%> + +
+ + + ERB + erb = ERB.new(template, nil, trim_mode: '-') + html = erb.result(binding) + [200, { 'Content-Type' => "text/html" }, [html]] + end + + def assertions(request) + sp = Saml::Kit.registry.metadata_for('http://localhost:8283/metadata.xml') + saml_binding = sp.assertion_consumer_service_for(binding: :http_post) + saml_response = saml_binding.deserialize(request.params) + raise saml_response.errors unless saml_response.valid? + + template = <<~ERB + + + + +

Received SAML Response

+ + + + ERB + erb = ERB.new(template, nil, trim_mode: '-') + html = erb.result(binding) + [200, { 'Content-Type' => "text/html" }, [html]] + end +end + +if __FILE__ == $0 + app = Rack::Builder.new do + use Rack::Reloader + run ServiceProvider.new + end.to_app + + Rackup::Server.start(app: app, Port: 8283) +end diff --git a/src/idp/.gitignore b/src/idp/.gitignore deleted file mode 100644 index 1d3ed4c..0000000 --- a/src/idp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -config.yml diff --git a/src/idp/README.md b/src/idp/README.md deleted file mode 100644 index a9d8f2a..0000000 --- a/src/idp/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# SAML IdP - -This is a tiny SAML Identity Provider for testing out interactions with -a SAML Service Provider - -## Getting Started - -1. Start the server: - - $ ruby main.rb - -1. Use `http://localhost:8282/metadata.xml` as your SAML IdP Metadata url. diff --git a/src/idp/main.rb b/src/idp/main.rb deleted file mode 100755 index daa15cc..0000000 --- a/src/idp/main.rb +++ /dev/null @@ -1,171 +0,0 @@ -#!/usr/bin/env ruby - -# Start the server by running: -# -# $ ruby main.rb - -require "bundler/inline" -gemfile do - source "https://rubygems.org" - - gem "erb", "~> 4.0" - gem "rack", "~> 3.0" - gem "rackup", "~> 2.0" - gem "saml-kit", "~> 1.0" - gem "webrick", "~> 1.0" -end - -class User - def initialize(attributes) - @attributes = attributes - end - - def name_id_for(name_id_format) - @attributes[:email] - end - - def assertion_attributes_for(request) - { - custom: 'custom attribute' - } - end -end - -class OnDemandRegistry < Saml::Kit::DefaultRegistry - def metadata_for(entity_id) - found = super(entity_id) - return found if found - - register_url(entity_id, verify_ssl: false) - super(entity_id) - end -end - -Saml::Kit.configure do |x| - x.entity_id = "http://localhost:8282/metadata.xml" - x.registry = OnDemandRegistry.new - x.logger = Logger.new("/dev/stderr") -end - -class IdentityProvider - def initialize - @storage = {} - end - - # Download IDP Metadata - # - # GET /metadata.xml - def metadata - xml = Saml::Kit::Metadata.build_xml do |builder| - builder.contact_email = 'hi@example.com' - builder.organization_name = "Acme, Inc" - builder.organization_url = "https://example.com" - builder.build_identity_provider do |x| - x.add_single_sign_on_service("http://localhost:8282/sessions/new", binding: :http_post) - x.name_id_formats = [Saml::Kit::Namespaces::EMAIL_ADDRESS] - x.attributes << :Username - end - end - - [200, { 'Content-Type' => "application/samlmetadata+xml" }, [xml]] - end - - def call(env) - path = env['PATH_INFO'] - case env['REQUEST_METHOD'] - when 'GET' - case path - when "/metadata.xml" - return metadata - when "/sessions/new" - return post_back(Rack::Request.new(env)) - else - return not_found - end - when 'POST' - case path - when "/sessions/new" - return post_back(Rack::Request.new(env)) - else - return not_found - end - end - not_found - end - - private - - def post_back(request) - params = saml_params_from(request) - saml_request = binding_for(request).deserialize(params) - @builder = nil - url, saml_params = saml_request.response_for( - User.new({ email: "example@example.com" }), - binding: :http_post, - relay_state: params[:RelayState] - ) do |builder| - builder.embed_signature = true - @builder = builder - end - template = <<~ERB - - - - -

Recieved SAML Request

- - -

Sending SAML Response (IdP -> SP)

- -
- <%- saml_params.each do |(key, value)| -%> - - <%- end -%> - -
- - - ERB - erb = ERB.new(template, nil, trim_mode: '-') - html = erb.result(binding) - [200, { 'Content-Type' => "text/html" }, [html]] - end - - - def not_found - [404, {}, []] - end - - def saml_params_from(request) - if request.post? - { - "SAMLRequest" => request.params["SAMLRequest"], - "RelayState" => request.params["RelayState"], - } - else - query_string = request.query_string - on = query_string.include?("&") ? "&" : "&" - Hash[query_string.split(on).map { |x| x.split("=", 2) }].symbolize_keys - end - end - - def binding_for(request) - location = "http://localhost:8282/sessions/new" - if request.post? - Saml::Kit::Bindings::HttpPost - .new(location: location) - else - Saml::Kit::Bindings::HttpRedirect - .new(location: location) - end - end -end - -if __FILE__ == $0 - app = Rack::Builder.new do - use Rack::Reloader - run IdentityProvider.new - end.to_app - - Rackup::Server.start(app: app, Port: 8282) -end diff --git a/src/sp/README.md b/src/sp/README.md deleted file mode 100644 index c7894a6..0000000 --- a/src/sp/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# SAML SP - -This is a tiny SAML Service Provider for testing out interactions with -a SAML Identity Provider (IdP) - -## Getting Started - -1. Start the server: - - $ ruby main.rb - -1. Start ngrok - - $ ngrok http 8283 - -1. Use `https://.ngrok.io/metadata.xml` as your SAML SP Metadata url. diff --git a/src/sp/main.rb b/src/sp/main.rb deleted file mode 100644 index 68a0e3d..0000000 --- a/src/sp/main.rb +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/env ruby - -# Start the server by running: -# -# $ ruby main.rb - -require "bundler/inline" -gemfile do - source "https://rubygems.org" - - gem "base64", "~> 0.1" - gem "erb", "~> 4.0" - gem "rack", "~> 3.0" - gem "rackup", "~> 2.0" - gem "saml-kit", "~> 1.0" - gem "webrick", "~> 1.0" -end - -class OnDemandRegistry < Saml::Kit::DefaultRegistry - def metadata_for(entity_id) - found = super(entity_id) - return found if found - - register_url(entity_id, verify_ssl: false) - super(entity_id) - end -end - -Saml::Kit.configure do |x| - x.entity_id = "http://localhost:8283/metadata.xml" - x.registry = OnDemandRegistry.new - x.logger = Logger.new("/dev/stderr") -end - -class ServiceProvider - def initialize - @storage = {} - end - - # Download IDP Metadata - # - # GET /metadata.xml - def metadata - xml = Saml::Kit::Metadata.build_xml do |builder| - builder.embed_signature = false - builder.contact_email = 'hi@example.com' - builder.organization_name = "Acme, Inc" - builder.organization_url = "https://example.com" - builder.build_service_provider do |x| - x.name_id_formats = [Saml::Kit::Namespaces::EMAIL_ADDRESS] - x.add_assertion_consumer_service("http://localhost:8283/assertions", binding: :http_post) - end - end - - [200, { 'Content-Type' => "application/samlmetadata+xml" }, [xml]] - end - - def call(env) - path = env['PATH_INFO'] - case env['REQUEST_METHOD'] - when 'GET' - case path - when "/metadata.xml" - return metadata - else - # TODO Generate a post to the IdP - return post_to_idp(Rack::Request.new(env)) - end - when 'POST' - case path - when "/assertions" - # TODO:: Render the SAMLResponse from the IdP - return assertions(Rack::Request.new(env)) - else - return not_found - end - end - not_found - end - - private - - def not_found - [404, {}, []] - end - - def post_to_idp(request) - idp = Saml::Kit.registry.metadata_for('http://localhost:8282/metadata.xml') - relay_state = Base64.strict_encode64(JSON.generate(redirect_to: '/dashboard')) - - @saml_builder = nil - uri, saml_params = idp.login_request_for(binding: :http_post, relay_state: relay_state) do |builder| - @saml_builder = builder - end - - template = <<~ERB - - - - -

Sending SAML Request (SP -> IdP)

- - -
- <%- saml_params.each do |(key, value)| -%> - - <%- end -%> - -
- - - ERB - erb = ERB.new(template, nil, trim_mode: '-') - html = erb.result(binding) - [200, { 'Content-Type' => "text/html" }, [html]] - end - - def assertions(request) - sp = Saml::Kit.registry.metadata_for('http://localhost:8283/metadata.xml') - saml_binding = sp.assertion_consumer_service_for(binding: :http_post) - saml_response = saml_binding.deserialize(request.params) - raise saml_response.errors unless saml_response.valid? - - template = <<~ERB - - - - -

Received SAML Response

- - - - ERB - erb = ERB.new(template, nil, trim_mode: '-') - html = erb.result(binding) - [200, { 'Content-Type' => "text/html" }, [html]] - end -end - -if __FILE__ == $0 - app = Rack::Builder.new do - use Rack::Reloader - run ServiceProvider.new - end.to_app - - Rackup::Server.start(app: app, Port: 8283) -end -- cgit v1.2.3