module RocketIO class Controller # easily restrict access to controller using basic auth # # @example protect all request methods # basic_auth do |user,pass| # user == 'admin' && pass == 'super secret password' # end # # @example protect only POST, PUT and DELETE request methods # basic_auth :post, :put, :delete do |user,pass| # user == 'admin' && pass == 'super secret password' # end # # @example use different credentials for GET and POST # basic_auth :get do |user,pass| # user == 'reader' && pass == 'readPass' # end # # basic_auth :post do |user,pass| # user == 'poster' && pass == 'writePass' # end # # @note authorization is composable, that's it, if superclass is protecting :get method # and current controller protects :post method, # both :get and :post will be protected in current controller # # @params *args [Array] # @param block [Proc] # def self.basic_auth *args, &block opts = args.last.is_a?(Hash) ? args.pop : {} rqms = args.any? ? args.map!(&:to_sym) : RocketIO::REQUEST_METHODS.values rqms.each do |rm| (@__basic_auth__ ||= {})[rm] = { class: Rack::Auth::Basic, arguments: [opts[:realm] || RocketIO::DEFAULT_AUTH_REALM].freeze, block: block, mock: RocketIO::HTTP_AUTHORIZATION_MOCKS[:basic] }.freeze end define_basic_auth_methods end def self.define_basic_auth_methods source = self prompts = (source.instance_variable_get(:@__basic_auth__) || {}).each_with_object(allocate.basic_auth.dup) do |(rm,p),o| method = :"__basic_auth__#{rm}__" private_api << define_method(method, &p[:block]) o[rm] = p.merge(method: method).freeze end.freeze return if prompts.empty? private_api << define_method(:basic_auth) {prompts} end def basic_auth; RocketIO::EMPTY_HASH end # easily restrict access to controller using digest auth # # @example protect all request methods using hashed passwords # # hash the password somewhere in irb: # # ::Digest::MD5.hexdigest 'admin:AccessRestricted:somePassword' # # username ^ realm ^ password ^ # # #=> 9d77d54decc22cdcfb670b7b79ee0ef0 # # digest_auth :passwords_hashed => true, :realm => 'AccessRestricted' do |user| # {'admin' => '9d77d54decc22cdcfb670b7b79ee0ef0'}[user] # end # # @example protect all request methods using plain passwords # digest_auth do |user| # {'admin' => 'password'}[user] # end # # @example protect only POST, PUT and DELETE request methods # digest_auth :post, :put, :delete do |user| # {'admin' => 'password'}[user] # end # # @example use different credentials for GET and POST # digest_auth :get do |user| # {'user' => 'readPass'}[user] # end # # digest_auth :post do |user| # {'poster' => 'writePass'}[user] # end # # @params *args [Array] # @param block [Proc] # def self.digest_auth *args, &block opts = args.last.is_a?(Hash) ? args.pop : {} opts[:realm] ||= RocketIO::DEFAULT_AUTH_REALM opts[:opaque] ||= opts[:realm] rqms = args.any? ? args.map!(&:to_sym) : RocketIO::REQUEST_METHODS.values rqms.each do |rm| (@__digest_auth__ ||= {})[rm] = { class: Rack::Auth::Digest::MD5, arguments: [opts].freeze, block: block, mock: RocketIO::HTTP_AUTHORIZATION_MOCKS[:digest] }.freeze end define_digest_auth_methods end def self.define_digest_auth_methods source = self prompts = (source.instance_variable_get(:@__digest_auth__) || {}).each_with_object(allocate.digest_auth.dup) do |(rm,p),o| method = :"__digest_auth__#{rm}__" private_api << define_method(method, &p[:block]) o[rm] = p.merge(method: method).freeze end.freeze return if prompts.empty? private_api << define_method(:digest_auth) {prompts} end def digest_auth; RocketIO::EMPTY_HASH end def user?; env[RocketIO::REMOTE_USER] end # checks whether authentication is required and # send an authorization request if credentials not present or invalid def validate_or_request_authentication_if_needed return unless auth = digest_auth[requested_method] || basic_auth[requested_method] return unless prompt = auth[:class].new(proc {}, *auth[:arguments]) do |*a| self.__send__(auth[:method], *a) end.call( if RocketIO::HTTP_AUTHORIZATION_KEYS.detect {|key| env.has_key?(key)} env else env.merge(RocketIO::HTTP_AUTHORIZATION_KEYS.first => auth[:mock]) end ) throw(:__response__, prompt) end end end