From c583bcd1473205104a1e1af812ed4976d30c7baa Mon Sep 17 00:00:00 2001 From: mo khan Date: Fri, 2 May 2025 14:29:41 -0600 Subject: refactor: remove anything unrelated to the authz daemon --- bin/ui | 503 ----------------------------------------------------------------- 1 file changed, 503 deletions(-) delete mode 100755 bin/ui (limited to 'bin/ui') diff --git a/bin/ui b/bin/ui deleted file mode 100755 index 0c1e4c4c..00000000 --- a/bin/ui +++ /dev/null @@ -1,503 +0,0 @@ -#!/usr/bin/env ruby - -require "bundler/inline" - -gemfile do - source "https://rubygems.org" - - gem "base64", "~> 0.1" - gem "erb", "~> 4.0" - gem "net-hippie", "~> 1.0" - gem "rack", "~> 3.0" - gem "rack-session", "~> 2.0" - gem "rackup", "~> 2.0" - gem "saml-kit", "1.4.0" - gem "webrick", "~> 1.0" -end - -$scheme = ENV.fetch("SCHEME", "http") -$port = ENV.fetch("PORT", 8283).to_i -$host = ENV.fetch("HOST", "localhost:#{$port}") -$idp_host = ENV.fetch("IDP_HOST", "localhost:8282") - -Net::Hippie.logger = Logger.new($stdout, level: :debug) - -class OnDemandRegistry < Saml::Kit::DefaultRegistry - def metadata_for(entity_id) - found = super(entity_id) - return found if found - - register_url(entity_id, verify_ssl: false) - super(entity_id) - end -end - -Saml::Kit.configure do |x| - x.entity_id = "#{$scheme}://#{$host}/saml/metadata.xml" - x.registry = OnDemandRegistry.new - x.logger = Logger.new("/dev/stderr") -end - -module OAuth - class Client - attr_reader :client_id, :client_secret, :http, :authz_host - - def initialize(authz_host, client_id, client_secret) - @authz_host = authz_host - @client_id = client_id - @client_secret = client_secret - @http = Net::Hippie::Client.new(headers: ::Net::Hippie::Client::DEFAULT_HEADERS.merge({ - 'Authorization' => Net::Hippie.basic_auth(client_id, client_secret), - })) - end - - def [](key) - server_metadata.fetch(key) - end - - def authorize_uri(redirect_uri:, state: SecureRandom.uuid, response_type: "code", response_mode: "query", scope: "openid") - [ - self[:authorization_endpoint], - to_query( - client_id: client_id, - state: state, - redirect_uri: redirect_uri, - response_mode: response_mode, - response_type: response_type, - scope: scope, - ) - ].join("?") - end - - def exchange(grant_type, params = {}) - with_http do |client| - client.post(self[:token_endpoint], body: body_for(grant_type, params)) - end - end - - private - - def body_for(grant_type, params) - case grant_type - when "authorization_code" - { - grant_type: grant_type, - code: params.fetch(:code), - code_verifier: params.fetch(:code_verifier, "not_implemented"), - } - when "urn:ietf:params:oauth:grant-type:saml2-bearer" - { - grant_type: grant_type, - assertion: params.fetch(:assertion), - } - else - raise NotImplementedError.new(grant_type) - end - end - - def to_query(params = {}) - params.map do |(key, value)| - [key, value].join("=") - end.join("&") - end - - def with_http - http.with_retry do |client| - yield client - end - end - - def server_metadata - @server_metadata ||= - with_http do |client| - response = client.get("http://#{authz_host}/.well-known/oauth-authorization-server") - JSON.parse(response.body, symbolize_names: true) - end - end - end -end - -module HTTPHelpers - def current_user?(request) - request.session[:access_token] - end - - def not_found - [404, { 'X-Backend-Server' => 'UI' }, []] - end - - def redirect_to(location) - if location.start_with?("http") - [302, { 'Location' => location }, []] - else - [302, { 'Location' => "#{$scheme}://#{$host}#{location}" }, []] - end - end - - def with_layout(bind) - template = <<~ERB - - - - UI - - - - - - - -
- #{yield} -
- - - ERB - ERB.new(template, trim_mode: '-').result(bind) - end -end - -class UI - include ::HTTPHelpers - - attr_reader :oauth_client - - def initialize(oauth_client) - @oauth_client = oauth_client - end - - def call(env) - request = Rack::Request.new(env) - case request.request_method - when Rack::GET - case request.path - when "/index.html" - return get_index(request) - when "/groups.html" - if current_user?(request) - return get_groups(request) - else - return redirect_to("/oidc/new") - end - when /\A\/groups\/\d+\/projects.html\z/ - if current_user?(request) - return get_projects(request) - else - return redirect_to("/oidc/new") - end - when "/oauth/callback" - return oauth_callback(Rack::Request.new(env)) - when "/oidc/new" - return redirect_to(oauth_client.authorize_uri( - redirect_uri: "#{request.base_url}/oauth/callback" - )) - when "/saml/metadata.xml" - return metadata - when "/saml/new" - return saml_post_to_idp(Rack::Request.new(env)) - else - return redirect_to("/index.html") - end - when Rack::POST - case request.path - when "/logout" - request.session.delete(:access_token) - request.session.delete(:id_token) - request.session.delete(:refresh_token) - return redirect_to("/") - when "/saml/assertions" - return saml_assertions(Rack::Request.new(env)) - else - return not_found - end - end - not_found - end - - private - - def metadata - xml = Saml::Kit::Metadata.build_xml do |builder| - builder.embed_signature = false - builder.contact_email = 'ui@example.com' - builder.organization_name = "Acme, Inc" - builder.organization_url = "https://example.com" - builder.build_service_provider do |x| - x.name_id_formats = [Saml::Kit::Namespaces::PERSISTENT] - x.add_assertion_consumer_service("#{$scheme}://#{$host}/saml/assertions", binding: :http_post) - end - end - - [200, { 'Content-Type' => "application/samlmetadata+xml" }, [xml]] - end - - def get_index(request) - html = with_layout(binding) do - <<~ERB - <%- if current_user?(request) -%> - -

Access Token

-
<%= request.session[:access_token] %>
- -

ID Token

-
<%= request.session[:id_token] %>
- <%- else -%> - - <%- end -%> - ERB - end - [200, { "Content-Type" => "text/html" }, [html]] - end - - def oauth_callback(request) - response = oauth_client.exchange("authorization_code", code: request.params['code']) - if response.code == "200" - tokens = JSON.parse(response.body, symbolize_names: true) - request.session[:access_token] = tokens[:access_token] - request.session[:id_token] = tokens[:id_token] - request.session[:refresh_token] = tokens[:access_token] - - html = with_layout(binding) do - <<~ERB - -

Access Token

-
<%= response.body %>
-
<%= JSON.pretty_generate(request.session[:access_token]) %>
- ERB - end - [200, { 'Content-Type' => "text/html" }, [html]] - else - [response.code, response.header, [response.body]] - end - end - - def get_groups(request) - http = Net::Hippie::Client.new(headers: ::Net::Hippie::Client::DEFAULT_HEADERS.merge({ - 'Authorization' => Net::Hippie.bearer_auth(request.session[:access_token]) - })) - - response = http.get("http://api.example.com:8080/groups.json") - if response.code == "200" - groups = JSON.parse(response.body, symbolize_names: true) - html = with_layout(binding) do - <<~ERB - - -

Groups

- - - - - - - - - - - - <%- groups.each do |group| -%> - - - - - - - - <%- end -%> - -
IDNameOrganization IDParent ID 
<%= group[:id] %><%= group[:name] %><%= group[:organization_id] %><%= group[:parent_id] %>Projects
- ERB - end - [200, { 'Content-Type' => "text/html" }, [html]] - else - [response.code, response.header, [response.body]] - end - end - - def get_projects(request) - http = Net::Hippie::Client.new(headers: ::Net::Hippie::Client::DEFAULT_HEADERS.merge({ - 'Authorization' => Net::Hippie.bearer_auth(request.session[:access_token]) - })) - - response = http.get("http://api.example.com:8080/projects.json") - if response.code == "200" - projects = JSON.parse(response.body, symbolize_names: true) - - html = with_layout(binding) do - <<~ERB - - -

Projects

- - - - - - - - - <%- projects.each do |project| -%> - - - - - <%- end -%> - -
NameGroup ID
<%= project[:name] %><%= project[:group_id] %>
- ERB - end - [200, { 'Content-Type' => "text/html" }, [html]] - else - [response.code, response.header, [response.body]] - end - end - - def saml_post_to_idp(request) - idp = Saml::Kit.registry.metadata_for("http://#{$idp_host}/saml/metadata.xml") - relay_state = Base64.strict_encode64(JSON.generate(redirect_to: '/dashboard')) - - @saml_builder = nil - uri, saml_params = idp.login_request_for(binding: :http_post, relay_state: relay_state) do |builder| - @saml_builder = builder - end - - html = with_layout(binding) do - <<~ERB -

Sending SAML Request (SP -> IdP)

- - -
- <%- saml_params.each do |(key, value)| -%> - - <%- end -%> - -
- ERB - end - [200, { 'Content-Type' => "text/html" }, [html]] - end - - def saml_assertions(request) - sp = Saml::Kit.registry.metadata_for("#{request.base_url}/saml/metadata.xml") - saml_binding = sp.assertion_consumer_service_for(binding: :http_post) - saml_response = saml_binding.deserialize(request.params) - raise saml_response.errors unless saml_response.valid? - - assertion = Base64.strict_encode64(saml_response.assertion.to_xml) - response = oauth_client.exchange( - "urn:ietf:params:oauth:grant-type:saml2-bearer", - assertion: assertion, - ) - if response.code == "200" - tokens = JSON.parse(response.body, symbolize_names: true) - request.session[:access_token] = tokens[:access_token] - request.session[:refresh_token] = tokens[:access_token] - - html = with_layout(binding) do - <<~ERB - - -

Received SAML Response

- - - - - ERB - end - [200, { 'Content-Type' => "text/html" }, [html]] - else - [response.code, response.header, [response.body]] - end - end -end - -if __FILE__ == $0 - app = Rack::Builder.new do - use Rack::CommonLogger - use Rack::Reloader - use Rack::Session::Cookie, { domain: $host.split(":", 2)[0], path: "/", secret: SecureRandom.hex(64) } - - run UI.new(::OAuth::Client.new($idp_host, 'client_id', 'client_secret')) - end.to_app - - Rackup::Server.start(app: app, Port: $port) -end -- cgit v1.2.3