require_relative 'whitelisting_middleware' module Stitches # A middleware that requires an API key for certain transactions, and makes its id available # in the enviornment for controllers. # # This follows http://www.ietf.org/rfc/rfc2617.txt for use of custom authorization methods, namely # the specification of an API key. # # Apps are expected to set the Authorization header (available to Rack apps as the environment # variable HTTP_AUTHORIZATION) to # # MyInternalRealm key=<> # # where MyInternalRealm is the value returned by Stitches.configuration.custom_http_auth_scheme and # <> is the UUID provided to the caller. It's expected that there is an entry # in the API_CLIENTS table with this value for "key". # # If that is the case, env[Stitches.configuration.env_var_to_hold_api_client_primary_key] will be the primary key of the # ApiClient that it maps to. class ApiKey < Stitches::WhitelistingMiddleware def initialize(app,options = {}) super(app,options) @realm = Rails.application.class.parent.to_s end protected def do_call(env) authorization = env["HTTP_AUTHORIZATION"] if authorization if authorization =~ /#{@configuration.custom_http_auth_scheme}\s+key=(.*)\s*$/ key = $1 if ApiClient.column_names.include?("enabled") client = ApiClient.where(key: key, enabled: true).first else ActiveSupport::Deprecation.warn('api_keys is missing "enabled" column. Run "rails g stitches:add_enabled_to_api_clients"') client = ApiClient.where(key: key).first end if client.present? env[@configuration.env_var_to_hold_api_client_primary_key] = client.id env[@configuration.env_var_to_hold_api_client] = client @app.call(env) else UnauthorizedResponse.new("key invalid",@realm,@configuration.custom_http_auth_scheme) end else UnauthorizedResponse.new("bad authorization type",@realm,@configuration.custom_http_auth_scheme) end else UnauthorizedResponse.new("no authorization header",@realm,@configuration.custom_http_auth_scheme) end end private class UnauthorizedResponse < Rack::Response def initialize(reason,realm,custom_http_auth_scheme) super("Unauthorized - #{reason}", 401, { "WWW-Authenticate" => "#{custom_http_auth_scheme} realm=#{realm}" }) end end end end