blob: cfa52c49dc35758e1c5d316d4c7276abd8c21607 [file] [log] [blame]
require_relative '../asf.rb'
require 'rack'
require 'etc'
require 'thread'
module ASF
module Auth
DIRECTORS = {
'rbowen' => 'rb',
'curcuru' => 'sc',
'bdelacretaz' => 'bd',
'jim' => 'jj',
'mattmann' => 'cm',
'ke4qqq' => 'dn',
'brett' => 'bp',
'rubys' => 'sr',
'gstein' => 'gs'
}
# decode HTTP authorization, when present
def self.decode(env)
class << env; attr_accessor :user, :password; end
auth = env['HTTP_AUTHORIZATION'] || ENV['HTTP_AUTHORIZATION']
if auth.to_s.empty?
env.user = env['REMOTE_USER'] || ENV['USER'] || Etc.getpwuid.name
else
require 'base64'
env.user, env.password = Base64.
decode64(auth[/^Basic ([A-Za-z0-9+\/=]+)$/,1].to_s).split(':',2)
end
env['REMOTE_USER'] ||= env.user
ASF::Person.new(env.user)
end
# 'use' the following class in config.ru to limit access
# to the application to ASF committers
class Committers < Rack::Auth::Basic
def initialize(app)
super(app, "ASF Committers", &proc {})
end
def call(env)
authorized = ( ENV['RACK_ENV'] == 'test' )
authorized ||= ASF::Auth.decode(env).asf_committer?
if authorized
@app.call(env)
else
unauthorized
end
end
end
# 'use' the following class in config.ru to limit access
# to the application to ASF members and officers and the accounting group.
class MembersAndOfficers < Rack::Auth::Basic
def initialize(app)
super(app, "ASF Members and Officers", &proc {})
end
def call(env)
authorized = ( ENV['RACK_ENV'] == 'test' )
person = ASF::Auth.decode(env)
authorized ||= DIRECTORS[env.user]
authorized ||= person.asf_member?
authorized ||= ASF.pmc_chairs.include? person
if not authorized
accounting = ASF::Authorization.new('pit').
find {|group, list| group=='accounting'}
authorized = (accounting and accounting.last.include? env.user)
end
if authorized
@app.call(env)
else
unauthorized
end
end
end
end
# 'use' the following class in config.ru to automatically run
# Garbage Collection every 'n' requests, or 'm' minutes.
#
# This tries to run garbage collection "out of band" (i.e., between
# requests), and when other requests are active (which can happen
# with threaded servers like Puma).
#
# In addition to keeping memory usage bounded, this keeps the LDAP
# cache from going stale.
#
class AutoGC
@@background = nil
def initialize(app, frequency=100, minutes=15)
@app = app
@frequency = frequency
@request_count = 0
@queue = Queue.new
@mutex = Mutex.new
if defined?(PhusionPassenger)
# https://github.com/suyccom/yousell/blob/master/config.ru
# https://www.phusionpassenger.com/library/indepth/ruby/out_of_band_work.html
if PhusionPassenger.respond_to?(:require_passenger_lib)
PhusionPassenger.require_passenger_lib 'rack/out_of_band_gc'
else
# Phusion Passenger < 4.0.33
require 'phusion_passenger/rack/out_of_band_gc'
end
@passenger = PhusionPassenger::Rack::OutOfBandGc.new(app, frequency)
end
Thread.kill(@@background) if @@background
if minutes
# divide minutes by frequency and use the result to determine the
# time between simulated requests
@@background = Thread.new do
seconds = minutes * 60.0 / frequency
loop do
sleep seconds
maybe_perform_gc
end
end
end
end
def call(env)
@queue.push 1
if @passenger
@passenger.call(env)
else
# https://github.com/puma/puma/issues/450
status, header, body = @app.call(env)
if ary = env['rack.after_reply']
ary << lambda {maybe_perform_gc}
else
Thread.new {sleep 0.1; maybe_perform_gc}
end
[status, header, body]
end
ensure
@queue.pop
end
def maybe_perform_gc
@mutex.synchronize do
@request_count += 1
if @queue.empty? and @request_count >= @frequency
@request_count = 0
disabled = GC.enable
GC.start
GC.disable if disabled
end
end
end
end
# Apache httpd on whimsy-vm is behind a proxy that converts https
# requests into http requests. Update the environment variables to
# match.
class HTTPS_workarounds
def initialize(app)
@app = app
end
def call(env)
if env['HTTPS'] == 'on'
env['SCRIPT_URI'].sub!(/^http:/, 'https:')
env['SERVER_PORT'] = '443'
# for reasons I don't understand, Passenger on whimsy doesn't
# forward root directory requests directly, so as a workaround
# these requests are rewritten and the following code maps
# the requests back:
if env['PATH_INFO'] == '/index.html'
env['PATH_INFO'] = '/'
env['SCRIPT_URI'] += '/'
end
end
return @app.call(env)
end
end
end