module Lipsiadmin module AccessControl module Helper#:nodoc: def recognize_path(path)#:nodoc: case path when String then ActionController::Routing::Routes.recognize_path(path, :method => :get) when Hash then path end end end # This Class map and get roles/projects for accounts # # Examples: # # roles_for :administrator do |role| # role.allow_all_actions "/backend/base" # role.deny_action_of "/backend/accounts/details" # # role.project_module :administration do |project| # project.menu :general_settings, "/backend/settings" do |submenu| # submenu.add :accounts, "/backend/accounts" do |submenu| # submenu.add :sub_accounts, "/backend/accounts/subaccounts" # end # end # end # end # # If a user logged with role administrator or that have a project_module administrator can: # # - Access in all actions of "/backend/base" controller # - Denied access to ONLY action "/backend/accounts/details" # - Access to a project module called Administration # - Access to all actions of the controller "/backend/settings" # - Access to all actions EXCEPT details of controller "/backend/accounts" # class Base class << self # We map project modules for a given role or roles def roles_for(*roles, &block) roles.each { |role| raise AccessControlError, "Role #{role} must be a symbol!" unless role.is_a?(Symbol) } @mappers ||= [] @roles ||= [] @roles.concat(roles) @mappers << Mapper.new(*roles, &block) end # Returns all roles def roles @roles.collect(&:to_s) end # Returns all project modules ids def project_modules @mappers.inject([]) do |pm, maps| pm.concat(maps.project_modules.collect(&:uid)) end.uniq end # Returns maps for a given role def find_by_role(role) role = case role when String then role.downcase.to_sym when Symbol then role end return @mappers.find_all { |m| m.roles.include?(role.to_sym) } end # Returns maps for a given project modules uids def find_by_project_modules(*uids) uids.inject([]) do |maps, uid| maps.concat @mappers.find_all { |m| m.project_modules.collect(&:uid).include?(uid.to_s) } end end end end class Mapper include Helper attr_reader :project_modules, :roles def initialize(*roles, &block)#:nodoc: @project_modules = [] @allowed = [] @denied = [] @roles = roles yield self end # Create a new project module def project_module(name, controller=nil, &block) @project_modules << ProjectModule.new(name, controller, &block) end # Globally allow an action of a controller for the current role def allow_action(path) @allowed << recognize_path(path) end # Globally deny an action of a controllerfor the current role def deny_action(path) @denied << recognize_path(path) end # Globally allow all actions from a controller for the current role def allow_all_actions(path) @allowed << { :controller => recognize_path(path)[:controller] } end # Globally denty all actions from a controller for the current role def deny_all_actions(path) @denied << { :controller => recognize_path(path)[:controller] } end # Return allowed actions/controllers def allowed @project_modules.each { |pm| @allowed.concat pm.allowed } return @allowed.uniq end # Return denied actions/controllers def denied return @denied.uniq end end class ProjectModule include Helper include ActionController::UrlWriter attr_reader :name, :menus, :url def initialize(name, path=nil, options={}, &block)#:nodoc: @name = name @options = options @allowed = [] @menus = [] if path @url = recognize_path(path) @allowed << { :controller => @url[:controller] } end yield self end # Build a new menu and automaitcally add the action on the allowed actions. def menu(name, path=nil, options={}, &block) @menus << Menu.new(name, path, options, &block) end # Return allowed controllers def allowed @menus.each { |m| @allowed.concat m.allowed } return @allowed.uniq end # Return the original name or try to translate or humanize the symbol def human_name return @name.is_a?(Symbol) ? I18n.t("backend.menus.#{@name}", :default => @name.to_s.humanize) : @name end # Return a unique id for the given project module def uid @name.to_s.downcase.gsub(/[^a-z0-9]+/, '').gsub(/-+$/, '').gsub(/^-+$/, '') end # Return ExtJs Config for this project module def config options = @options.merge(:text => human_name) options.merge!(:menu => @menus.collect(&:config)) if @menus.size > 0 options.merge!(:handler => "function(){ Backend.app.load('#{url_for(@url.merge(:only_path => true))}') }".to_l) if @url return options end end class Menu include Helper include ActionController::UrlWriter attr_reader :name, :options, :url, :items def initialize(name, path=nil, options={}, &block)#:nodoc: @name = name @options = options @allowed = [] @items = [] if path @url = recognize_path(path) @allowed << { :controller => @url[:controller] } if path end yield self if block_given? end # Add a new submenu to the menu def add(name, path=nil, options={}, &block) @items << Menu.new(name, path, options, &block) end # Return allowed controllers def allowed @items.each { |i| @allowed.concat i.allowed } return @allowed.uniq end # Return the original name or try to translate or humanize the symbol def human_name return @name.is_a?(Symbol) ? I18n.t("backend.menus.#{@name}", :default => @name.to_s.humanize) : @name end # Return a unique id for the given project module def uid @name.to_s.downcase.gsub(/[^a-z0-9]+/, '').gsub(/-+$/, '').gsub(/^-+$/, '') end # Return ExtJs Config for this menu def config options = @options.merge(:text => human_name) options.merge!(:menu => @items.collect(&:config)) if @items.size > 0 options.merge!(:handler => "function(){ Backend.app.load('#{url_for(@url.merge(:only_path => true))}') }".to_l) if @url return options end end class AccessControlError < StandardError#:nodoc: end end end