require 'orange-core/middleware/base' require 'rack/openid' require 'openid_dm_store' module Orange::Middleware # This middleware locks down entire contexts and puts them behind an openid # login system. Currently only supports a single user id. # # class AccessControl < Base # Sets up the options for the middleware # @param [Hash] opts hash of options # @option opts [Boolean] :openid Whether to use openid logins or not (currently only option) # @option opts [Boolean] :handle_login Whether the access control system should handle # presenting the login form, or let other parts of the app do that. # @option opts [Boolean] :config_id Whether to use the id set in a config file def init(opts = {}) defs = {:locked => [:preview, :admin, :orange], :login => '/login', :logout => '/logout', :handle_login => true, :openid => true, :single_user => false} opts = opts.with_defaults!(defs) @openid = opts[:openid] @locked = opts[:locked] @login = opts[:login] @logout = opts[:logout] @handle = opts[:handle_login] @single = opts[:single_user] unless @single orange.load(Orange::UserResource.new, :users) end end def packet_call(packet) packet['user.id'] ||= (packet.session['user.id'] || false) packet['openid.profile'] ||= (packet.session['openid.profile'] || false) packet['user'] = orange[:users].user_for(packet) unless packet['user.id'].blank? if @openid && need_to_handle?(packet) ret = handle_openid(packet) return ret unless ret.blank? # unless handle_openid returns false, exit immediately end unless access_allowed?(packet) if packet['user'] && packet['route.context'] == :admin # User doesn't have permissions, try to get to a site that he does site = packet['user'].orange_sites.first if site.class.to_s == "OrangeSubsite" subsite = orange[:sitemap].url_for(packet, {:resource => 'subsites', :resource_id => site.id}) packet.reroute("/admin#{subsite}#{packet['route.path'].gsub(/^\//, '')}") end end packet.flash['user.after_login'] = packet.request.path packet.reroute(@login) end pass packet end def access_allowed?(packet) return true unless @locked.include?(packet['route.context']) if packet['user.id'] || orange.options['main_user'].blank? if @single return true if(main_user?(packet)) # Current id no good. packet['user.id'] = nil packet.session['user.id'] = nil return false # Main_user can always log in (root access) elsif main_user?(packet) orange[:users].new(packet, :open_id => packet['user.id'], :name => 'Main User') unless packet['user', false] return true else return orange[:users].access_allowed?(packet, packet['user.id']) end else return false end end def main_user?(packet) id = packet['user.id'].gsub(/^https?:\/\//, '').gsub(/\/$/, '') users = orange.options['main_users'] || [] users = users.dup.push(orange.options['main_user']) users = users.flatten.compact matches = users.select{|user| (user == id) || (id == user.gsub(/^https?:\/\//, '').gsub(/\/$/, '')) } matches.length > 0 ? true : false end def need_to_handle?(packet) @handle && ([@login, @logout].include? packet.request.path.gsub(/\/$/, '')) end def handle_openid(packet) if packet.request.path.gsub(/\/$/, '') == @logout packet.session['user.id'] = nil packet['user.id'] = nil after = packet.flash('user.after_login') || '/' packet.reroute(after) return false end packet.reroute('/') if packet['user.id'] # Reroute to index if we're logged in. # If login set if packet.request.post? packet['template.disable'] = true # Check for openid response if resp = packet.env["rack.openid.response"] if resp.status == :success packet['user.id'] = resp.identity_url packet['user.openid.url'] = resp.identity_url packet['user.openid.response'] = resp # Load in any registration data gathered profile_data = {} # merge the SReg data and the AX data into a single hash of profile data [ OpenID::SReg::Response, OpenID::AX::FetchResponse ].each do |data_response| if data_response.from_success_response( resp ) profile_data.merge! data_response.from_success_response( resp ).data end end packet.session['openid.profile'] = profile_data packet['openid.profile'] = profile_data if packet['user.id'] =~ /^https?:\/\/(www.)?google.com\/accounts/ packet['user.id'] = profile_data["http://axschema.org/contact/email"] packet['user.id'] = packet['user.id'].first if packet['user.id'].kind_of?(Array) end if packet['user.id'] =~ /^https?:\/\/(www.)?yahoo.com/ || packet['user.id'] =~ /^https?:\/\/(my\.|me\.)?yahoo.com/ packet['user.id'] = profile_data["http://axschema.org/contact/email"] packet['user.id'] = packet['user.id'].first if packet['user.id'].kind_of?(Array) end after = packet.flash('user.after_login') || '/' # Save id into session if we have one. packet.session['user.id'] = packet['user.id'] # If the user was supposed to be going somewhere, redirect there packet.reroute(after) false else packet.flash['error'] = resp.status packet.reroute(@login) false end # Set WWW-Authenticate header if awaiting openid.response else packet[:status] = 401 packet[:headers] = {} id = packet.request.params["openid_identifier"] id = "http://#{id}" unless id =~ /^https?:\/\// packet.add_header('WWW-Authenticate', Rack::OpenID.build_header( :identifier => id, :required => [:email, "http://axschema.org/contact/email"] ) ) packet[:content] = 'Got openID?' return packet.finish end # Show login form, if necessary else packet[:content] = orange[:parser].haml('openid_login.haml', packet) return packet.finish end end # end handle_openid end end