class Heroku::Bouncer::Middleware
Constants
- DEFAULT_LOGIN_PATH
- DecryptedHash
Encapsulates encrypting and decrypting a hash of data. Does not store the key that is passed in.
- UnableToFetchUserError
Attributes
login_path[R]
Public Class Methods
new(app, options = {})
click to toggle source
Calls superclass method
# File lib/heroku/bouncer/middleware.rb, line 18 def initialize(app, options = {}) if options[:disabled] @app = app @disabled = true # super is not called; we're not using sinatra if we're disabled else super(app) @disabled = false @cookie_secret = extract_option(options, :secret, SecureRandom.hex(64)) @allow_if_user = extract_option(options, :allow_if_user, nil) @login_path = extract_option(options, :login_path, DEFAULT_LOGIN_PATH) @redirect_url = extract_option(options, :redirect_url, 'https://www.heroku.com') # backwards-compatibilty for `herokai_only`: # * check email for ending with `@heroku.com` # * The redirect URL can be passed as a string value to `herokai_only` herokai_only = extract_deprecated_option("please use `allow_if_user` instead", options, :herokai_only, false) if herokai_only if herokai_only.is_a?(String) && !options[:redirect_url] @redirect_url = herokai_only end @allow_if_user ||= lambda { |user| user['email'].end_with?("@heroku.com") } end # backwards-compatibility for allow_if allow_if = extract_option(options, :allow_if, false) if allow_if @allow_if_user ||= lambda { |user| allow_if.call(user['email']) } end @expose_token = extract_option(options, :expose_token, false) @expose_email = extract_option(options, :expose_email, true) @expose_user = extract_option(options, :expose_user, true) @session_sync_nonce = extract_option(options, :session_sync_nonce, nil) @allow_anonymous = extract_option(options, :allow_anonymous, nil) @skip = extract_option(options, :skip, false) end end
Public Instance Methods
call(env)
click to toggle source
Calls superclass method
# File lib/heroku/bouncer/middleware.rb, line 57 def call(env) if @disabled || skip?(env) @app.call(env) else unlock_session_data(env) do super(env) end end end
Private Instance Methods
anonymous_request_allowed?()
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 194 def anonymous_request_allowed? auth_request? || (@allow_anonymous && @allow_anonymous.call(request)) end
auth_paths()
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 170 def auth_paths @auth_paths ||= [ "/auth/heroku/callback", "/auth/heroku", "/auth/failure", "/auth/sso-logout", "/auth/logout", DEFAULT_LOGIN_PATH, login_path ].compact.freeze end
auth_request?()
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 182 def auth_request? auth_paths.include?(request.path_info) end
custom_login_path?()
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 203 def custom_login_path? login_path != DEFAULT_LOGIN_PATH end
decrypt_store(env)
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 243 def decrypt_store(env) env["rack.session"][:bouncer] = DecryptedHash.unlock(env["rack.session"][:bouncer], @cookie_secret) end
destroy_session()
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 271 def destroy_session store.keys.each { |k| store_delete(k) } end
encrypt_store(env)
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 248 def encrypt_store(env) if env["rack.session"].key?(:bouncer) env["rack.session"][:bouncer] = env["rack.session"][:bouncer].lock(@cookie_secret) end end
enforce_host(scheme, host, port, url)
click to toggle source
Prevent open redirect vulnerabilities by setting the current host
# File lib/heroku/bouncer/middleware.rb, line 282 def enforce_host(scheme, host, port, url) return_to = URI.parse(url) rescue '/' return_to.scheme = scheme return_to.host = host return_to.port = port unless port == 80 return_to.to_s end
expired?()
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 198 def expired? ts = store_read(:expires_at) ts.nil? || Time.now.to_i > ts end
expose_store()
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 275 def expose_store store.each_pair do |key, value| request.env["bouncer.#{key}"] = value end end
extract_deprecated_option(warning, options, option, default = nil)
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 220 def extract_deprecated_option(warning, options, option, default = nil) $stderr.puts "[warn] heroku-bouncer: `#{option}` option is deprecated: #{warning}" if options.has_key?(option) extract_option(options, option, default) end
extract_option(options, option, default = nil)
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 216 def extract_option(options, option, default = nil) options.fetch(option, default) end
fetch_user(token, retries = 3)
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 225 def fetch_user(token, retries = 3) response = ::Faraday.new(ENV["HEROKU_API_URL"] || "https://api.heroku.com/").get('/account') do |r| r.headers['Accept'] = 'application/vnd.heroku+json; version=3' r.headers['Authorization'] = "Bearer #{token}" end if response.status == 200 ::Heroku::Bouncer::JsonParser.call(response.body) elsif retries > 0 sleep(0.1) fetch_user(token, retries - 1) else raise UnableToFetchUserError end rescue ::Faraday::ClientError, ::Heroku::Bouncer::JsonParserError raise UnableToFetchUserError end
require_authentication()
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 211 def require_authentication store_write(:return_to, request.url) redirect to(login_path) end
session_nonce_mismatch?()
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 186 def session_nonce_mismatch? @session_sync_nonce && (store_read(@session_sync_nonce.to_sym).to_s != session_nonce_cookie.to_s) && !auth_request? end
skip?(env)
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 207 def skip?(env) @skip && @skip.call(env) end
store()
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 255 def store session[:bouncer] end
store_delete(key)
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 267 def store_delete(key) store.delete(key) end
store_read(key)
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 263 def store_read(key) store.fetch(key, nil) end
store_write(key, value)
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 259 def store_write(key, value) store[key] = value end
unlock_session_data(env) { || ... }
click to toggle source
# File lib/heroku/bouncer/middleware.rb, line 163 def unlock_session_data(env, &block) decrypt_store(env) yield ensure encrypt_store(env) end