commit acfbc45886c4d81a2a3ca5af433a6124a0a7191a
Author: Marek Hulan <mhulan@redhat.com>
Date:   Wed Nov 20 10:58:13 2013 +0100

    fixes #812 - new permissions model, user group role and nest support, role filters for better granularity
    
    Contributions from:
    * Daniel Lobato <elobatocs@gmail.com>
    * Joseph Magen <jmagen@redhat.com>
    * Tom McKay <thomasmckay@redhat.com>
    * Greg Sutcliffe <gsutclif@redhat.com>
    * Dominic Cleal <dcleal@redhat.com>

diff --git a/.gitignore b/.gitignore
index 2b8191857..d7b5a6bfa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,5 @@ locale/*/LC_MESSAGES
 coverage/
 tags
 _build
+zeus.json
+custom_plan.rb
diff --git a/Gemfile b/Gemfile
index a4cb4aa84..6f7dfd80e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -11,12 +11,13 @@ gem 'rest-client', :require => 'rest_client'
 gem "audited-activerecord", "3.0.0"
 gem "will_paginate", "~> 3.0.2"
 gem "ancestry", "~> 2.0"
-gem 'scoped_search', '>= 2.5'
+gem 'scoped_search', '>= 2.6.2'
 gem 'net-ldap'
 gem 'uuidtools'
 gem "apipie-rails", "~> 0.0.23"
 gem 'rabl', '>= 0.7.5', '<= 0.9.0'
 gem 'oauth'
+gem 'deep_cloneable'
 gem 'foreigner', '~> 1.4.2'
 
 if RUBY_VERSION =~ /^1\.8/
diff --git a/app/assets/javascripts/filters.js b/app/assets/javascripts/filters.js
new file mode 100644
index 000000000..ef04743c5
--- /dev/null
+++ b/app/assets/javascripts/filters.js
@@ -0,0 +1,15 @@
+$(document).ready(function () {
+  $('#filter_resource_type').change(function () {
+    $.ajax({
+      url: $(this).data('url'),
+      data: {
+        resource_type: $('#filter_resource_type').val()
+      },
+      dataType: "script"
+    });
+  });
+
+  $('#filter_unlimited').change(function () {
+    $('#search').prop('disabled', $(this).prop('checked'));
+  });
+});
diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb
index e1acf7656..94b5e23e8 100644
--- a/app/controllers/about_controller.rb
+++ b/app/controllers/about_controller.rb
@@ -2,8 +2,8 @@ class AboutController < ApplicationController
   skip_before_filter :authorize, :only => :index
 
   def index
-    @proxies = SmartProxy.my_proxies.includes(:features)
-    @compute_resources = ComputeResource.my_compute_resources
+    @smart_proxies = SmartProxy.authorized(:view_smart_proxies).includes(:features)
+    @compute_resources = ComputeResource.authorized(:view_compute_resources)
     @plugins = Foreman::Plugin.all
   end
 
diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb
index c58ea9104..824f4b9bf 100644
--- a/app/controllers/api/base_controller.rb
+++ b/app/controllers/api/base_controller.rb
@@ -43,8 +43,15 @@ module Api
       @resource_class ||= resource_name.classify.constantize
     end
 
-    def resource_scope
-      @resource_scope ||= resource_class.scoped
+    def resource_scope(controller = controller_name)
+      @resource_scope ||= begin
+        scope = resource_class.scoped
+        if resource_class.respond_to?(:authorized)
+          scope.authorized("#{action_permission}_#{controller}", resource_class)
+        else
+          scope
+        end
+      end
     end
 
     def api_request?
@@ -138,14 +145,15 @@ module Api
     #
     # example:
     # @host = Host.find_resource params[:id]
-    def find_resource
+    def find_resource(controller = controller_name)
       resource = resource_identifying_attributes.find do |key|
         next if key=='name' and (params[:id] =~ /\A\d+\z/)
         method = "find_by_#{key}"
         id = key=='id' ? params[:id].to_i : params[:id]
-        resource_scope.respond_to?(method) and
-          (resource = resource_scope.send method, id) and
-          break resource
+        scope = resource_scope(controller)
+        if scope.respond_to?(method)
+          (resource = scope.send method, id) and break resource
+        end
       end
 
       if resource
@@ -219,7 +227,10 @@ module Api
           if allowed_nested_id.include?(param)
             resource_identifying_attributes.each do |key|
               find_method = "find_by_#{key}"
-              @nested_obj ||= md[1].classify.constantize.send(find_method, params[param])
+              model = md[1].classify.constantize
+              controller = "#{md[1].pluralize}_#{controller_name}"
+              authorized_scope = model.authorized("#{action_permission}_#{controller}")
+              @nested_obj ||= authorized_scope.send(find_method, params[param])
             end
           else
             # there should be a route error before getting here, but just in case,
@@ -250,5 +261,19 @@ module Api
       []
     end
 
+    def action_permission
+      case params[:action]
+      when 'new', 'create'
+        'create'
+      when 'edit', 'update'
+        'edit'
+      when 'destroy'
+        'destroy'
+      when 'index', 'show', 'status'
+        'view'
+      else
+        raise ::Foreman::Exception.new(N_("unknown permission for %s"), "#{params[:controller]}##{params[:action]}")
+      end
+    end
   end
 end
diff --git a/app/controllers/api/v1/architectures_controller.rb b/app/controllers/api/v1/architectures_controller.rb
index 545075ddb..0a0c57a22 100644
--- a/app/controllers/api/v1/architectures_controller.rb
+++ b/app/controllers/api/v1/architectures_controller.rb
@@ -10,7 +10,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @architectures = Architecture.includes(:operatingsystems).
+        @architectures = Architecture.
+          authorized(:view_architectures).
+          includes(:operatingsystems).
           search_for(*search_options).paginate(paginate_options)
       end
 
diff --git a/app/controllers/api/v1/audits_controller.rb b/app/controllers/api/v1/audits_controller.rb
index d070811e6..56d2f0b4a 100644
--- a/app/controllers/api/v1/audits_controller.rb
+++ b/app/controllers/api/v1/audits_controller.rb
@@ -12,7 +12,7 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        Audit.unscoped { @audits = Audit.search_for(*search_options).paginate(paginate_options) }
+        Audit.unscoped { @audits = Audit.authorized(:view_audit_logs).search_for(*search_options).paginate(paginate_options) }
       end
 
       api :GET, "/audits/:id/", "Show an audit"
diff --git a/app/controllers/api/v1/common_parameters_controller.rb b/app/controllers/api/v1/common_parameters_controller.rb
index 51fb944b5..e61f6dc3b 100644
--- a/app/controllers/api/v1/common_parameters_controller.rb
+++ b/app/controllers/api/v1/common_parameters_controller.rb
@@ -1,7 +1,7 @@
 module Api
   module V1
     class CommonParametersController < V1::BaseController
-      before_filter :find_resource, :only => [:show, :update, :destroy]
+      before_filter(:only => %w{show update destroy}) { find_resource('globals') }
 
       api :GET, "/common_parameters/", "List all common parameters."
       param :search, String, :desc => "filter results"
@@ -10,7 +10,10 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @common_parameters = CommonParameter.search_for(*search_options).paginate(paginate_options)
+        @common_parameters = CommonParameter.
+          authorized(:view_globals).
+          search_for(*search_options).
+          paginate(paginate_options)
       end
 
       api :GET, "/common_parameters/:id/", "Show a common parameter."
diff --git a/app/controllers/api/v1/compute_resources_controller.rb b/app/controllers/api/v1/compute_resources_controller.rb
index a956319b3..7bf52596c 100644
--- a/app/controllers/api/v1/compute_resources_controller.rb
+++ b/app/controllers/api/v1/compute_resources_controller.rb
@@ -10,7 +10,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @compute_resources = ComputeResource.my_compute_resources.search_for(*search_options).paginate(paginate_options)
+        @compute_resources = ComputeResource.
+          authorized(:view_compute_resources).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/compute_resources/:id/", "Show an compute resource."
@@ -66,10 +68,6 @@ module Api
         process_response @compute_resource.destroy
       end
 
-      def resource_scope
-        resource_class.my_compute_resources
-      end
-
     end
   end
 end
diff --git a/app/controllers/api/v1/config_templates_controller.rb b/app/controllers/api/v1/config_templates_controller.rb
index c7885430b..9a655b430 100644
--- a/app/controllers/api/v1/config_templates_controller.rb
+++ b/app/controllers/api/v1/config_templates_controller.rb
@@ -3,7 +3,7 @@ module Api
     class ConfigTemplatesController < V1::BaseController
       include Foreman::Renderer
 
-      before_filter :find_resource, :only => [:show, :update, :destroy]
+      before_filter(:only => %w{show update destroy}) { find_resource('templates') }
       before_filter :handle_template_upload, :only => [:create, :update]
       before_filter :process_template_kind, :only => [:create, :update]
 
@@ -14,7 +14,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @config_templates = ConfigTemplate.search_for(*search_options).paginate(paginate_options).
+        @config_templates = ConfigTemplate.
+          authorized(:view_templates).
+          search_for(*search_options).paginate(paginate_options).
           includes(:operatingsystems, :template_combinations, :template_kind)
       end
 
@@ -61,7 +63,7 @@ module Api
       param :version, String, :desc => "template version"
 
       def revision
-        audit = Audit.find(params[:version])
+        audit = Audit.authorized(:view_audit_logs).find(params[:version])
         render :json => audit.revision.template
       end
 
@@ -75,7 +77,7 @@ module Api
       api :GET, "/config_templates/build_pxe_default", "Change the default PXE menu on all configured TFTP servers"
 
       def build_pxe_default
-        status, msg = ConfigTemplate.build_pxe_default(self)
+        status, msg = ConfigTemplate.authorized(:deploy_templates).build_pxe_default(self)
         render :json => msg, :status => status
       end
 
diff --git a/app/controllers/api/v1/domains_controller.rb b/app/controllers/api/v1/domains_controller.rb
index def3a6d2a..02ad341c9 100644
--- a/app/controllers/api/v1/domains_controller.rb
+++ b/app/controllers/api/v1/domains_controller.rb
@@ -22,7 +22,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @domains = Domain.search_for(*search_options).paginate(paginate_options)
+        @domains = Domain.
+          authorized(:view_domains).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/domains/:id/", "Show a domain."
diff --git a/app/controllers/api/v1/environments_controller.rb b/app/controllers/api/v1/environments_controller.rb
index 3fafb2f8c..23bbf5152 100644
--- a/app/controllers/api/v1/environments_controller.rb
+++ b/app/controllers/api/v1/environments_controller.rb
@@ -12,7 +12,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @environments = Environment.search_for(*search_options).paginate(paginate_options)
+        @environments = Environment.
+          authorized(:view_environments).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/environments/:id/", "Show an environment."
diff --git a/app/controllers/api/v1/fact_values_controller.rb b/app/controllers/api/v1/fact_values_controller.rb
index 65b990ff3..0d2cf4255 100644
--- a/app/controllers/api/v1/fact_values_controller.rb
+++ b/app/controllers/api/v1/fact_values_controller.rb
@@ -1,7 +1,6 @@
 module Api
   module V1
     class FactValuesController < V1::BaseController
-      before_filter :find_resource, :only => %w{show update destroy}
       before_filter :setup_search_options, :only => :index
 
       api :GET, "/fact_values/", "List all fact values."
@@ -12,7 +11,10 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        values = FactValue.my_facts.no_timestamp_facts.
+        values = FactValue.
+          authorized(:view_facts).
+          my_facts.
+          no_timestamp_facts.
           search_for(*search_options).paginate(paginate_options).
           includes(:fact_name, :host)
         render :json => FactValue.build_facts_hash(values.all)
diff --git a/app/controllers/api/v1/hostgroups_controller.rb b/app/controllers/api/v1/hostgroups_controller.rb
index f57b9012c..019d3c113 100644
--- a/app/controllers/api/v1/hostgroups_controller.rb
+++ b/app/controllers/api/v1/hostgroups_controller.rb
@@ -10,7 +10,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @hostgroups = Hostgroup.includes(:hostgroup_classes, :group_parameters).
+        @hostgroups = Hostgroup.
+          authorized(:view_hostgroups).
+          includes(:hostgroup_classes, :group_parameters).
           search_for(*search_options).paginate(paginate_options)
       end
 
diff --git a/app/controllers/api/v1/hosts_controller.rb b/app/controllers/api/v1/hosts_controller.rb
index 014a59a4c..3da4e8954 100644
--- a/app/controllers/api/v1/hosts_controller.rb
+++ b/app/controllers/api/v1/hosts_controller.rb
@@ -10,7 +10,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @hosts = Host.my_hosts.search_for(*search_options).paginate(paginate_options)
+        @hosts = Host.
+          authorized(:view_hosts, Host).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/hosts/:id/", "Show a host."
@@ -121,13 +123,12 @@ Return value may either be one of the following:
         render :json => { :status => @host.host_status }.to_json if @host
       end
 
-      # we need to limit resources for a current user
-      def resource_scope
-        resource_class.my_hosts
-      end
-
       private
 
+      def resource_scope(controller = controller_name)
+        Host.authorized("#{action_permission}_#{controller}", Host)
+      end
+
       # this is required for template generation (such as pxelinux) which is not done via a web request
       def forward_request_url
         @host.request_url = request.host_with_port if @host.respond_to?(:request_url)
diff --git a/app/controllers/api/v1/images_controller.rb b/app/controllers/api/v1/images_controller.rb
index 269ee76a1..a0298d428 100644
--- a/app/controllers/api/v1/images_controller.rb
+++ b/app/controllers/api/v1/images_controller.rb
@@ -12,7 +12,10 @@ module Api
       param :compute_resource_id, :identifier, :required => true
 
       def index
-        @images = @compute_resource.images.search_for(*search_options).paginate(paginate_options)
+        @images = @compute_resource.
+          images.
+          authorized(:view_images).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/compute_resources/:compute_resource_id/images/:id/", "Show an image"
@@ -65,7 +68,7 @@ module Api
       private
 
       def find_compute_resource
-        @compute_resource = ComputeResource.find(params[:compute_resource_id])
+        @compute_resource = ComputeResource.authorized(:view_compute_resources).find(params[:compute_resource_id])
       end
 
     end
diff --git a/app/controllers/api/v1/lookup_keys_controller.rb b/app/controllers/api/v1/lookup_keys_controller.rb
index 328593e74..b85ca5b08 100644
--- a/app/controllers/api/v1/lookup_keys_controller.rb
+++ b/app/controllers/api/v1/lookup_keys_controller.rb
@@ -1,7 +1,7 @@
 module Api
   module V1
     class LookupKeysController < V1::BaseController
-      before_filter :find_resource, :only => %w{show update destroy}
+      before_filter(:only => %w{show update destroy}) { find_resource('external_variables') }
       before_filter :setup_search_options, :only => :index
 
       api :GET, "/lookup_keys/", "List all lookup_keys."
@@ -11,7 +11,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @lookup_keys = LookupKey.search_for(*search_options).paginate(paginate_options)
+        @lookup_keys = LookupKey.
+          authorized(:view_external_variables).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/lookup_keys/:id/", "Show a lookup key."
diff --git a/app/controllers/api/v1/media_controller.rb b/app/controllers/api/v1/media_controller.rb
index 58c484929..1e1d3a510 100644
--- a/app/controllers/api/v1/media_controller.rb
+++ b/app/controllers/api/v1/media_controller.rb
@@ -30,7 +30,9 @@ Available families:
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @media = Medium.search_for(*search_options).paginate(paginate_options)
+        @media = Medium.
+          authorized(:view_media).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/media/:id/", "Show a medium."
diff --git a/app/controllers/api/v1/models_controller.rb b/app/controllers/api/v1/models_controller.rb
index e7d5d831c..4defc36d7 100644
--- a/app/controllers/api/v1/models_controller.rb
+++ b/app/controllers/api/v1/models_controller.rb
@@ -10,7 +10,10 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @models = Model.search_for(*search_options).paginate(paginate_options)
+        @models = Model.
+          authorized(:view_models).
+          search_for(*search_options).
+          paginate(paginate_options)
       end
 
       api :GET, "/models/:id/", "Show a model."
diff --git a/app/controllers/api/v1/operatingsystems_controller.rb b/app/controllers/api/v1/operatingsystems_controller.rb
index dbe268785..5e76efddf 100644
--- a/app/controllers/api/v1/operatingsystems_controller.rb
+++ b/app/controllers/api/v1/operatingsystems_controller.rb
@@ -16,6 +16,7 @@ module Api
 
       def index
         @operatingsystems = Operatingsystem.
+          authorized(:view_operatingsystems).
           includes(:media, :architectures, :ptables, :config_templates, :os_default_templates).
           search_for(*search_options).paginate(paginate_options)
       end
@@ -69,8 +70,8 @@ module Api
       param :architecture, String
 
       def bootfiles
-        medium = Medium.find_by_name(params[:medium])
-        arch   = Architecture.find_by_name(params[:architecture])
+        medium = Medium.authorized(:view_media).find_by_name(params[:medium])
+        arch   = Architecture.authorized(:view_architectures).find_by_name(params[:architecture])
         render :json => @operatingsystem.pxe_files(medium, arch)
       rescue => e
         render :json => e.to_s, :status => :unprocessable_entity
diff --git a/app/controllers/api/v1/ptables_controller.rb b/app/controllers/api/v1/ptables_controller.rb
index ef7c86bc9..e873b5ba5 100644
--- a/app/controllers/api/v1/ptables_controller.rb
+++ b/app/controllers/api/v1/ptables_controller.rb
@@ -10,7 +10,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @ptables = Ptable.search_for(*search_options).paginate(paginate_options)
+        @ptables = Ptable.
+          authorized(:view_ptables).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/ptables/:id/", "Show a ptable."
diff --git a/app/controllers/api/v1/puppetclasses_controller.rb b/app/controllers/api/v1/puppetclasses_controller.rb
index e49cf8f0f..31f388e76 100644
--- a/app/controllers/api/v1/puppetclasses_controller.rb
+++ b/app/controllers/api/v1/puppetclasses_controller.rb
@@ -12,7 +12,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        values = Puppetclass.search_for(*search_options).paginate(paginate_options).
+        values = Puppetclass.
+          authorized(:view_puppetclasses).
+          search_for(*search_options).paginate(paginate_options).
           select([:name, :id]).
           includes(:lookup_keys)
         render :json => Puppetclass.classes2hash(values.all)
diff --git a/app/controllers/api/v1/reports_controller.rb b/app/controllers/api/v1/reports_controller.rb
index 89001a016..6117c4b08 100644
--- a/app/controllers/api/v1/reports_controller.rb
+++ b/app/controllers/api/v1/reports_controller.rb
@@ -11,7 +11,10 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @reports = Report.my_reports.includes(:logs => [:source, :message]).
+        @reports = Report.
+          authorized(:view_reports).
+          my_reports.
+          includes(:logs => [:source, :message]).
           search_for(*search_options).paginate(paginate_options)
       end
 
@@ -33,8 +36,8 @@ module Api
 
       def last
         conditions = { :host_id => Host.find_by_name(params[:host_id]).try(:id) } unless params[:host_id].blank?
-        max_id = Report.my_reports.where(conditions).maximum(:id)
-        @report = Report.includes(:logs => [:message, :source]).find(max_id)
+        max_id = Report.authorized(:view_reports).my_reports.where(conditions).maximum(:id)
+        @report = Report.authorized(:view_reports).includes(:logs => [:message, :source]).find(max_id)
         render :show
       end
 
diff --git a/app/controllers/api/v1/smart_proxies_controller.rb b/app/controllers/api/v1/smart_proxies_controller.rb
index b5342e446..1f03d3e1f 100644
--- a/app/controllers/api/v1/smart_proxies_controller.rb
+++ b/app/controllers/api/v1/smart_proxies_controller.rb
@@ -58,9 +58,18 @@ module Api
       end
 
       private
+      def action_permission
+        case params[:action]
+        when 'refresh'
+          :edit
+        else
+          super
+        end
+      end
+
       def proxies_by_type(type)
-        return SmartProxy.includes(:features).try(type.downcase+"_proxies") if not type.nil?
-        return SmartProxy.includes(:features).all
+        return SmartProxy.authorized(:view_smart_proxies).includes(:features).try(type.downcase+"_proxies") if not type.nil?
+        return SmartProxy.authorized(:view_smart_proxies).includes(:features).all
       end
 
       def check_feature_type
diff --git a/app/controllers/api/v1/statistics_controller.rb b/app/controllers/api/v1/statistics_controller.rb
index a56e533aa..e70fd087d 100644
--- a/app/controllers/api/v1/statistics_controller.rb
+++ b/app/controllers/api/v1/statistics_controller.rb
@@ -6,22 +6,22 @@ module Api
       api :GET, "/statistics/", "Get statistics"
 
       def index
-        @os_count    = Host.my_hosts.count_distribution :operatingsystem
-        @arch_count  = Host.my_hosts.count_distribution :architecture
-        @env_count   = Host.my_hosts.count_distribution :environment
-        @klass_count = Host.my_hosts.count_habtm "puppetclass"
-        @cpu_count   = FactValue.my_facts.count_each "processorcount"
-        @model_count = FactValue.my_facts.count_each "manufacturer"
-        @mem_size    = FactValue.my_facts.mem_average "memorysize"
-        @mem_free    = FactValue.my_facts.mem_average "memoryfree"
-        @swap_size   = FactValue.my_facts.mem_average "swapsize"
-        @swap_free   = FactValue.my_facts.mem_average "swapfree"
-        @mem_totsize = FactValue.my_facts.mem_sum "memorysize"
-        @mem_totfree = FactValue.my_facts.mem_sum "memoryfree"
-        render :json => { :statistics => { :os_count    => @os_count, :arch_count => @arch_count,
-                                            :env_count   => @env_count, :klass_count => @klass_count, :cpu_count => @cpu_count,
-                                            :model_count => @model_count, :mem_size => @mem_size, :mem_free => @mem_free, :swap_size => @swap_size,
-                                            :swap_free   => @swap_free, :mem_totsize => @mem_totsize, :mem_totfree => @mem_totfree } }
+        @os_count    = Host.authorized(:view_hosts).count_distribution :operatingsystem
+        @arch_count  = Host.authorized(:view_hosts).count_distribution :architecture
+        @env_count   = Host.authorized(:view_hosts).count_distribution :environment
+        @klass_count = Host.authorized(:view_hosts).count_habtm "puppetclass"
+        @cpu_count   = FactValue.authorized(:view_facts).my_facts.count_each "processorcount"
+        @model_count = FactValue.authorized(:view_facts).my_facts.count_each "manufacturer"
+        @mem_size    = FactValue.authorized(:view_facts).my_facts.mem_average "memorysize"
+        @mem_free    = FactValue.authorized(:view_facts).my_facts.mem_average "memoryfree"
+        @swap_size   = FactValue.authorized(:view_facts).my_facts.mem_average "swapsize"
+        @swap_free   = FactValue.authorized(:view_facts).my_facts.mem_average "swapfree"
+        @mem_totsize = FactValue.authorized(:view_facts).my_facts.mem_sum "memorysize"
+        @mem_totfree = FactValue.authorized(:view_facts).my_facts.mem_sum "memoryfree"
+        render :json => { :os_count    => @os_count,    :arch_count => @arch_count,   :swap_size => @swap_size,
+                          :env_count   => @env_count,   :klass_count => @klass_count, :cpu_count => @cpu_count,
+                          :model_count => @model_count, :mem_size => @mem_size,       :mem_free => @mem_free,
+                          :swap_free   => @swap_free,   :mem_totsize => @mem_totsize, :mem_totfree => @mem_totfree }
       end
 
     end
diff --git a/app/controllers/api/v1/subnets_controller.rb b/app/controllers/api/v1/subnets_controller.rb
index d79b03418..5ef0dcc91 100644
--- a/app/controllers/api/v1/subnets_controller.rb
+++ b/app/controllers/api/v1/subnets_controller.rb
@@ -11,7 +11,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @subnets = Subnet.includes(:tftp, :dhcp, :dns).
+        @subnets = Subnet.
+          authorized(:view_subnets).
+          includes(:tftp, :dhcp, :dns).
           search_for(*search_options).paginate(paginate_options)
       end
 
diff --git a/app/controllers/api/v1/usergroups_controller.rb b/app/controllers/api/v1/usergroups_controller.rb
index eafefc62a..bc96d1c59 100644
--- a/app/controllers/api/v1/usergroups_controller.rb
+++ b/app/controllers/api/v1/usergroups_controller.rb
@@ -10,7 +10,9 @@ module Api
       param :order, String, :desc => "sort results"
 
       def index
-        @usergroups = Usergroup.search_for(*search_options).paginate(paginate_options)
+        @usergroups = Usergroup.
+          authorized(:view_usergroups).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/usergroups/:id/", "Show a usergroup."
diff --git a/app/controllers/api/v1/users_controller.rb b/app/controllers/api/v1/users_controller.rb
index 2827aac8d..0a0944d36 100644
--- a/app/controllers/api/v1/users_controller.rb
+++ b/app/controllers/api/v1/users_controller.rb
@@ -12,14 +12,15 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @users = User.search_for(*search_options).paginate(paginate_options)
+        @users = User.
+          authorized(:view_users).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/users/:id/", "Show an user."
       param :id, String, :required => true
 
       def show
-        @user
       end
 
       api :POST, "/users/", "Create an user."
diff --git a/app/controllers/api/v2/architectures_controller.rb b/app/controllers/api/v2/architectures_controller.rb
index 6c701b8a8..ce560a1ee 100644
--- a/app/controllers/api/v2/architectures_controller.rb
+++ b/app/controllers/api/v2/architectures_controller.rb
@@ -10,7 +10,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @architectures = Architecture.includes(:operatingsystems).
+        @architectures = Architecture.
+          authorized(:view_architectures).
+          includes(:operatingsystems).
           search_for(*search_options).paginate(paginate_options)
       end
 
diff --git a/app/controllers/api/v2/audits_controller.rb b/app/controllers/api/v2/audits_controller.rb
index 59612f4f2..402ceb85f 100644
--- a/app/controllers/api/v2/audits_controller.rb
+++ b/app/controllers/api/v2/audits_controller.rb
@@ -1,7 +1,8 @@
 module Api
   module V2
     class AuditsController < V2::BaseController
-      before_filter :find_resource, :only => %w{show update destroy}
+      before_filter :find_resource, :only => %w{show}
+      before_filter(:only => %w{show}) { find_resource('audit_logs') }
       before_filter :setup_search_options, :only => :index
 
       api :GET, "/audits/", "List all audits."
@@ -12,7 +13,7 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        Audit.unscoped { @audits = Audit.search_for(*search_options).paginate(paginate_options) }
+        Audit.unscoped { @audits = Audit.authorized(:view_audit_logs).search_for(*search_options).paginate(paginate_options) }
       end
 
       api :GET, "/audits/:id/", "Show an audit"
diff --git a/app/controllers/api/v2/common_parameters_controller.rb b/app/controllers/api/v2/common_parameters_controller.rb
index 18fcc2a29..2e9dce23c 100644
--- a/app/controllers/api/v2/common_parameters_controller.rb
+++ b/app/controllers/api/v2/common_parameters_controller.rb
@@ -2,7 +2,7 @@
 module Api
   module V2
     class CommonParametersController < V2::BaseController
-      before_filter :find_resource, :only => [:show, :update, :destroy]
+      before_filter(:only => %w{show update destroy}) { find_resource('globals') }
 
       api :GET, "/common_parameters/", "List all common parameters."
       param :search, String, :desc => "filter results"
@@ -11,7 +11,10 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @common_parameters = CommonParameter.search_for(*search_options).paginate(paginate_options)
+        @common_parameters = CommonParameter.
+          authorized(:view_globals).
+          search_for(*search_options).
+          paginate(paginate_options)
       end
 
       api :GET, "/common_parameters/:id/", "Show a common parameter."
diff --git a/app/controllers/api/v2/compute_resources_controller.rb b/app/controllers/api/v2/compute_resources_controller.rb
index c579fe37d..dc4f1af24 100644
--- a/app/controllers/api/v2/compute_resources_controller.rb
+++ b/app/controllers/api/v2/compute_resources_controller.rb
@@ -43,8 +43,8 @@ module Api
       param_group :compute_resource, :as => :create
 
       def create
-          @compute_resource = ComputeResource.new_provider(params[:compute_resource])
-          process_response @compute_resource.save
+        @compute_resource = ComputeResource.new_provider(params[:compute_resource])
+        process_response @compute_resource.save
       end
 
 
@@ -91,8 +91,15 @@ module Api
         render :available_storage_domains, :layout => 'api/v2/layouts/index_layout'
       end
 
-      def resource_scope
-        ComputeResource.my_compute_resources
+      private
+
+      def action_permission
+        case params[:action]
+          when 'available_images', 'available_clusters', 'available_networks', 'available_storage_domains'
+            :view
+          else
+            super
+        end
       end
     end
   end
diff --git a/app/controllers/api/v2/config_templates_controller.rb b/app/controllers/api/v2/config_templates_controller.rb
index 7492db45b..a6cc00096 100644
--- a/app/controllers/api/v2/config_templates_controller.rb
+++ b/app/controllers/api/v2/config_templates_controller.rb
@@ -5,7 +5,7 @@ module Api
       include Api::TaxonomyScope
       include Foreman::Renderer
 
-      before_filter :find_resource, :only => [:show, :update, :destroy]
+      before_filter(:only => %w{show update destroy}) { find_resource('templates') }
       before_filter :handle_template_upload, :only => [:create, :update]
       before_filter :process_template_kind, :only => [:create, :update]
       before_filter :process_operatingsystems, :only => [:create, :update]
@@ -17,7 +17,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @config_templates = ConfigTemplate.search_for(*search_options).paginate(paginate_options).
+        @config_templates = ConfigTemplate.
+          authorized(:view_templates).
+          search_for(*search_options).paginate(paginate_options).
           includes(:operatingsystems, :template_combinations, :template_kind)
       end
 
@@ -60,7 +62,7 @@ module Api
       param :version, String, :desc => "template version"
 
       def revision
-        audit = Audit.find(params[:version])
+        audit = Audit.authorized(:view_audit_logs).find(params[:version])
         render :json => audit.revision.template
       end
 
@@ -74,7 +76,7 @@ module Api
       api :GET, "/config_templates/build_pxe_default", "Change the default PXE menu on all configured TFTP servers"
 
       def build_pxe_default
-        status, msg = ConfigTemplate.build_pxe_default(self)
+        status, msg = ConfigTemplate.authorized(:deploy_templates).build_pxe_default(self)
         render :json => msg, :status => status
       end
 
diff --git a/app/controllers/api/v2/domains_controller.rb b/app/controllers/api/v2/domains_controller.rb
index c2ec7d90b..5b6583e4b 100644
--- a/app/controllers/api/v2/domains_controller.rb
+++ b/app/controllers/api/v2/domains_controller.rb
@@ -26,7 +26,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @domains = Domain.search_for(*search_options).paginate(paginate_options)
+        @domains = Domain.
+          authorized(:view_domains).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/domains/:id/", "Show a domain."
diff --git a/app/controllers/api/v2/environments_controller.rb b/app/controllers/api/v2/environments_controller.rb
index 972f25b9b..c06afd087 100644
--- a/app/controllers/api/v2/environments_controller.rb
+++ b/app/controllers/api/v2/environments_controller.rb
@@ -14,7 +14,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @environments = Environment.search_for(*search_options).paginate(paginate_options)
+        @environments = Environment.
+          authorized(:view_environments).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/environments/:id/", "Show an environment."
diff --git a/app/controllers/api/v2/fact_values_controller.rb b/app/controllers/api/v2/fact_values_controller.rb
index deacb9cdc..e27192560 100644
--- a/app/controllers/api/v2/fact_values_controller.rb
+++ b/app/controllers/api/v2/fact_values_controller.rb
@@ -1,7 +1,6 @@
 module Api
   module V2
     class FactValuesController < V2::BaseController
-      before_filter :find_resource, :only => %w{show update destroy}
       before_filter :setup_search_options, :only => :index
 
       api :GET, "/fact_values/", "List all fact values."
@@ -16,8 +15,8 @@ module Api
         @fact_values = FactValue.build_facts_hash(values.all)
       end
 
-      def resource_scope
-        FactValue.my_facts.no_timestamp_facts
+      def resource_scope(controller = controller_name)
+        FactValue.authorized(:view_facts).my_facts.no_timestamp_facts
       end
 
     end
diff --git a/app/controllers/api/v2/filters_controller.rb b/app/controllers/api/v2/filters_controller.rb
new file mode 100644
index 000000000..de10cf7c1
--- /dev/null
+++ b/app/controllers/api/v2/filters_controller.rb
@@ -0,0 +1,76 @@
+module Api
+  module V2
+    class FiltersController < V2::BaseController
+      include Api::Version2
+      include Api::TaxonomyScope
+
+      before_filter :find_role
+      before_filter :find_resource, :only => %w{show update destroy}
+
+      api :GET, "/filters/", "List all filters."
+      param :search, String, :desc => "filter results", :required => false
+      param :page, String, :desc => "paginate results"
+      param :per_page, String, :desc => "number of entries per request"
+
+      def index
+        @filters = resource_scope.search_for(*search_options).paginate(paginate_options)
+      end
+
+      api :GET, "/filters/:id/", "Show a filter."
+      param :id, :identifier, :required => true
+
+      def show
+      end
+
+      def_param_group :filter do
+        param :filter, Hash, :action_aware => true, :required => true do
+          param :role_id, String, :required => true
+          param :search, String
+          param :permission_ids, Array
+          param :organization_ids, Array
+          param :location_ids, Array
+        end
+      end
+
+      api :POST, "/filters/", "Create a filter."
+      param_group :filter, :as => :create
+
+      def create
+        @filter = Filter.new(params[:filter])
+        process_response @filter.save
+      end
+
+      api :PUT, "/filters/:id/", "Update a filter."
+      param :id, String, :required => true
+      param_group :filter
+
+      def update
+        process_response @filter.update_attributes(params[:filter])
+      end
+
+      api :DELETE, "/filters/:id/", "Delete a filter."
+      param :id, String, :required => true
+
+      def destroy
+        process_response @filter.destroy
+      end
+
+      private
+
+      def find_role
+        @role = Role.find_by_id(role_id)
+      end
+
+      def resource_scope(controller = controller_name)
+        @resource_scope ||= @role.present? ?
+            @role.filters.authorized("#{action_permission}_#{controller}") :
+            resource_class.scoped.authorized("#{action_permission}_#{controller}")
+      end
+
+      def role_id
+        params[:role_id]
+      end
+
+    end
+  end
+end
diff --git a/app/controllers/api/v2/host_classes_controller.rb b/app/controllers/api/v2/host_classes_controller.rb
index c44a18683..c3dc41b2b 100644
--- a/app/controllers/api/v2/host_classes_controller.rb
+++ b/app/controllers/api/v2/host_classes_controller.rb
@@ -10,7 +10,7 @@ module Api
       api :GET, "/hosts/:host_id/puppetclass_ids/", "List all puppetclass id's for host"
 
       def index
-        render :json => { root_node_name => HostClass.where(:host_id => host_id).pluck('puppetclass_id') }
+        render :json => { root_node_name => HostClass.authorized(:edit_classes).where(:host_id => host_id).pluck('puppetclass_id') }
       end
 
       api :POST, "/hosts/:host_id/puppetclass_ids", "Add a puppetclass to host"
@@ -27,7 +27,7 @@ module Api
       param :id, String, :required => true, :desc => "id of puppetclass"
 
       def destroy
-        @host_class = HostClass.where(:host_id => host_id, :puppetclass_id => params[:id])
+        @host_class = HostClass.authorized(:edit_classes).where(:host_id => host_id, :puppetclass_id => params[:id])
         process_response @host_class.destroy_all
       end
 
@@ -38,7 +38,7 @@ module Api
         if params[:host_id] =~ /^\d+$/
           return @host_id = params[:host_id].to_i
         else
-          @host ||= Host::Managed.find_by_name(params[:host_id])
+          @host ||= Host::Managed.authorized(:view_hosts).find_by_name(params[:host_id])
           return @host_id = @host.id if @host
           not_found
         end
diff --git a/app/controllers/api/v2/hostgroups_controller.rb b/app/controllers/api/v2/hostgroups_controller.rb
index 448178f5c..0ac7a2c8a 100644
--- a/app/controllers/api/v2/hostgroups_controller.rb
+++ b/app/controllers/api/v2/hostgroups_controller.rb
@@ -14,7 +14,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @hostgroups = Hostgroup.includes(:hostgroup_classes, :group_parameters).
+        @hostgroups = Hostgroup.
+          authorized(:view_hostgroups).
+          includes(:hostgroup_classes, :group_parameters).
           search_for(*search_options).paginate(paginate_options)
       end
 
diff --git a/app/controllers/api/v2/hosts_controller.rb b/app/controllers/api/v2/hosts_controller.rb
index 849924832..842e6184f 100644
--- a/app/controllers/api/v2/hosts_controller.rb
+++ b/app/controllers/api/v2/hosts_controller.rb
@@ -7,7 +7,9 @@ module Api
       include Api::TaxonomyScope
       include Foreman::Controller::SmartProxyAuth
 
-      before_filter :find_resource, :except => [:index, :create, :facts]
+      before_filter :find_resource, :except => %w{index create facts}
+      before_filter :permissions_check, :only => %w{power boot puppetrun}
+
       add_puppetmaster_filters :facts
 
       api :GET, "/hosts/", "List all hosts."
@@ -104,11 +106,6 @@ Return value may either be one of the following:
         render :json => { :status => @host.host_status }.to_json if @host
       end
 
-      # we need to limit resources for a current user
-      def resource_scope
-        Host.my_hosts
-      end
-
       api :PUT, "/hosts/:id/puppetrun", "Force a puppet run on the agent."
       param :id, :identifier_dottable, :required => true
 
@@ -158,6 +155,25 @@ Return value may either be one of the following:
 
       private
 
+      def resource_scope(controller = controller_name)
+        Host.authorized("#{action_permission}_#{controller}", Host)
+      end
+
+      def action_permission
+        case params[:action]
+          when 'puppetrun'
+            :puppetrun
+          when 'power'
+            :power
+          when 'boot'
+            :ipmi_boot
+          when 'console'
+            :console
+          else
+            super
+        end
+      end
+
       # this is required for template generation (such as pxelinux) which is not done via a web request
       def forward_request_url
         @host.request_url = request.host_with_port if @host.respond_to?(:request_url)
@@ -175,6 +191,10 @@ Return value may either be one of the following:
         raise ::Foreman::Exception.new("A problem occurred when detecting host type: #{e.message}")
       end
 
+      def permissions_check
+        permission = "#{params[:action]}_hosts".to_sym
+        deny_access unless Host.authorized(permission).find(@host.id)
+      end
     end
   end
 end
diff --git a/app/controllers/api/v2/images_controller.rb b/app/controllers/api/v2/images_controller.rb
index 4b7c40ec3..acf77d585 100644
--- a/app/controllers/api/v2/images_controller.rb
+++ b/app/controllers/api/v2/images_controller.rb
@@ -12,8 +12,9 @@ module Api
       param :compute_resource_id, :identifier, :required => true
 
       def index
-        @images = @compute_resource.images.search_for(*search_options).paginate(paginate_options)
-        @total = @compute_resource.images.count
+        base = @compute_resource.images.authorized(:view_images)
+        @images = base.search_for(*search_options).paginate(paginate_options)
+        @total = base.count
       end
 
       api :GET, "/compute_resources/:compute_resource_id/images/:id/", "Show an image"
@@ -63,7 +64,7 @@ module Api
       private
 
       def find_compute_resource
-        @compute_resource = ComputeResource.find(params[:compute_resource_id])
+        @compute_resource = ComputeResource.authorized(:view_compute_resources).find(params[:compute_resource_id])
       end
 
     end
diff --git a/app/controllers/api/v2/media_controller.rb b/app/controllers/api/v2/media_controller.rb
index 847058c73..0788de4a2 100644
--- a/app/controllers/api/v2/media_controller.rb
+++ b/app/controllers/api/v2/media_controller.rb
@@ -34,7 +34,9 @@ Available families:
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @media = Medium.search_for(*search_options).paginate(paginate_options)
+        @media = Medium.
+          authorized(:view_media).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/media/:id/", "Show a medium."
diff --git a/app/controllers/api/v2/models_controller.rb b/app/controllers/api/v2/models_controller.rb
index ad9b17c84..25504fc80 100644
--- a/app/controllers/api/v2/models_controller.rb
+++ b/app/controllers/api/v2/models_controller.rb
@@ -10,7 +10,10 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @models = Model.search_for(*search_options).paginate(paginate_options)
+        @models = Model.
+          authorized(:view_models).
+          search_for(*search_options).
+          paginate(paginate_options)
       end
 
       api :GET, "/models/:id/", "Show a model."
diff --git a/app/controllers/api/v2/operatingsystems_controller.rb b/app/controllers/api/v2/operatingsystems_controller.rb
index cc237dac7..99e673429 100644
--- a/app/controllers/api/v2/operatingsystems_controller.rb
+++ b/app/controllers/api/v2/operatingsystems_controller.rb
@@ -16,6 +16,7 @@ module Api
 
       def index
         @operatingsystems = Operatingsystem.
+          authorized(:view_operatingsystems).
           includes(:media, :architectures, :ptables, :config_templates, :os_default_templates).
           search_for(*search_options).paginate(paginate_options)
       end
@@ -66,8 +67,8 @@ module Api
       param :architecture, String
 
       def bootfiles
-        medium = Medium.find_by_name(params[:medium])
-        arch   = Architecture.find_by_name(params[:architecture])
+        medium = Medium.authorized(:view_media).find_by_name(params[:medium])
+        arch   = Architecture.authorized(:view_architectures).find_by_name(params[:architecture])
         render :json => @operatingsystem.pxe_files(medium, arch)
       rescue => e
         render :json => e.to_s, :status => :unprocessable_entity
diff --git a/app/controllers/api/v2/override_values_controller.rb b/app/controllers/api/v2/override_values_controller.rb
index f033f40a7..ecaba9f23 100644
--- a/app/controllers/api/v2/override_values_controller.rb
+++ b/app/controllers/api/v2/override_values_controller.rb
@@ -63,6 +63,7 @@ module Api
         render 'api/v2/override_values/show'
       end
 
+      private
       def find_override_values
         if @smart
           @override_values = @smart.lookup_values.paginate(paginate_options)
diff --git a/app/controllers/api/v2/parameters_controller.rb b/app/controllers/api/v2/parameters_controller.rb
index 68fb756a1..8f47ee17c 100644
--- a/app/controllers/api/v2/parameters_controller.rb
+++ b/app/controllers/api/v2/parameters_controller.rb
@@ -136,6 +136,15 @@ module Api
 
       private
 
+      def action_permission
+        case params[:action]
+          when 'reset'
+            :destroy
+          else
+            super
+        end
+      end
+
       def parameters_method
         # hostgroup.rb has a method def parameters, so I didn't create has_many :parameters like Host, Domain, Os
         nested_obj.is_a?(Hostgroup) ? :group_parameters : :parameters
diff --git a/app/controllers/api/v2/permissions_controller.rb b/app/controllers/api/v2/permissions_controller.rb
new file mode 100644
index 000000000..c043fa2ac
--- /dev/null
+++ b/app/controllers/api/v2/permissions_controller.rb
@@ -0,0 +1,32 @@
+module Api
+  module V2
+    class PermissionsController < V2::BaseController
+      before_filter :find_resource, :only => %w{show}
+
+      api :GET, "/permissions/", "List all permissions."
+      param :page, String, :desc => "paginate results"
+      param :per_page, String, :desc => "number of entries per request"
+      param :resource_type, String
+      param :name, String
+
+      def index
+        type = params[:resource_type].blank? ? nil : params[:resource_type]
+        name = params[:name].blank? ? nil : params[:name]
+        if type
+          @permissions = Permission.find_all_by_resource_type(type)
+        elsif name
+          @permissions = Permission.find_all_by_name(name)
+        else
+          @permissions = Permission.all
+        end
+      end
+
+      api :GET, "/permissions/:id/", "Show a permission."
+      param :id, :identifier, :required => true
+
+      def show
+      end
+
+    end
+  end
+end
diff --git a/app/controllers/api/v2/ptables_controller.rb b/app/controllers/api/v2/ptables_controller.rb
index ed239b0c8..832aefb2b 100644
--- a/app/controllers/api/v2/ptables_controller.rb
+++ b/app/controllers/api/v2/ptables_controller.rb
@@ -10,7 +10,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @ptables = Ptable.search_for(*search_options).paginate(paginate_options)
+        @ptables = Ptable.
+          authorized(:view_ptables).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/ptables/:id/", "Show a ptable."
diff --git a/app/controllers/api/v2/puppetclasses_controller.rb b/app/controllers/api/v2/puppetclasses_controller.rb
index 8f868b1a2..c6a77fe1e 100644
--- a/app/controllers/api/v2/puppetclasses_controller.rb
+++ b/app/controllers/api/v2/puppetclasses_controller.rb
@@ -21,7 +21,7 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        values   = Puppetclass.search_for(*search_options) unless nested_obj
+        values   = Puppetclass.authorized(:view_puppetclasses).search_for(*search_options) unless nested_obj
         values ||= case nested_obj
                      when Host::Base, Hostgroup
                        #NOTE: no search_for on array generated by all_puppetclasses
diff --git a/app/controllers/api/v2/reports_controller.rb b/app/controllers/api/v2/reports_controller.rb
index c8a95f799..f81da8eac 100644
--- a/app/controllers/api/v2/reports_controller.rb
+++ b/app/controllers/api/v2/reports_controller.rb
@@ -15,7 +15,10 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @reports = Report.my_reports.includes(:logs => [:source, :message]).
+        @reports = Report.
+          authorized(:view_reports).
+          my_reports.
+          includes(:logs => [:source, :message]).
           search_for(*search_options).paginate(paginate_options)
         @total = Report.my_reports.count
       end
@@ -58,8 +61,8 @@ module Api
 
       def last
         conditions = { :host_id => Host.find_by_name(params[:host_id]).try(:id) } unless params[:host_id].blank?
-        max_id = Report.my_reports.where(conditions).maximum(:id)
-        @report = Report.includes(:logs => [:message, :source]).find(max_id)
+        max_id = Report.authorized(:view_reports).my_reports.where(conditions).maximum(:id)
+        @report = Report.authorized(:view_reports).includes(:logs => [:message, :source]).find(max_id)
         render :show
       end
 
diff --git a/app/controllers/api/v2/roles_controller.rb b/app/controllers/api/v2/roles_controller.rb
index baa810ec7..835b762c3 100644
--- a/app/controllers/api/v2/roles_controller.rb
+++ b/app/controllers/api/v2/roles_controller.rb
@@ -1,7 +1,6 @@
 module Api
   module V2
     class RolesController < V2::BaseController
-      before_filter :require_admin
       before_filter :find_resource, :only => %w{show update destroy}
 
       api :GET, "/roles/", "List all roles."
diff --git a/app/controllers/api/v2/smart_proxies_controller.rb b/app/controllers/api/v2/smart_proxies_controller.rb
index 29b5012b8..3dfca2f5b 100644
--- a/app/controllers/api/v2/smart_proxies_controller.rb
+++ b/app/controllers/api/v2/smart_proxies_controller.rb
@@ -63,8 +63,17 @@ module Api
 
       private
       def proxies_by_type(type = nil)
-        return SmartProxy.includes(:features).try(type.downcase+"_proxies") if type.present?
-        return SmartProxy.includes(:features).scoped
+        return SmartProxy.authorized(:view_smart_proxies).includes(:features).try(type.downcase+"_proxies") if type.present?
+        return SmartProxy.authorized(:view_smart_proxies).includes(:features).scoped
+      end
+
+      def action_permission
+        case params[:action]
+        when 'refresh'
+          :edit
+        else
+          super
+        end
       end
 
       def check_feature_type
diff --git a/app/controllers/api/v2/statistics_controller.rb b/app/controllers/api/v2/statistics_controller.rb
index 94a2ad5ca..e059c8eaa 100644
--- a/app/controllers/api/v2/statistics_controller.rb
+++ b/app/controllers/api/v2/statistics_controller.rb
@@ -6,22 +6,22 @@ module Api
       api :GET, "/statistics/", "Get statistics"
 
       def index
-        @os_count    = Host.my_hosts.count_distribution :operatingsystem
-        @arch_count  = Host.my_hosts.count_distribution :architecture
-        @env_count   = Host.my_hosts.count_distribution :environment
-        @klass_count = Host.my_hosts.count_habtm "puppetclass"
-        @cpu_count   = FactValue.my_facts.count_each "processorcount"
-        @model_count = FactValue.my_facts.count_each "manufacturer"
-        @mem_size    = FactValue.my_facts.mem_average "memorysize"
-        @mem_free    = FactValue.my_facts.mem_average "memoryfree"
-        @swap_size   = FactValue.my_facts.mem_average "swapsize"
-        @swap_free   = FactValue.my_facts.mem_average "swapfree"
-        @mem_totsize = FactValue.my_facts.mem_sum "memorysize"
-        @mem_totfree = FactValue.my_facts.mem_sum "memoryfree"
-        render :json => { :os_count    => @os_count, :arch_count => @arch_count,
-                                            :env_count   => @env_count, :klass_count => @klass_count, :cpu_count => @cpu_count,
-                                            :model_count => @model_count, :mem_size => @mem_size, :mem_free => @mem_free, :swap_size => @swap_size,
-                                            :swap_free   => @swap_free, :mem_totsize => @mem_totsize, :mem_totfree => @mem_totfree }
+        @os_count    = Host.authorized(:view_hosts).count_distribution :operatingsystem
+        @arch_count  = Host.authorized(:view_hosts).count_distribution :architecture
+        @env_count   = Host.authorized(:view_hosts).count_distribution :environment
+        @klass_count = Host.authorized(:view_hosts).count_habtm "puppetclass"
+        @cpu_count   = FactValue.authorized(:view_facts).my_facts.count_each "processorcount"
+        @model_count = FactValue.authorized(:view_facts).my_facts.count_each "manufacturer"
+        @mem_size    = FactValue.authorized(:view_facts).my_facts.mem_average "memorysize"
+        @mem_free    = FactValue.authorized(:view_facts).my_facts.mem_average "memoryfree"
+        @swap_size   = FactValue.authorized(:view_facts).my_facts.mem_average "swapsize"
+        @swap_free   = FactValue.authorized(:view_facts).my_facts.mem_average "swapfree"
+        @mem_totsize = FactValue.authorized(:view_facts).my_facts.mem_sum "memorysize"
+        @mem_totfree = FactValue.authorized(:view_facts).my_facts.mem_sum "memoryfree"
+        render :json => { :os_count    => @os_count,    :arch_count => @arch_count,   :swap_size => @swap_size,
+                          :env_count   => @env_count,   :klass_count => @klass_count, :cpu_count => @cpu_count,
+                          :model_count => @model_count, :mem_size => @mem_size,       :mem_free => @mem_free,
+                          :swap_free   => @swap_free,   :mem_totsize => @mem_totsize, :mem_totfree => @mem_totfree }
       end
 
     end
diff --git a/app/controllers/api/v2/subnets_controller.rb b/app/controllers/api/v2/subnets_controller.rb
index dd19b96f0..cdababf52 100644
--- a/app/controllers/api/v2/subnets_controller.rb
+++ b/app/controllers/api/v2/subnets_controller.rb
@@ -14,7 +14,9 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @subnets = Subnet.includes(:tftp, :dhcp, :dns).
+        @subnets = Subnet.
+          authorized(:view_subnets).
+          includes(:tftp, :dhcp, :dns).
           search_for(*search_options).paginate(paginate_options)
       end
 
diff --git a/app/controllers/api/v2/template_combinations_controller.rb b/app/controllers/api/v2/template_combinations_controller.rb
index fc8cff548..3dd355be3 100644
--- a/app/controllers/api/v2/template_combinations_controller.rb
+++ b/app/controllers/api/v2/template_combinations_controller.rb
@@ -36,7 +36,7 @@ module Api
       end
 
       def find_parent_config_template
-        @config_template = ConfigTemplate.find(params[:config_template_id])
+        @config_template = ConfigTemplate.authorized(:view_templates).find(params[:config_template_id])
         not_found unless @config_template
         @config_template
       end
diff --git a/app/controllers/api/v2/usergroups_controller.rb b/app/controllers/api/v2/usergroups_controller.rb
index 9a251e103..79ca59592 100644
--- a/app/controllers/api/v2/usergroups_controller.rb
+++ b/app/controllers/api/v2/usergroups_controller.rb
@@ -10,7 +10,9 @@ module Api
       param :order, String, :desc => "sort results"
 
       def index
-        @usergroups = Usergroup.search_for(*search_options).paginate(paginate_options)
+        @usergroups = Usergroup.
+          authorized(:view_usergroups).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/usergroups/:id/", "Show a usergroup."
diff --git a/app/controllers/api/v2/users_controller.rb b/app/controllers/api/v2/users_controller.rb
index af8c74533..1f2af027f 100644
--- a/app/controllers/api/v2/users_controller.rb
+++ b/app/controllers/api/v2/users_controller.rb
@@ -1,10 +1,10 @@
 module Api
   module V2
     class UsersController < V2::BaseController
+      before_filter :find_resource, :only => %w{show update destroy}
       include Foreman::Controller::UsersMixin
       include Api::Version2
       include Api::TaxonomyScope
-      before_filter :find_resource, :only => %w{show update destroy}
 
       api :GET, "/users/", "List all users."
       param :search, String, :desc => "filter results"
@@ -13,14 +13,15 @@ module Api
       param :per_page, String, :desc => "number of entries per request"
 
       def index
-        @users = User.search_for(*search_options).paginate(paginate_options)
+        @users = User.
+          authorized(:view_users).
+          search_for(*search_options).paginate(paginate_options)
       end
 
       api :GET, "/users/:id/", "Show an user."
       param :id, String, :required => true
 
       def show
-        @user
       end
 
       def_param_group :user do
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index e79d97a05..390345b10 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -10,6 +10,7 @@ class ApplicationController < ActionController::Base
 
   # standard layout to all controllers
   helper 'layout'
+  helper_method :authorizer
 
   before_filter :require_ssl, :require_login
   before_filter :set_gettext_locale_db, :set_gettext_locale
@@ -45,6 +46,10 @@ class ApplicationController < ActionController::Base
     authorized ? true : deny_access
   end
 
+  def authorizer
+    @authorizer ||= Authorizer.new(User.current, :collection => instance_variable_get("@#{controller_name}"))
+  end
+
   def deny_access
     (User.current.logged? || request.xhr?) ? render_403 : require_login
   end
@@ -111,7 +116,7 @@ class ApplicationController < ActionController::Base
   end
 
   def model_of_controller
-    controller_path.singularize.camelize.gsub('/','::').constantize
+    @model_of_controller ||= controller_path.singularize.camelize.gsub('/','::').constantize
   end
 
 
@@ -124,10 +129,38 @@ class ApplicationController < ActionController::Base
     not_found and return if params[:id].blank?
 
     name = controller_name.singularize
-    model = model_of_controller
-    # determine if we are searching for a numerical id or plain name
     cond = "find" + (params[:id] =~ /\A\d+(-.+)?\Z/ ? "" : "_by_name")
-    not_found and return unless instance_variable_set("@#{name}", model.send(cond, params[:id]))
+    not_found and return unless instance_variable_set("@#{name}", resource_base.send(cond, params[:id]))
+  end
+
+  def current_permission
+    [action_permission, controller_permission].join('_')
+  end
+
+  def controller_permission
+    controller_name
+  end
+
+  def action_permission
+    case params[:action]
+      when 'new', 'create'
+        'create'
+      when 'edit', 'update'
+        'edit'
+      when 'destroy'
+        'destroy'
+      when 'index', 'show'
+        'view'
+      else
+        raise ::Foreman::Exception.new(N_("unknown permission for %s"), "#{params[:controller]}##{params[:action]}")
+    end
+  end
+
+  # not all models includes Authorizable so we detect whether we should apply authorized scope or not
+  def resource_base
+    @resource_base ||= model_of_controller.respond_to?(:authorized) ?
+        model_of_controller.authorized(current_permission) :
+        model_of_controller.scoped
   end
 
   def notice notice
diff --git a/app/controllers/architectures_controller.rb b/app/controllers/architectures_controller.rb
index ad8a7105e..7e9604eb4 100644
--- a/app/controllers/architectures_controller.rb
+++ b/app/controllers/architectures_controller.rb
@@ -3,7 +3,8 @@ class ArchitecturesController < ApplicationController
   before_filter :find_by_name, :only => %w{edit update destroy}
 
   def index
-    @architectures = Architecture.includes(:operatingsystems).search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
+    base = resource_base.includes(:operatingsystems).search_for(params[:search], :order => params[:order])
+    @architectures = base.paginate(:page => params[:page])
   end
 
   def new
diff --git a/app/controllers/audits_controller.rb b/app/controllers/audits_controller.rb
index 7a0a3a123..ac1316dce 100644
--- a/app/controllers/audits_controller.rb
+++ b/app/controllers/audits_controller.rb
@@ -4,11 +4,18 @@ class AuditsController < ApplicationController
   before_filter :setup_search_options, :only => :index
 
   def index
-    Audit.unscoped { @audits = Audit.search_for(params[:search], :order => params[:order]).paginate :page => params[:page] }
+    Audit.unscoped { @audits = resource_base.search_for(params[:search], :order => params[:order]).paginate :page => params[:page] }
   end
 
   def show
-    @audit = Audit.find(params[:id])
-    @history = Audit.descending.where(:auditable_id => @audit.auditable_id, :auditable_type => @audit.auditable_type)
+    @audit = resource_base.find(params[:id])
+    @history = resource_base.descending.where(:auditable_id => @audit.auditable_id, :auditable_type => @audit.auditable_type)
   end
+
+  private
+
+  def controller_permission
+    'audit_logs'
+  end
+
 end
diff --git a/app/controllers/auth_source_ldaps_controller.rb b/app/controllers/auth_source_ldaps_controller.rb
index f5992ff9b..341f078a5 100644
--- a/app/controllers/auth_source_ldaps_controller.rb
+++ b/app/controllers/auth_source_ldaps_controller.rb
@@ -1,6 +1,8 @@
 class AuthSourceLdapsController < ApplicationController
+  before_filter :find_by_name, :only => [:edit, :update, :destroy]
+
   def index
-    @auth_source_ldaps = AuthSourceLdap.all
+    @auth_source_ldaps = resource_base.all
   end
 
   def new
@@ -17,11 +19,9 @@ class AuthSourceLdapsController < ApplicationController
   end
 
   def edit
-    @auth_source_ldap = AuthSourceLdap.find(params[:id])
   end
 
   def update
-    @auth_source_ldap = AuthSourceLdap.find(params[:id])
     # remove from hash :account_password if blank?
     params[:auth_source_ldap].except!(:account_password) if params[:auth_source_ldap][:account_password].blank?
     if @auth_source_ldap.update_attributes(params[:auth_source_ldap])
@@ -32,12 +32,10 @@ class AuthSourceLdapsController < ApplicationController
   end
 
   def destroy
-    @auth_source_ldap = AuthSourceLdap.find(params[:id])
     if @auth_source_ldap.destroy
       process_success
     else
       process_error
     end
-
   end
 end
diff --git a/app/controllers/autosign_controller.rb b/app/controllers/autosign_controller.rb
index f4137149d..ac205eec3 100644
--- a/app/controllers/autosign_controller.rb
+++ b/app/controllers/autosign_controller.rb
@@ -1,7 +1,9 @@
 class AutosignController < ApplicationController
-  before_filter :find_proxy, :setup_proxy
 
   def index
+    @proxy = SmartProxy.authorized(:view_smart_proxies_autosign).find(params[:smart_proxy_id])
+    setup_proxy
+
     begin
       autosign = @api.autosign
     rescue => e
@@ -12,9 +14,14 @@ class AutosignController < ApplicationController
   end
 
   def new
+    @proxy = SmartProxy.authorized(:create_smart_proxies_autosign).find(params[:smart_proxy_id])
+    setup_proxy
   end
 
   def create
+    @proxy = SmartProxy.authorized(:create_smart_proxies_autosign).find(params[:smart_proxy_id])
+    setup_proxy
+
     if @api.set_autosign(params[:id])
       process_success({:success_redirect => smart_proxy_autosign_index_path(@proxy), :object_name => 'puppet autosign entry'})
     else
@@ -23,6 +30,9 @@ class AutosignController < ApplicationController
   end
 
   def destroy
+    @proxy = SmartProxy.authorized(:destroy_smart_proxies_autosign).find(params[:smart_proxy_id])
+    setup_proxy
+
     if @api.del_autosign(params[:id])
       process_success({:success_redirect => smart_proxy_autosign_index_path(@proxy), :object_name => 'puppet autosign entry'})
     else
@@ -32,10 +42,6 @@ class AutosignController < ApplicationController
 
   private
 
-  def find_proxy
-    @proxy = SmartProxy.find(params[:smart_proxy_id])
-  end
-
   def setup_proxy
     @api = ProxyAPI::Puppetca.new({:url => @proxy.url})
   end
diff --git a/app/controllers/bookmarks_controller.rb b/app/controllers/bookmarks_controller.rb
index ad08ec425..58f7f1db3 100644
--- a/app/controllers/bookmarks_controller.rb
+++ b/app/controllers/bookmarks_controller.rb
@@ -2,7 +2,7 @@ class BookmarksController < ApplicationController
   before_filter :find_by_name, :only => %w{show edit update destroy}
 
   def index
-    @bookmarks = Bookmark.paginate(:page => params[:page])
+    @bookmarks = resource_base.paginate(:page => params[:page])
   end
 
   def new
diff --git a/app/controllers/common_parameters_controller.rb b/app/controllers/common_parameters_controller.rb
index 51e819336..72bc99df2 100644
--- a/app/controllers/common_parameters_controller.rb
+++ b/app/controllers/common_parameters_controller.rb
@@ -1,8 +1,9 @@
 class CommonParametersController < ApplicationController
   include Foreman::Controller::AutoCompleteSearch
+  before_filter :find_by_name, :only => [:edit, :update, :destroy]
 
   def index
-    @common_parameters = CommonParameter.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
+    @common_parameters = resource_base.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
   end
 
   def new
@@ -19,11 +20,9 @@ class CommonParametersController < ApplicationController
   end
 
   def edit
-    @common_parameter = CommonParameter.find(params[:id])
   end
 
   def update
-    @common_parameter = CommonParameter.find(params[:id])
     if @common_parameter.update_attributes(params[:common_parameter])
       process_success
     else
@@ -32,11 +31,20 @@ class CommonParametersController < ApplicationController
   end
 
   def destroy
-    @common_parameter = CommonParameter.find(params[:id])
     if @common_parameter.destroy
       process_success
     else
       process_error
     end
   end
+
+  private
+
+  def controller_permission
+    'globals'
+  end
+
+  def resource_base
+    model_of_controller.authorized(current_permission, CommonParameter)
+  end
 end
diff --git a/app/controllers/compute_profiles_controller.rb b/app/controllers/compute_profiles_controller.rb
index 99f689b61..252bd875f 100644
--- a/app/controllers/compute_profiles_controller.rb
+++ b/app/controllers/compute_profiles_controller.rb
@@ -1,10 +1,9 @@
 class ComputeProfilesController < ApplicationController
-
   include Foreman::Controller::AutoCompleteSearch
-  before_filter :find_compute_profile, :only => %w{edit show update destroy}
+  before_filter :find_by_name, :only => [:show, :edit, :update, :destroy]
 
   def index
-    @compute_profiles = ComputeProfile.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
+    @compute_profiles = resource_base.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
   end
 
   def show
@@ -42,10 +41,5 @@ class ComputeProfilesController < ApplicationController
     end
   end
 
-  private
-
-  def find_compute_profile
-    @compute_profile = ComputeProfile.find(params[:id])
-  end
 
 end
diff --git a/app/controllers/compute_resources_controller.rb b/app/controllers/compute_resources_controller.rb
index c35170697..17352c5c6 100644
--- a/app/controllers/compute_resources_controller.rb
+++ b/app/controllers/compute_resources_controller.rb
@@ -2,10 +2,10 @@ class ComputeResourcesController < ApplicationController
   include Foreman::Controller::AutoCompleteSearch
   AJAX_REQUESTS = %w{template_selected cluster_selected}
   before_filter :ajax_request, :only => AJAX_REQUESTS
-  before_filter :find_by_id, :only => [:show, :edit, :update, :destroy, :ping, :associate] + AJAX_REQUESTS
+  before_filter :find_by_name, :only => [:show, :edit, :associate, :update, :destroy, :ping] + AJAX_REQUESTS
 
   def index
-    @compute_resources = ComputeResource.my_compute_resources.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
+    @compute_resources = resource_base.search_for(params[:search], :order => params[:order]).paginate :page => params[:page]
   end
 
   def new
@@ -86,7 +86,7 @@ class ComputeResourcesController < ApplicationController
     # cr_id is posted from AJAX function. cr_id is nil if new
     Rails.logger.info "CR_ID IS #{params[:cr_id]}"
     if params[:cr_id].present? && params[:cr_id] != 'null'
-      @compute_resource = ComputeResource.find(params[:cr_id])
+      @compute_resource = ComputeResource.authorized(:edit_compute_resources).find(params[:cr_id])
       params[:compute_resource].delete(:password) if params[:compute_resource][:password].blank?
       @compute_resource.attributes = params[:compute_resource]
     else
@@ -112,9 +112,14 @@ class ComputeResourcesController < ApplicationController
 
   private
 
-  def find_by_id
-    @compute_resource = ComputeResource.find(params[:id])
-    not_found and return unless @compute_resource
-    deny_access and return unless ComputeResource.my_compute_resources.include?(@compute_resource)
+  def action_permission
+    case params[:action]
+      when 'associate'
+        'edit'
+      when 'ping', 'template_selected', 'cluster_selected'
+        'view'
+      else
+        super
+    end
   end
 end
diff --git a/app/controllers/compute_resources_vms_controller.rb b/app/controllers/compute_resources_vms_controller.rb
index 0e5fa6c6e..658f3dffd 100644
--- a/app/controllers/compute_resources_vms_controller.rb
+++ b/app/controllers/compute_resources_vms_controller.rb
@@ -1,9 +1,9 @@
 class ComputeResourcesVmsController < ApplicationController
-  before_filter :find_compute_resource
-  before_filter :find_vm, :only => [:show, :power, :pause, :console, :associate]
 
   def index
+    @compute_resource = find_compute_resource(:view_compute_resources_vms)
     @vms = @compute_resource.vms.all(params[:filters] || {})
+    @authorizer = Authorizer.new(User.current, :collection => [@compute_resource])
     respond_to do |format|
       format.html
       format.json { render :json => @vms }
@@ -13,10 +13,12 @@ class ComputeResourcesVmsController < ApplicationController
   end
 
   def new
+    @compute_resource = find_compute_resource
     @vm = @compute_resource.new_vm
   end
 
   def create
+    @compute_resource = find_compute_resource
     if @compute_resource.create_vm params[:vm]
       process_success :success_redirect => compute_resource_vms_path(@compute_resource)
     else
@@ -25,6 +27,8 @@ class ComputeResourcesVmsController < ApplicationController
   end
 
   def associate
+    @compute_resource = find_compute_resource(:edit_compute_resources)
+    @vm = find_vm
     if Host.where(:uuid => @vm.identity).any?
       process_error(:error_msg => _("VM already associated with a host"), :redirect => compute_resource_vm_path(:compute_resource_id => params[:compute_resource_id], :id => @vm.identity))
     else
@@ -41,6 +45,8 @@ class ComputeResourcesVmsController < ApplicationController
   end
 
   def show
+    @compute_resource = find_compute_resource(:view_compute_resources_vms)
+    @vm = find_vm
     respond_to do |format|
       format.html
       format.json { render :json => @vm }
@@ -48,14 +54,19 @@ class ComputeResourcesVmsController < ApplicationController
   end
 
   def power
+    @compute_resource = find_compute_resource(:power_compute_resources_vms)
+    @vm = find_vm
     run_vm_action(@vm.ready? ? :stop : :start)
   end
 
   def pause
+    @compute_resource = find_compute_resource(:power_compute_resources_vms)
+    @vm = find_vm
     run_vm_action(@vm.ready? ? :pause : :start)
   end
 
   def destroy
+    @compute_resource = find_compute_resource(:destroy_compute_resources_vms)
     if @compute_resource.destroy_vm params[:id]
       process_success({ :success_redirect => compute_resource_vms_path(@compute_resource) })
     else
@@ -64,6 +75,8 @@ class ComputeResourcesVmsController < ApplicationController
   end
 
   def console
+    @compute_resource = find_compute_resource(:console_compute_resources_vms)
+    @vm = find_vm
     @console = @compute_resource.console @vm.identity
     render case @console[:type]
              when 'spice'
@@ -79,13 +92,12 @@ class ComputeResourcesVmsController < ApplicationController
 
   private
 
-  def find_compute_resource
-    @compute_resource = ComputeResource.find(params[:compute_resource_id])
-    @compute_resource = ComputeResource.my_compute_resources.find(params[:compute_resource_id]) rescue deny_access
+  def find_compute_resource(permission = :view_compute_resources)
+    ComputeResource.authorized(permission).find(params[:compute_resource_id])
   end
 
   def find_vm
-    @vm = @compute_resource.find_vm_by_uuid params[:id]
+    @compute_resource.find_vm_by_uuid params[:id]
   end
 
   def run_vm_action(action)
diff --git a/app/controllers/concerns/api/import_puppetclasses_common_controller.rb b/app/controllers/concerns/api/import_puppetclasses_common_controller.rb
index 19225dc9a..c8ac305e4 100644
--- a/app/controllers/concerns/api/import_puppetclasses_common_controller.rb
+++ b/app/controllers/concerns/api/import_puppetclasses_common_controller.rb
@@ -90,8 +90,8 @@ module Api::ImportPuppetclassesCommonController
 
   def find_required_puppet_proxy
     id = params.keys.include?('smart_proxy_id') ? params['smart_proxy_id'] : params['id']
-    @smart_proxy   = SmartProxy.find_by_id(id.to_i) if id.to_i > 0
-    @smart_proxy ||= SmartProxy.find_by_name(id)
+    @smart_proxy   = SmartProxy.authorized(:view_smart_proxies).find_by_id(id.to_i) if id.to_i > 0
+    @smart_proxy ||= SmartProxy.authorized(:view_smart_proxies).find_by_name(id)
     unless @smart_proxy && SmartProxy.puppet_proxies.pluck("smart_proxies.id").include?(@smart_proxy.id)
       not_found 'We did not find a foreman proxy that can provide the information, ensure that this proxy has the puppet feature turned on.'
     end
@@ -108,8 +108,8 @@ module Api::ImportPuppetclassesCommonController
   end
 
   def find_optional_environment
-    @environment   = Environment.find_by_id(@env_id.to_i) if @env_id.to_i > 0
-    @environment ||= Environment.find_by_name(@env_id)
+    @environment   = Environment.authorized(:view_environments).find_by_id(@env_id.to_i) if @env_id.to_i > 0
+    @environment ||= Environment.authorized(:view_environments).find_by_name(@env_id)
     @environment
   end
 
diff --git a/app/controllers/concerns/api/v2/lookup_keys_common_controller.rb b/app/controllers/concerns/api/v2/lookup_keys_common_controller.rb
index ed491ecba..ce4a679dc 100644
--- a/app/controllers/concerns/api/v2/lookup_keys_common_controller.rb
+++ b/app/controllers/concerns/api/v2/lookup_keys_common_controller.rb
@@ -44,34 +44,34 @@ module Api::V2::LookupKeysCommonController
     end
 
     def find_puppetclass
-      @puppetclass   = Puppetclass.find_by_id(params['puppetclass_id'].to_i) if params['puppetclass_id'].to_i > 0
-      @puppetclass ||= Puppetclass.find_by_name(params['puppetclass_id'])
+      @puppetclass   = Puppetclass.authorized(:view_puppetclasses).find_by_id(params['puppetclass_id'].to_i) if params['puppetclass_id'].to_i > 0
+      @puppetclass ||= Puppetclass.authorized(:view_puppetclasses).find_by_name(params['puppetclass_id'])
       @puppetclass
     end
 
     def find_environment
-      @environment   = Environment.find_by_id(params['environment_id'].to_i) if params['environment_id'].to_i > 0
-      @environment ||= Environment.find_by_name(params['environment_id'])
+      @environment   = Environment.authorized(:view_environments).find_by_id(params['environment_id'].to_i) if params['environment_id'].to_i > 0
+      @environment ||= Environment.authorized(:view_environments).find_by_name(params['environment_id'])
       @environment
     end
 
     def find_host
-      @host   = Host::Base.find_by_id(params['host_id'].to_i) if params['host_id'].to_i > 0
-      @host ||= Host::Base.find_by_name(params['host_id'])
+      @host   = Host::Base.authorized(:view_hosts).find_by_id(params['host_id'].to_i) if params['host_id'].to_i > 0
+      @host ||= Host::Base.authorized(:view_hosts).find_by_name(params['host_id'])
       @host
     end
 
     def find_hostgroup
-      @hostgroup   = Hostgroup.find_by_id(params['hostgroup_id'].to_i) if params['hostgroup_id'].to_i > 0
-      @hostgroup ||= Hostgroup.find_by_name(params['hostgroup_id'])
+      @hostgroup   = Hostgroup.authorized(:view_hostgroups).find_by_id(params['hostgroup_id'].to_i) if params['hostgroup_id'].to_i > 0
+      @hostgroup ||= Hostgroup.authorized(:view_hostgroups).find_by_name(params['hostgroup_id'])
       @hostgroup
     end
 
     def find_smart_variable
       id = params.keys.include?('smart_variable_id') ? params['smart_variable_id'] : params['id']
-      @smart_variable   = LookupKey.smart_variables.find_by_id(id.to_i) if id.to_i > 0
+      @smart_variable   = LookupKey.authorized(:view_external_variables).smart_variables.find_by_id(id.to_i) if id.to_i > 0
       @smart_variable ||= (puppet_cond = { :puppetclass_id => @puppetclass.id } if @puppetclass
-                           LookupKey.smart_variables.where(puppet_cond).find_by_key(id)
+                           LookupKey.authorized(:view_external_variables).smart_variables.where(puppet_cond).find_by_key(id)
                           )
       @smart_variable
     end
@@ -81,19 +81,19 @@ module Api::V2::LookupKeysCommonController
     end
 
     def smart_variables_resource_scope
-      return LookupKey.smart_variables unless (@puppetclass || @host || @hostgroup)
+      return LookupKey.authorized(:view_external_variables).smart_variables unless (@puppetclass || @host || @hostgroup)
       puppetclass_ids   = @puppetclass.id if @puppetclass
       puppetclass_ids ||= @hostgroup.all_puppetclasses.map(&:id) if @hostgroup
       puppetclass_ids ||= @host.all_puppetclasses.map(&:id) if @host
-      LookupKey.global_parameters_for_class(puppetclass_ids)
+      LookupKey.authorized(:view_external_variables).global_parameters_for_class(puppetclass_ids)
     end
 
     def find_smart_class_parameter
       id = params.keys.include?('smart_class_parameter_id') ? params['smart_class_parameter_id'] : params['id']
-      @smart_class_parameter = LookupKey.smart_class_parameters.find_by_id(id.to_i) if id.to_i > 0
+      @smart_class_parameter = LookupKey.authorized(:view_external_variables).smart_class_parameters.find_by_id(id.to_i) if id.to_i > 0
       @smart_class_parameter ||= (puppet_cond = { 'environment_classes.puppetclass_id'=> @puppetclass.id } if @puppetclass
                                   env_cond = { 'environment_classes.environment_id' => @environment.id } if @environment
-                                  LookupKey.smart_class_parameters.where(puppet_cond).where(env_cond).where(:key => id).first
+                                  LookupKey.authorized(:view_external_variables).smart_class_parameters.where(puppet_cond).where(env_cond).where(:key => id).first
                                  )
       @smart_class_parameter
     end
@@ -103,20 +103,21 @@ module Api::V2::LookupKeysCommonController
     end
 
     def smart_class_parameters_resource_scope
-      return LookupKey.smart_class_parameters unless (@puppetclass || @environment || @host || @hostgroup)
+      base = LookupKey.authorized(:view_external_variables)
+      return base.smart_class_parameters unless (@puppetclass || @environment || @host || @hostgroup)
       if @puppetclass && @environment
-        LookupKey.smart_class_parameters_for_class(@puppetclass.id, @environment.id)
+        base.smart_class_parameters_for_class(@puppetclass.id, @environment.id)
       elsif @puppetclass && !@environment
         environment_ids = @puppetclass.environment_classes.pluck(:environment_id).uniq
-        LookupKey.smart_class_parameters_for_class(@puppetclass.id, environment_ids)
+        base.smart_class_parameters_for_class(@puppetclass.id, environment_ids)
       elsif !@puppetclass && @environment
         puppetclass_ids = @environment.environment_classes.pluck(:puppetclass_id).uniq
-        LookupKey.smart_class_parameters_for_class(puppetclass_ids, @environment.id)
+        base.smart_class_parameters_for_class(puppetclass_ids, @environment.id)
       elsif @host || @hostgroup
         puppetclass_ids = (@host || @hostgroup).all_puppetclasses.map(&:id)
         environment_id  = (@host || @hostgroup).environment_id
         # scope :parameters_for_class uses .override
-        LookupKey.parameters_for_class(puppetclass_ids, environment_id)
+        base.parameters_for_class(puppetclass_ids, environment_id)
       end
     end
 
diff --git a/app/controllers/concerns/api/v2/taxonomies_controller.rb b/app/controllers/concerns/api/v2/taxonomies_controller.rb
index e89cceb50..078054f4f 100644
--- a/app/controllers/concerns/api/v2/taxonomies_controller.rb
+++ b/app/controllers/concerns/api/v2/taxonomies_controller.rb
@@ -100,7 +100,7 @@ module Api::V2::TaxonomiesController
   end
 
   def allowed_nested_id
-    %w(domain_id compute_resource_id subnet_id environment_id hostgroup_id smart_proxy_id user_id medium_id organization_id location_id)
+    %w(domain_id compute_resource_id subnet_id environment_id hostgroup_id smart_proxy_id user_id medium_id organization_id location_id filter_id)
   end
 
 end
diff --git a/app/controllers/concerns/foreman/controller/taxonomies_controller.rb b/app/controllers/concerns/foreman/controller/taxonomies_controller.rb
index bb95515f4..825447b46 100644
--- a/app/controllers/concerns/foreman/controller/taxonomies_controller.rb
+++ b/app/controllers/concerns/foreman/controller/taxonomies_controller.rb
@@ -135,7 +135,7 @@ module Foreman::Controller::TaxonomiesController
 
   def assign_hosts
     @taxonomy_type = taxonomy_single.classify
-    @hosts = Host.my_hosts.send("no_#{taxonomy_single}").includes(included_associations).search_for(params[:search],:order => params[:order]).paginate(:page => params[:page])
+    @hosts = Host.authorized(:view_hosts, Host).send("no_#{taxonomy_single}").includes(included_associations).search_for(params[:search],:order => params[:order]).paginate(:page => params[:page])
     render "hosts/assign_hosts"
   end
 
diff --git a/app/controllers/concerns/foreman/controller/taxonomy_multiple.rb b/app/controllers/concerns/foreman/controller/taxonomy_multiple.rb
index 9f0316c5e..8f4b2b963 100644
--- a/app/controllers/concerns/foreman/controller/taxonomy_multiple.rb
+++ b/app/controllers/concerns/foreman/controller/taxonomy_multiple.rb
@@ -8,20 +8,20 @@ module Foreman::Controller::TaxonomyMultiple
   end
 
   def select_multiple_organization
-    find_multiple
+    @hosts = find_multiple
   end
 
   def select_multiple_location
-    find_multiple
+    @hosts = find_multiple
   end
 
   def update_multiple_organization
-    find_multiple
+    @hosts = find_multiple
     update_multiple_taxonomies(:organization)
   end
 
   def update_multiple_location
-    find_multiple
+    @hosts = find_multiple
     update_multiple_taxonomies(:location)
   end
 
@@ -53,4 +53,4 @@ module Foreman::Controller::TaxonomyMultiple
     redirect_back_or_to hosts_path
   end
 
-end
\ No newline at end of file
+end
diff --git a/app/controllers/concerns/foreman/controller/users_mixin.rb b/app/controllers/concerns/foreman/controller/users_mixin.rb
index 63def7983..e038b0037 100644
--- a/app/controllers/concerns/foreman/controller/users_mixin.rb
+++ b/app/controllers/concerns/foreman/controller/users_mixin.rb
@@ -13,7 +13,6 @@ module Foreman::Controller::UsersMixin
   end
 
   def clear_params_on_update
-    find_resource
     if params[:user]
       @admin = params[:user].has_key?(:admin) ? params[:user].delete(:admin) : nil
       # Remove keys for restricted variables when the user is editing their own account
@@ -44,4 +43,4 @@ module Foreman::Controller::UsersMixin
     sub_hg = Hostgroup.where(:id => hostgroup_ids).map(&:subtree).flatten.reject { |hg| hg.user_ids.include?(@user.id) }
     sub_hg.each { |hg| hg.users << @user }
   end
-end
\ No newline at end of file
+end
diff --git a/app/controllers/config_templates_controller.rb b/app/controllers/config_templates_controller.rb
index 2cc030e5d..e29a48139 100644
--- a/app/controllers/config_templates_controller.rb
+++ b/app/controllers/config_templates_controller.rb
@@ -2,12 +2,12 @@ class ConfigTemplatesController < ApplicationController
   include Foreman::Controller::AutoCompleteSearch
   include Foreman::Renderer
 
-  before_filter :find_by_id, :only => [:edit, :update, :destroy]
-  before_filter :load_history, :only => :edit
   before_filter :handle_template_upload, :only => [:create, :update]
+  before_filter :find_by_name, :only => [:edit, :update, :destroy]
+  before_filter :load_history, :only => :edit
 
   def index
-    @config_templates = ConfigTemplate.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page]).includes(:template_kind, :template_combinations => [:hostgroup, :environment])
+    @config_templates = resource_base.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page]).includes(:template_kind, :template_combinations => [:hostgroup, :environment])
   end
 
   def new
@@ -72,7 +72,7 @@ class ConfigTemplatesController < ApplicationController
       :id => template.name, :hostgroup => hostgroup.name
   end
 
-  def find_by_id
-    @config_template = ConfigTemplate.find(params[:id])
+  def controller_permission
+    'templates'
   end
 end
diff --git a/app/controllers/domains_controller.rb b/app/controllers/domains_controller.rb
index 66fa9d662..619111b65 100644
--- a/app/controllers/domains_controller.rb
+++ b/app/controllers/domains_controller.rb
@@ -3,7 +3,7 @@ class DomainsController < ApplicationController
   before_filter :find_by_name, :only => %w{edit update destroy}
 
   def index
-    @domains = Domain.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
+    @domains      = resource_base.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
     @host_counter = Host.group(:domain_id).where(:domain_id => @domains.select(&:id)).count
   end
 
diff --git a/app/controllers/environments_controller.rb b/app/controllers/environments_controller.rb
index b62e75964..5fd5e0be4 100644
--- a/app/controllers/environments_controller.rb
+++ b/app/controllers/environments_controller.rb
@@ -5,7 +5,7 @@ class EnvironmentsController < ApplicationController
   before_filter :find_by_name, :only => %w{edit update destroy}
 
   def index
-    @environments = Environment.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
+    @environments = resource_base.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
     @host_counter = Host.group(:environment_id).where(:environment_id => @environments.select(&:id)).count
   end
 
diff --git a/app/controllers/fact_values_controller.rb b/app/controllers/fact_values_controller.rb
index 97ee081bd..65f441d01 100644
--- a/app/controllers/fact_values_controller.rb
+++ b/app/controllers/fact_values_controller.rb
@@ -4,11 +4,12 @@ class FactValuesController < ApplicationController
   before_filter :setup_search_options, :only => :index
 
   def index
+    base = resource_base.no_timestamp_facts
     begin
-      values = FactValue.my_facts.search_for(params[:search], :order => params[:order])
+      values = base.my_facts.search_for(params[:search], :order => params[:order])
     rescue => e
       error e.to_s
-      values = FactValue.search_for ""
+      values = base.search_for ""
     end
 
     conds = (original_search_parameter || '').split(/AND|OR/i)
@@ -26,4 +27,10 @@ class FactValuesController < ApplicationController
     @fact_values = values.no_timestamp_facts.required_fields.paginate :page => params[:page]
   end
 
+  private
+
+  def controller_permission
+    'facts'
+  end
+
 end
diff --git a/app/controllers/filters_controller.rb b/app/controllers/filters_controller.rb
new file mode 100644
index 000000000..8d52ad049
--- /dev/null
+++ b/app/controllers/filters_controller.rb
@@ -0,0 +1,80 @@
+class FiltersController < ApplicationController
+  include Foreman::Controller::AutoCompleteSearch
+
+  before_filter :find_role
+  before_filter :setup_search_options, :only => :index
+
+  def index
+    @filters = resource_base.includes(:role, :permissions).search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
+    @roles_authorizer = Authorizer.new(User.current, :collection => @filters.map(&:role_id))
+  end
+
+  def new
+    @filter = resource_base.build
+  end
+
+  def create
+    @filter = Filter.new(params[:filter])
+    if @filter.save
+      process_success :success_redirect => filters_path(:role_id => @role)
+    else
+      process_error
+    end
+  end
+
+  def edit
+    @filter = resource_base.includes(:permissions).find(params[:id])
+  end
+
+  def update
+    @filter = resource_base.find(params[:id])
+    if @filter.update_attributes(params[:filter])
+      process_success :success_redirect => filters_path(:role_id => @role)
+    else
+      process_error
+    end
+  end
+
+  def destroy
+    @filter = resource_base.find(params[:id])
+    if @filter.destroy
+      process_success :success_redirect => filters_path(:role_id => @role)
+    else
+      process_error
+    end
+  end
+
+  protected
+
+  def find_role
+    @role = Role.find_by_id(role_id)
+  end
+
+  def resource_base
+    @resource_base ||= @role.present? ?
+        @role.filters.authorized(current_permission) :
+        Filter.scoped.authorized(current_permission)
+  end
+
+  def role_id
+    params[:role_id]
+  end
+
+  def setup_search_options
+    @original_search_parameter = params[:search]
+    params[:search] ||= ""
+    params.keys.each do |param|
+      if param =~ /role_id$/
+        unless (role = Role.find_by_id(params[param])).blank?
+          query = "role = \"#{role.name}\""
+          params[:search] += query unless params[:search].include? query
+        end
+      elsif param =~ /(\w+)_id$/
+        unless params[param].blank?
+          query = "#{$1} = #{params[param]}"
+          params[:search] += query unless params[:search].include? query
+        end
+      end
+    end
+  end
+end
diff --git a/app/controllers/hostgroups_controller.rb b/app/controllers/hostgroups_controller.rb
index 24588e653..b0a7ab282 100644
--- a/app/controllers/hostgroups_controller.rb
+++ b/app/controllers/hostgroups_controller.rb
@@ -1,11 +1,10 @@
 class HostgroupsController < ApplicationController
   include Foreman::Controller::HostDetails
   include Foreman::Controller::AutoCompleteSearch
-
-  before_filter :find_hostgroup, :only => [:edit, :update, :destroy, :clone]
+  before_filter :find_by_name, :only => [:nest, :clone, :edit, :update, :destroy]
 
   def index
-    @hostgroups = Hostgroup.my_groups.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
+    @hostgroups = resource_base.search_for(params[:search], :order => params[:order]).paginate :page => params[:page]
   end
 
   def new
@@ -13,7 +12,7 @@ class HostgroupsController < ApplicationController
   end
 
   def nest
-    @parent = Hostgroup.find(params[:id])
+    @parent = @hostgroup
     @hostgroup = Hostgroup.new(:parent_id => @parent.id)
 
     load_vars_for_ajax
@@ -56,8 +55,6 @@ class HostgroupsController < ApplicationController
   end
 
   def edit
-    auth  = User.current.admin? ? true : Hostgroup.my_groups.include?(@hostgroup)
-    not_found and return unless auth
     load_vars_for_ajax
   end
 
@@ -96,7 +93,7 @@ class HostgroupsController < ApplicationController
 
   def process_hostgroup
 
-    @parent = Hostgroup.find(params[:hostgroup][:parent_id]) if params[:hostgroup][:parent_id].to_i > 0
+    @parent = Hostgroup.authorized(:view_hostgroups).find(params[:hostgroup][:parent_id]) if params[:hostgroup][:parent_id].to_i > 0
     return head(:not_found) unless @parent
 
     @hostgroup = Hostgroup.new(params[:hostgroup])
@@ -117,10 +114,6 @@ class HostgroupsController < ApplicationController
 
   private
 
-  def find_hostgroup
-    @hostgroup = Hostgroup.find(params[:id])
-  end
-
   def load_vars_for_ajax
     return unless @hostgroup
     @architecture    = @hostgroup.architecture
@@ -140,4 +133,13 @@ class HostgroupsController < ApplicationController
     User.where(:subscribe_to_all_hostgroups => true)
   end
 
+  def action_permission
+    case params[:action]
+      when 'nest', 'clone'
+        'view'
+      else
+        super
+    end
+  end
+
 end
diff --git a/app/controllers/hosts_controller.rb b/app/controllers/hosts_controller.rb
index 349c048bf..f9a80c1b3 100644
--- a/app/controllers/hosts_controller.rb
+++ b/app/controllers/hosts_controller.rb
@@ -8,26 +8,30 @@ class HostsController < ApplicationController
   SEARCHABLE_ACTIONS= %w[index active errors out_of_sync pending disabled ]
   AJAX_REQUESTS=%w{compute_resource_selected hostgroup_or_environment_selected current_parameters puppetclass_parameters process_hostgroup process_taxonomy}
   BOOT_DEVICES={ :disk => N_('Disk'), :cdrom => N_('CDROM'), :pxe => N_('PXE'), :bios => N_('BIOS') }
+  MULTIPLE_ACTIONS = %w(multiple_parameters update_multiple_parameters  select_multiple_hostgroup
+                        update_multiple_hostgroup select_multiple_environment update_multiple_environment
+                        multiple_destroy submit_multiple_destroy multiple_build
+                        submit_multiple_build multiple_disable submit_multiple_disable
+                        multiple_enable submit_multiple_enable multiple_puppetrun
+                        update_multiple_puppetrun multiple_disassociate update_multiple_disassociate)
 
   add_puppetmaster_filters PUPPETMASTER_ACTIONS
   before_filter :ajax_request, :only => AJAX_REQUESTS
-  before_filter :find_multiple, :only => [:update_multiple_parameters, :multiple_build,
-    :select_multiple_hostgroup, :select_multiple_environment, :multiple_parameters, :multiple_destroy,
-    :multiple_enable, :multiple_disable, :submit_multiple_disable, :submit_multiple_enable, :update_multiple_hostgroup,
-    :update_multiple_environment, :submit_multiple_build, :submit_multiple_destroy, :update_multiple_puppetrun,
-    :multiple_puppetrun, :multiple_disassociate, :update_multiple_disassociate]
-  before_filter :find_by_name, :only => %w[show edit update destroy puppetrun setBuild cancelBuild
-    storeconfig_klasses clone pxe_config toggle_manage power console bmc ipmi_boot disassociate]
+  before_filter :find_by_name, :only => [:show, :clone, :edit, :update, :destroy, :puppetrun,
+                                         :setBuild, :cancelBuild, :power, :bmc, :ipmi_boot,
+                                         :console, :toggle_manage, :pxe_config,
+                                         :storeconfig_klasses, :disassociate]
   before_filter :taxonomy_scope, :only => [:new, :edit] + AJAX_REQUESTS
   before_filter :set_host_type, :only => [:update]
+  before_filter :find_multiple, :only => MULTIPLE_ACTIONS
   helper :hosts, :reports
 
   def index (title = nil)
     begin
-      search = Host.my_hosts.search_for(params[:search],:order => params[:order])
+      search = resource_base.search_for(params[:search], :order => params[:order])
     rescue => e
       error e.to_s
-      search = Host.my_hosts.search_for ''
+      search = resource_base.search_for ''
     end
     respond_to do |format|
       format.html do
@@ -35,6 +39,7 @@ class HostsController < ApplicationController
         # SQL optimizations queries
         @last_reports = Report.where(:host_id => @hosts.map(&:id)).group(:host_id).maximum(:id)
         # rendering index page for non index page requests (out of sync hosts etc)
+        @hostgroup_authorizer = Authorizer.new(User.current, :collection => @hosts.map(&:hostgroup_id).compact.uniq)
         render :index if title and (@title = title)
       end
       format.yaml do
@@ -133,7 +138,7 @@ class HostsController < ApplicationController
     return not_found unless (params[:host] && (id=params[:host][:compute_resource_id]))
     Taxonomy.as_taxonomy @organization, @location do
       compute_profile_id = params[:host][:compute_profile_id] || Hostgroup.find_by_id(params[:host][:hostgroup_id]).try(:compute_profile_id)
-      compute_resource = ComputeResource.find(id)
+      compute_resource = ComputeResource.authorized(:view_compute_resources).find_by_id(id)
       render :partial => "compute", :locals => { :compute_resource => compute_resource,
                                                  :vm_attrs         => compute_resource.compute_profile_attributes_for(compute_profile_id) }
     end
@@ -168,8 +173,8 @@ class HostsController < ApplicationController
 
   def externalNodes
     certname = params[:name]
-    @host ||= Host.find_by_certname certname
-    @host ||= Host.find_by_name certname
+    @host ||= resource_base.find_by_certname certname
+    @host ||= resource_base.find_by_name certname
     not_found and return unless @host
 
     begin
@@ -475,7 +480,7 @@ class HostsController < ApplicationController
     @compute_profile = @hostgroup.compute_profile
 
     @host = if params[:host][:id]
-      host = Host::Base.find(params[:host][:id])
+      host = Host::Base.authorized(:view_hosts, Host).find(params[:host][:id])
       host = host.becomes Host::Managed
       host.attributes = params[:host]
       host
@@ -528,8 +533,41 @@ class HostsController < ApplicationController
 
   private
 
+  def resource_base
+    @resource_base ||= Host.authorized(current_permission, Host)
+  end
+
+  def action_permission
+    case params[:action]
+      when 'clone', 'externalNodes', 'bmc', 'pxe_config', 'storeconfig_klasses',
+          'active', 'errors', 'out_of_sync', 'pending', 'disabled'
+        :view
+      when 'puppetrun', 'multiple_puppetrun', 'update_multiple_puppetrun'
+        :puppetrun
+      when 'setBuild', 'cancelBuild', 'multiple_build', 'submit_multiple_build'
+        :build
+      when 'power'
+        :power
+      when 'ipmi_boot'
+        :ipmi_boot
+      when 'console'
+        :console
+      when 'toggle_manage', 'multiple_parameters', 'update_multiple_parameters',
+          'select_multiple_hostgroup', 'update_multiple_hostgroup', 'select_multiple_environment',
+          'update_multiple_environment', 'multiple_disable', 'submit_multiple_disable',
+          'multiple_enable', 'submit_multiple_enable',
+          'update_multiple_organization', 'select_multiple_organization',
+          'update_multiple_location', 'select_multiple_location'
+        :edit
+      when 'multiple_destroy', 'submit_multiple_destroy'
+        :destroy
+      else
+        super
+    end
+  end
+
   def refresh_host
-    @host = Host::Base.find_by_id(params['host_id'])
+    @host = Host::Base.authorized(:view_hosts, Host).find_by_id(params['host_id'])
     if @host
       unless @host.kind_of?(Host::Managed)
         @host      = @host.becomes(Host::Managed)
@@ -585,13 +623,14 @@ class HostsController < ApplicationController
     # determine if we are searching for a numerical id or plain name
 
     if id =~ /^\d+$/
-      @host = Host::Base.my_hosts.find_by_id id.to_i
+      @host = resource_base.find_by_id id.to_i
     else
-      @host = Host::Base.my_hosts.find_by_name id.downcase
-      @host ||= Host::Base.my_hosts.find_by_mac params[:host][:mac] if params[:host] && params[:host][:mac]
+      @host = resource_base.find_by_name id.downcase
+      @host ||= resource_base.find_by_mac params[:host][:mac] if params[:host] && params[:host][:mac]
     end
 
-    not_found and return false unless @host
+    not_found and return(false) unless @host
+    @host
   end
 
   def load_vars_for_ajax
@@ -611,7 +650,7 @@ class HostsController < ApplicationController
   def find_multiple
   # Lets search by name or id and make sure one of them exists first
     if params[:host_names].present? or params[:host_ids].present?
-      @hosts = Host::Base.where("id IN (?) or name IN (?)", params[:host_ids], params[:host_names] )
+      @hosts = resource_base.where("id IN (?) or name IN (?)", params[:host_ids], params[:host_names] )
       if @hosts.empty?
         error _('No hosts were found with that id or name')
         redirect_to(hosts_path) and return false
@@ -621,9 +660,12 @@ class HostsController < ApplicationController
       redirect_to(hosts_path) and return false
     end
 
-    rescue => e
-      error _("Something went wrong while selecting hosts - %s") % (e)
-      redirect_to hosts_path
+    return @hosts
+  rescue => e
+    error _("Something went wrong while selecting hosts - %s") % (e)
+    logger.debug e.message
+    logger.debug e.backtrace.join("\n")
+    redirect_to hosts_path and return false
   end
 
   def toggle_hostmode mode=true
diff --git a/app/controllers/images_controller.rb b/app/controllers/images_controller.rb
index 258a10605..248eccd92 100644
--- a/app/controllers/images_controller.rb
+++ b/app/controllers/images_controller.rb
@@ -1,10 +1,10 @@
 class ImagesController < ApplicationController
   before_filter :find_compute_resource
-  before_filter :find_by_name, :except => [:index, :new, :create]
+  before_filter :find_by_name, :only => [:edit, :update, :destroy]
 
   def index
     # Listing images in /hosts/new consumes this method as JSON
-    values = @compute_resource.images.search_for(params[:search], :order => params[:order])
+    values = resource_base.where(:compute_resource_id => @compute_resource.id).search_for(params[:search], :order => params[:order])
     respond_to do |format|
       format.html { @images = values.paginate :page => params[:page] }
       format.json { render :json => values }
@@ -46,7 +46,7 @@ class ImagesController < ApplicationController
   private
 
   def find_compute_resource
-    @compute_resource = ComputeResource.find(params[:compute_resource_id])
+    @compute_resource = ComputeResource.authorized(:view_compute_resources).find(params[:compute_resource_id])
   end
 
 end
diff --git a/app/controllers/lookup_keys_controller.rb b/app/controllers/lookup_keys_controller.rb
index 39cef7812..4a26e3e7c 100644
--- a/app/controllers/lookup_keys_controller.rb
+++ b/app/controllers/lookup_keys_controller.rb
@@ -1,10 +1,11 @@
 class LookupKeysController < ApplicationController
   include Foreman::Controller::AutoCompleteSearch
-  before_filter :find_by_key, :except => :index
   before_filter :setup_search_options, :only => :index
+  before_filter :find_by_key, :only => [:edit, :update, :destroy], :if => Proc.new { params[:id] }
 
   def index
-    @lookup_keys = LookupKey.search_for(params[:search], :order => params[:order]).includes(:puppetclass).paginate(:page => params[:page])
+    @lookup_keys = resource_base.search_for(params[:search], :order => params[:order]).includes(:param_classes, :puppetclass).paginate(:page => params[:page])
+    @puppetclass_authorizer = Authorizer.new(User.current, :collection => @lookup_keys.map(&:puppetclass_id).compact.uniq)
   end
 
   def edit
@@ -27,10 +28,14 @@ class LookupKeysController < ApplicationController
   end
 
   private
+
   def find_by_key
-    if params[:id]
-      @lookup_key = LookupKey.find(params[:id])
-      not_found and return if @lookup_key.blank?
-    end
+    @lookup_key = resource_base.find(params[:id])
+    not_found and return if @lookup_key.blank?
+    @lookup_key
+  end
+
+  def controller_permission
+    'external_variables'
   end
 end
diff --git a/app/controllers/media_controller.rb b/app/controllers/media_controller.rb
index abd65fd76..76d13b229 100644
--- a/app/controllers/media_controller.rb
+++ b/app/controllers/media_controller.rb
@@ -1,9 +1,9 @@
 class MediaController < ApplicationController
   include Foreman::Controller::AutoCompleteSearch
-  before_filter :find_medium, :only => %w{edit update destroy}
+  before_filter :find_by_name, :only => %w{edit update destroy}
 
   def index
-    @media = Medium.includes(:operatingsystems).search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
+    @media = resource_base.includes(:operatingsystems).search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
   end
 
   def new
@@ -38,9 +38,4 @@ class MediaController < ApplicationController
     end
   end
 
-  private
-  def find_medium
-    @medium = Medium.find(params[:id])
-  end
-
 end
diff --git a/app/controllers/models_controller.rb b/app/controllers/models_controller.rb
index 23b00226c..ef7de838b 100644
--- a/app/controllers/models_controller.rb
+++ b/app/controllers/models_controller.rb
@@ -1,8 +1,9 @@
 class ModelsController < ApplicationController
   include Foreman::Controller::AutoCompleteSearch
+  before_filter :find_by_name, :only => [:edit, :update, :destroy]
 
   def index
-    @models  = Model.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
+    @models       = resource_base.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
     @host_counter = Host.group(:model_id).where(:model_id => @models.select(&:id)).count
   end
 
@@ -20,11 +21,9 @@ class ModelsController < ApplicationController
   end
 
   def edit
-    @model = Model.find(params[:id])
   end
 
   def update
-    @model = Model.find(params[:id])
     if @model.update_attributes(params[:model])
       process_success
     else
@@ -33,11 +32,11 @@ class ModelsController < ApplicationController
   end
 
   def destroy
-    @model = Model.find(params[:id])
     if @model.destroy
       process_success
     else
       process_error
     end
   end
+
 end
diff --git a/app/controllers/operatingsystems_controller.rb b/app/controllers/operatingsystems_controller.rb
index 518725d06..c1ad640f0 100644
--- a/app/controllers/operatingsystems_controller.rb
+++ b/app/controllers/operatingsystems_controller.rb
@@ -1,9 +1,9 @@
 class OperatingsystemsController < ApplicationController
   include Foreman::Controller::AutoCompleteSearch
-  before_filter :find_os, :only => %w{edit update destroy bootfiles}
+  before_filter :find_by_name, :only => %w{edit update destroy}
 
   def index
-    @operatingsystems = Operatingsystem.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
+    @operatingsystems = resource_base.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
     @host_counter     = Host.group(:operatingsystem_id).where(:operatingsystem_id => @operatingsystems.collect(&:id)).count
   end
 
@@ -44,10 +44,4 @@ class OperatingsystemsController < ApplicationController
       process_error
     end
   end
-
-  private
-  def find_os
-    @operatingsystem = Operatingsystem.find(params[:id])
-  end
-
 end
diff --git a/app/controllers/permissions_controller.rb b/app/controllers/permissions_controller.rb
new file mode 100644
index 000000000..702c88496
--- /dev/null
+++ b/app/controllers/permissions_controller.rb
@@ -0,0 +1,18 @@
+class PermissionsController < ApplicationController
+  include FiltersHelper
+  respond_to :js
+
+  def index
+    type = params[:resource_type].blank? ? nil : params[:resource_type]
+    @permissions = Permission.find_all_by_resource_type(type)
+    @search_path = search_path(type)
+    @granular = granular?(type)
+  end
+
+  private
+
+  def granular?(type)
+    Filter.new(:resource_type => type).granular?
+  end
+
+end
diff --git a/app/controllers/ptables_controller.rb b/app/controllers/ptables_controller.rb
index 3b507818f..ab34c65a3 100644
--- a/app/controllers/ptables_controller.rb
+++ b/app/controllers/ptables_controller.rb
@@ -1,9 +1,10 @@
 class PtablesController < ApplicationController
   include Foreman::Controller::AutoCompleteSearch
-  before_filter :find_ptable, :only => %w{edit update destroy}
+  before_filter :find_by_name, :only => [:edit, :update, :destroy]
 
   def index
-    @ptables = Ptable.includes(:operatingsystems).search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
+    @ptables = resource_base.includes(:operatingsystems).
+      search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
   end
 
   def new
@@ -38,9 +39,4 @@ class PtablesController < ApplicationController
     end
   end
 
-  private
-  def find_ptable
-    @ptable = Ptable.find(params[:id])
-  end
-
 end
diff --git a/app/controllers/puppetca_controller.rb b/app/controllers/puppetca_controller.rb
index 7e0dbf34e..49af71fff 100644
--- a/app/controllers/puppetca_controller.rb
+++ b/app/controllers/puppetca_controller.rb
@@ -1,10 +1,9 @@
 class PuppetcaController < ApplicationController
-  before_filter :find_proxy
 
   def index
+    @proxy = find_proxy
     # expire cache if forced
     Rails.cache.delete("ca_#{@proxy.id}") if params[:expire_cache] == "true"
-
     begin
       certs = if params[:state].blank?
                 SmartProxies::PuppetCA.find_by_state(@proxy, "valid") + SmartProxies::PuppetCA.find_by_state(@proxy, "pending")
@@ -26,6 +25,7 @@ class PuppetcaController < ApplicationController
   end
 
   def update
+    @proxy = find_proxy(:edit_smart_proxies_puppetca)
     cert = SmartProxies::PuppetCA.find(@proxy, params[:id])
     if cert.sign
       process_success({ :success_redirect => smart_proxy_puppetca_index_path(@proxy, :state => params[:state]), :object_name => cert.to_s })
@@ -37,6 +37,7 @@ class PuppetcaController < ApplicationController
   end
 
   def destroy
+    @proxy = find_proxy(:destroy_smart_proxies_puppetca)
     cert = SmartProxies::PuppetCA.find(@proxy, params[:id])
     if cert.destroy
       process_success({ :success_redirect => smart_proxy_puppetca_index_path(@proxy, :state => params[:state]), :object_name => cert.to_s })
@@ -47,8 +48,8 @@ class PuppetcaController < ApplicationController
 
   private
 
-  def find_proxy
-    @proxy = SmartProxy.find(params[:smart_proxy_id])
+  def find_proxy(permission = :view_smart_proxies_puppetca)
+    SmartProxy.authorized(permission).find(params[:smart_proxy_id])
   end
 
 end
diff --git a/app/controllers/puppetclasses_controller.rb b/app/controllers/puppetclasses_controller.rb
index f276b74dd..63fc2727d 100644
--- a/app/controllers/puppetclasses_controller.rb
+++ b/app/controllers/puppetclasses_controller.rb
@@ -1,15 +1,16 @@
 class PuppetclassesController < ApplicationController
   include Foreman::Controller::Environments
   include Foreman::Controller::AutoCompleteSearch
-  before_filter :find_by_name, :only => [:edit, :update, :destroy, :assign]
+  before_filter :find_by_name, :only => [:edit, :update, :destroy]
   before_filter :setup_search_options, :only => :index
   before_filter :reset_redirect_to_url, :only => :index
   before_filter :store_redirect_to_url, :only => :edit
 
   def index
-    @puppetclasses = Puppetclass.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page])
+    @puppetclasses = resource_base.search_for(params[:search], :order => params[:order]).includes(:environments, :hostgroups).paginate(:page => params[:page])
     @host_counter = Host.group(:puppetclass_id).joins(:puppetclasses).where(:puppetclasses => {:id => @puppetclasses.collect(&:id)}).count
     @keys_counter = Puppetclass.joins(:class_params).select('distinct environment_classes.lookup_key_id').group(:name).count
+    @hostgroups_authorizer = Authorizer.new(User.current, :collection => HostgroupClass.find_all_by_puppetclass_id(@puppetclasses.map(&:id)).compact.uniq.map(&:hostgroup_id))
   end
 
   def new
@@ -96,8 +97,9 @@ class PuppetclassesController < ApplicationController
 
   def find_by_name
     not_found and return if params[:id].blank?
-    pc = Puppetclass.includes(:class_params => [:environment_classes, :environments, :lookup_values])
+    pc = resource_base.includes(:class_params => [:environment_classes, :environments, :lookup_values])
     @puppetclass = (params[:id] =~ /\A\d+\Z/) ? pc.find(params[:id]) : pc.find_by_name(params[:id])
     not_found and return unless @puppetclass
   end
+
 end
diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb
index 922af0abc..26eb17246 100644
--- a/app/controllers/reports_controller.rb
+++ b/app/controllers/reports_controller.rb
@@ -4,24 +4,25 @@ class ReportsController < ApplicationController
   before_filter :setup_search_options, :only => :index
 
   def index
-    @reports = Report.my_reports.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page], :per_page => params[:per_page]).includes(:host)
+    report_authorized = resource_base.my_reports
+    @reports = report_authorized.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page], :per_page => params[:per_page]).includes(:host)
   end
 
   def show
     # are we searching for the last report?
     if params[:id] == "last"
       conditions = { :host_id => Host.find_by_name(params[:host_id]).try(:id) } unless params[:host_id].blank?
-      params[:id] = Report.my_reports.where(conditions).maximum(:id)
+      params[:id] = resource_base.where(conditions).maximum(:id)
     end
 
     return not_found if params[:id].blank?
 
-    @report = Report.my_reports.includes(:logs => [:message, :source]).find(params[:id])
+    @report = resource_base.includes(:logs => [:message, :source]).find(params[:id])
     @offset = @report.reported_at - @report.created_at
   end
 
   def destroy
-    @report = Report.find(params[:id])
+    @report = resource_base.find(params[:id])
     if @report.destroy
       notice _("Successfully destroyed report.")
     else
diff --git a/app/controllers/roles_controller.rb b/app/controllers/roles_controller.rb
index 3b6008a6f..bbf5c5b26 100644
--- a/app/controllers/roles_controller.rb
+++ b/app/controllers/roles_controller.rb
@@ -17,7 +17,7 @@
 
 class RolesController < ApplicationController
   include Foreman::Controller::AutoCompleteSearch
-  before_filter :require_admin
+  before_filter :find_by_id, :only => [:clone, :edit, :update, :destroy]
 
   def index
     @roles = Role.search_for(params[:search], :order => params[:order]).paginate :page => params[:page]
@@ -25,11 +25,12 @@ class RolesController < ApplicationController
 
   def new
     # Prefills the form with 'default user' role permissions
-    @role        = Role.new({:permissions => Role.default_user.permissions})
+    @role = Role.new({:permissions => Role.default_user.permissions})
   end
 
   def create
-    @role = Role.new(params[:role])
+    @role = role_from_form
+
     if @role.save
       process_success
     else
@@ -37,12 +38,19 @@ class RolesController < ApplicationController
     end
   end
 
+  def clone
+    @cloned_role      = true
+    @original_role_id = @role.id
+    flash[:notice] = _("Role cloned from role %{old_name}") %
+                      { :old_name => @role.name }
+    @role = Role.new
+    render :action => :new
+  end
+
   def edit
-    @role = Role.find(params[:id])
   end
 
   def update
-    @role = Role.find(params[:id])
     if @role.update_attributes(params[:role])
       process_success
     else
@@ -51,7 +59,6 @@ class RolesController < ApplicationController
   end
 
   def destroy
-    @role = Role.find(params[:id])
     if @role.destroy
       process_success
     else
@@ -59,16 +66,32 @@ class RolesController < ApplicationController
     end
   end
 
-  def report
-    @roles = Role.all(:order => 'builtin, name')
-    @permissions = Foreman::AccessControl.permissions.select { |p| !p.public? }
-    if request.post?
-      @roles.each do |role|
-        role.permissions = params[:permissions][role.id.to_s]
-        role.save
-      end
-      notice _("All non public permissions successfully updated")
-      redirect_to roles_url
+  private
+
+  def find_by_id
+    @role = Role.find(params[:id])
+  end
+
+  def action_permission
+    case params[:action]
+      when 'clone'
+        'view'
+      else
+        super
+    end
+  end
+
+  def role_from_form
+    if params[:original_role_id].present?
+      new_role = Role.find(params[:original_role_id]).
+                   dup(:include => [:filters => :filterings])
+      new_role.name    = params[:role][:name]
+      new_role.builtin = false
+    else
+      new_role = Role.new(params[:role])
     end
+
+    new_role
   end
+
 end
diff --git a/app/controllers/smart_proxies_controller.rb b/app/controllers/smart_proxies_controller.rb
index fa81940c7..dcc8d41d1 100644
--- a/app/controllers/smart_proxies_controller.rb
+++ b/app/controllers/smart_proxies_controller.rb
@@ -1,20 +1,22 @@
 class SmartProxiesController < ApplicationController
-  before_filter :find_by_id, :only => [:edit, :update, :destroy, :ping, :refresh]
+
+  include Foreman::Controller::AutoCompleteSearch
+  before_filter :find_by_name, :only => [:edit, :update, :refresh, :ping, :destroy]
 
   def index
-    @proxies = SmartProxy.includes(:features).paginate :page => params[:page]
+    @smart_proxies = resource_base.includes(:features).paginate :page => params[:page]
   end
 
   def new
-    @proxy = SmartProxy.new
+    @smart_proxy = SmartProxy.new
   end
 
   def create
-    @proxy = SmartProxy.new(params[:smart_proxy])
-    if @proxy.save
-      process_success :object => @proxy
+    @smart_proxy = SmartProxy.new(params[:smart_proxy])
+    if @smart_proxy.save
+      process_success :object => @smart_proxy
     else
-      process_error :object => @proxy
+      process_error :object => @smart_proxy
     end
   end
 
@@ -23,38 +25,46 @@ class SmartProxiesController < ApplicationController
 
   def ping
     respond_to do |format|
-      format.json {render :json => errors_hash(@proxy.refresh)}
+      format.json {render :json => errors_hash(@smart_proxy.refresh)}
     end
   end
 
   def refresh
-    old_features = @proxy.features
-    if @proxy.refresh.blank? && @proxy.save
-      msg = @proxy.features == old_features ? _("No changes found when refreshing features from %s.") : _("Successfully refreshed features from %s.")
-      process_success :object => @proxy, :success_msg => msg % @proxy.name
+    old_features = @smart_proxy.features
+    if @smart_proxy.refresh.blank? && @smart_proxy.save
+      msg = @smart_proxy.features == old_features ? _("No changes found when refreshing features from %s.") : _("Successfully refreshed features from %s.")
+      process_success :object => @smart_proxy, :success_msg => msg % @smart_proxy.name
     else
-      process_error :object => @proxy
+      process_error :object => @smart_proxy
     end
   end
 
   def update
-    if @proxy.update_attributes(params[:smart_proxy])
-      process_success :object => @proxy
+    if @smart_proxy.update_attributes(params[:smart_proxy])
+      process_success :object => @smart_proxy
     else
-      process_error :object => @proxy
+      process_error :object => @smart_proxy
     end
   end
 
   def destroy
-    if @proxy.destroy
-      process_success :object => @proxy
+    if @smart_proxy.destroy
+      process_success :object => @smart_proxy
     else
-      process_error :object => @proxy
+      process_error :object => @smart_proxy
     end
   end
 
   private
-  def find_by_id
-    @proxy = SmartProxy.find(params[:id])
+
+  def action_permission
+    case params[:action]
+      when 'refresh'
+        :edit
+      when 'ping'
+        :view
+      else
+        super
+    end
   end
 end
diff --git a/app/controllers/statistics_controller.rb b/app/controllers/statistics_controller.rb
index 6d744c81c..294c328d4 100644
--- a/app/controllers/statistics_controller.rb
+++ b/app/controllers/statistics_controller.rb
@@ -1,18 +1,18 @@
 class StatisticsController < ApplicationController
 
   def index
-    @os_count    = Host.my_hosts.count_distribution :operatingsystem
-    @arch_count  = Host.my_hosts.count_distribution :architecture
-    @env_count   = Host.my_hosts.count_distribution :environment
-    @klass_count = Host.my_hosts.count_habtm "puppetclass"
-    @cpu_count   = FactValue.my_facts.count_each "processorcount"
-    @model_count = FactValue.my_facts.count_each "manufacturer"
-    @mem_size    = FactValue.my_facts.mem_average "memorysize"
-    @mem_free    = FactValue.my_facts.mem_average "memoryfree"
-    @swap_size   = FactValue.my_facts.mem_average "swapsize"
-    @swap_free   = FactValue.my_facts.mem_average "swapfree"
-    @mem_totsize = FactValue.my_facts.mem_sum "memorysize"
-    @mem_totfree = FactValue.my_facts.mem_sum "memoryfree"
+    @os_count    = Host.authorized(:view_hosts, Host).count_distribution :operatingsystem
+    @arch_count  = Host.authorized(:view_hosts, Host).count_distribution :architecture
+    @env_count   = Host.authorized(:view_hosts, Host).count_distribution :environment
+    @klass_count = Host.authorized(:view_hosts, Host).count_habtm "puppetclass"
+    @cpu_count   = FactValue.authorized(:view_facts).my_facts.count_each "processorcount"
+    @model_count = FactValue.authorized(:view_facts).my_facts.count_each "manufacturer"
+    @mem_size    = FactValue.authorized(:view_facts).my_facts.mem_average "memorysize"
+    @mem_free    = FactValue.authorized(:view_facts).my_facts.mem_average "memoryfree"
+    @swap_size   = FactValue.authorized(:view_facts).my_facts.mem_average "swapsize"
+    @swap_free   = FactValue.authorized(:view_facts).my_facts.mem_average "swapfree"
+    @mem_totsize = FactValue.authorized(:view_facts).my_facts.mem_sum "memorysize"
+    @mem_totfree = FactValue.authorized(:view_facts).my_facts.mem_sum "memoryfree"
     respond_to do |format|
       format.html
       format.json do
diff --git a/app/controllers/subnets_controller.rb b/app/controllers/subnets_controller.rb
index 151f09de4..6239ec4a4 100644
--- a/app/controllers/subnets_controller.rb
+++ b/app/controllers/subnets_controller.rb
@@ -1,8 +1,9 @@
 class SubnetsController < ApplicationController
   include Foreman::Controller::AutoCompleteSearch
+  before_filter :find_by_name, :only => [:edit, :update, :destroy]
 
   def index
-    @subnets = Subnet.search_for(params[:search], :order => params[:order]).includes(:domains, :dhcp).paginate :page => params[:page]
+    @subnets = resource_base.search_for(params[:search], :order => params[:order]).includes(:domains, :dhcp).paginate :page => params[:page]
   end
 
   def new
@@ -19,11 +20,9 @@ class SubnetsController < ApplicationController
   end
 
   def edit
-    @subnet = Subnet.find(params[:id])
   end
 
   def update
-    @subnet = Subnet.find(params[:id])
     if @subnet.update_attributes(params[:subnet])
       process_success
     else
@@ -32,7 +31,6 @@ class SubnetsController < ApplicationController
   end
 
   def destroy
-    @subnet = Subnet.find(params[:id])
     if @subnet.destroy
       process_success
     else
@@ -46,7 +44,7 @@ class SubnetsController < ApplicationController
     organization = params[:organization_id].blank? ? nil : Organization.find(params[:organization_id])
     location = params[:location_id].blank? ? nil : Location.find(params[:location_id])
     Taxonomy.as_taxonomy organization, location do
-      not_found and return unless (subnet = Subnet.find(s))
+      not_found and return unless (subnet = Subnet.authorized(:view_subnets).find(s))
       if (ip = subnet.unused_ip(params[:host_mac]))
         render :json => {:ip => ip}
       else
@@ -55,7 +53,7 @@ class SubnetsController < ApplicationController
       end
     end
   rescue => e
-    logger.warn "Failed to query #{subnet} for free ip: #{e}"
+    logger.warn "Failed to query subnet #{s} for free ip: #{e}"
     head :status => 500
   end
 
diff --git a/app/controllers/usergroups_controller.rb b/app/controllers/usergroups_controller.rb
index 3b43c0a73..1eca10ebc 100644
--- a/app/controllers/usergroups_controller.rb
+++ b/app/controllers/usergroups_controller.rb
@@ -1,6 +1,9 @@
 class UsergroupsController < ApplicationController
+  include Foreman::Controller::AutoCompleteSearch
+  before_filter :find_by_name, :only => [:edit, :update, :destroy]
+
   def index
-    @usergroups = Usergroup.paginate :page => params[:page]
+    @usergroups = resource_base.paginate :page => params[:page]
   end
 
   def new
@@ -17,24 +20,30 @@ class UsergroupsController < ApplicationController
   end
 
   def edit
-    @usergroup = Usergroup.find(params[:id])
   end
 
   def update
-    @usergroup = Usergroup.find(params[:id])
     if @usergroup.update_attributes(params[:usergroup])
       process_success
     else
       process_error
     end
+  rescue Foreman::CyclicGraphException => e
+    @usergroup.errors[:usergroups] = e.record.errors[:base].join(' ')
+    process_error
   end
 
   def destroy
-    @usergroup = Usergroup.find(params[:id])
     if @usergroup.destroy
       process_success
     else
       process_error
     end
   end
+
+  private
+
+  def find_by_id(permission = :view_usergroups)
+    Usergroup.authorized(permission).find(params[:id])
+  end
 end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index afc426752..8e81dce4b 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -2,14 +2,14 @@ class UsersController < ApplicationController
   include Foreman::Controller::AutoCompleteSearch
   include Foreman::Controller::UsersMixin
 
-  before_filter :find_resource, :only => [:edit, :update, :destroy]
   skip_before_filter :require_mail, :only => [:edit, :update, :logout]
   skip_before_filter :require_login, :authorize, :session_expiry, :update_activity_time, :set_taxonomy, :set_gettext_locale_db, :only => [:login, :logout, :extlogout]
   skip_before_filter :authorize, :only => :extlogin
   after_filter       :update_activity_time, :only => :login
+  skip_before_filter :update_admin_flag, :only => :update
 
   def index
-    @users = User.search_for(params[:search], :order => params[:order]).includes(:auth_source).paginate(:page => params[:page])
+    @users = User.authorized(:view_users).search_for(params[:search], :order => params[:order]).includes(:auth_source).paginate(:page => params[:page])
   end
 
   def new
@@ -26,6 +26,7 @@ class UsersController < ApplicationController
 
   def edit
     editing_self?
+    @user = find_resource(:edit_users)
     if @user.user_facts.count == 0
       user_fact = @user.user_facts.build :operator => "==", :andor => "or"
       user_fact.fact_name_id = FactName.first.id if FactName.first
@@ -33,6 +34,9 @@ class UsersController < ApplicationController
   end
 
   def update
+    editing_self?
+    @user = find_resource(:edit_users)
+    update_admin_flag
     if @user.update_attributes(params[:user])
       update_sub_hostgroups_owners
 
@@ -43,6 +47,7 @@ class UsersController < ApplicationController
   end
 
   def destroy
+    @user = find_resource(:destroy_users)
     if @user == User.current
       notice _("You are currently logged in, suicidal?")
       redirect_to :back and return
@@ -102,8 +107,8 @@ class UsersController < ApplicationController
 
   private
 
-  def find_resource
-    @user ||= User.find(params[:id])
+  def find_resource(permission = :view_users)
+    editing_self? ? User.current : User.authorized(permission).find(params[:id])
   end
 
   def login_user(user)
diff --git a/app/helpers/ancestry_helper.rb b/app/helpers/ancestry_helper.rb
index a4a816ba8..0c13255f8 100644
--- a/app/helpers/ancestry_helper.rb
+++ b/app/helpers/ancestry_helper.rb
@@ -1,7 +1,7 @@
 module AncestryHelper
 
   # this helper is used for hostgroup names and location/organization names.  Both have ancestry
-  def label_with_link(obj, max_length = 1000)
+  def label_with_link(obj, max_length = 1000, authorizer = nil)
     return if obj.blank?
     options = (obj.title.to_s.size > max_length) ? { :'data-original-title' => obj.title, :rel => 'twipsy' } : {}
     nesting = obj.title.to_s.gsub(/[^\/]+\/?$/, '')
@@ -10,7 +10,7 @@ module AncestryHelper
     link_to_if_authorized(
       content_tag(:span,
                   content_tag(:span, nesting, :class => 'gray') + name, options),
-      send("hash_for_edit_#{obj.class.name.tableize.singularize}_path", obj))
+      send("hash_for_edit_#{obj.class.name.tableize.singularize}_path", obj).merge(:auth_object => obj, :authorizer => authorizer))
   end
 
-end
\ No newline at end of file
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 68ca18e65..e02279e48 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -113,31 +113,39 @@ module ApplicationHelper
     options
   end
 
-  def check_all_roles_links
-    link_to(_("Check all"), "#", :id => "check_all_roles", :remote => true) +
-    link_to(_("Uncheck all"), "#", :id => "uncheck_all_roles", :remote => true)
-  end
-
   # Return true if user is authorized for controller/action, otherwise false
-  # +controller+ : String or symbol for the controller
-  # +action+     : String or symbol for the action
-  def authorized_for(controller, action, id = nil)
-    User.current.allowed_to?({:controller => controller, :action => action, :id => id}) rescue false
+  # +options+ : Hash containing
+  #             :controller : String or symbol for the controller, defaults to params[:controller]
+  #             :action     : String or symbol for the action
+  #             :id         : Id parameter
+  #             :auth_action: String or symbol for the action, this has higher priority that :action
+  #             :auth_object: Specific object on which we may verify particular permission
+  #             :authorizer : Specific authorizer to perform authorization on (handy to inject authorizer with base collection)
+  #             :permission : Specific permission to check authorization on (handy on custom permission names)
+  def authorized_for(options)
+    action          = options.delete(:auth_action) || options.delete(:action)
+    object          = options.delete(:auth_object)
+    user            = User.current
+    controller      = options[:controller] || params[:controller]
+    controller_name = controller.to_s.gsub(/::/, "_").underscore
+    id              = options[:id]
+    permission      = options.delete(:permission) || [action, controller_name].join('_')
+
+    if object.nil?
+      user.allowed_to?({ :controller => controller_name, :action => action, :id => id }) rescue false
+    else
+      authorizer = options.delete(:authorizer) || Authorizer.new(user)
+      authorizer.can?(permission, object) rescue false
+    end
   end
 
   # Display a link if user is authorized, otherwise a string
   # +name+    : String to be displayed
-  # +options+ : Hash containing
-  #             :controller  : String or Symbol representing the controller
-  #             :auth_action : String or Symbol representing the action to be used for authorization checks
+  # +options+ : Hash containing options for authorized_for and link_to
   # +html_options+ : Hash containing html options for the link or span
   def link_to_if_authorized(name, options = {}, html_options = {})
-    auth_options = {
-      :controller => options[:controller]         || params[:controller],
-      :action     => options.delete(:auth_action) || options[:action],
-      :id         => options[:id]
-    }
-    if User.current.allowed_to?(auth_options)
+    enable_link = authorized_for(options)
+    if enable_link
       link_to name, options, html_options
     else
       link_to_function name, nil, html_options.merge!(:class => "#{html_options[:class]} disabled", :disabled => true)
@@ -149,16 +157,14 @@ module ApplicationHelper
     html_options = {:confirm => _('Are you sure?'), :method => :delete, :class => 'delete'}.merge(html_options)
     display_link_if_authorized(_("Delete"), options, html_options)
   end
+
   # Display a link if user is authorized, otherwise nothing
   # +name+    : String to be displayed
-  # +options+ : Hash containing
-  #             :controller  : String or Symbol representing the controller
-  #             :auth_action : String or Symbol representing the action to be used for authorization checks
+  # +options+ : Hash containing options for authorized_for and link_to
   # +html_options+ : Hash containing html options for the link or span
   def display_link_if_authorized(name, options = {}, html_options = {})
-    auth_action = options.delete :auth_action
     enable_link = html_options.has_key?(:disabled) ? !html_options[:disabled] : true
-    if enable_link and authorized_for(options[:controller] || params[:controller], auth_action || options[:action])
+    if enable_link and authorized_for(options)
       link_to(name, options, html_options)
     else
       ""
@@ -166,7 +172,9 @@ module ApplicationHelper
   end
 
   def authorized_edit_habtm klass, association, prefix = nil, options = {}
-    return edit_habtm(klass, association, prefix, options) if authorized_for params[:controller], params[:action]
+    if authorized_for :controller => params[:controller], :action => params[:action]
+      return edit_habtm(klass, association, prefix, options)
+    end
     show_habtm klass.send(association.name.pluralize.downcase)
   end
 
@@ -198,7 +206,7 @@ module ApplicationHelper
   end
 
   def auto_complete_search(name, val, options = {})
-    path = send("#{controller_name}_path")
+    path = options[:path] || send("#{controller_name}_path")
     options.merge!(:class => "autocomplete-input form-control", :'data-url' => "#{path}/auto_complete_#{name}" )
     text_field_tag(name, val, options)
   end
diff --git a/app/helpers/autosign_helper.rb b/app/helpers/autosign_helper.rb
index 1aa0784f1..7d0729d84 100644
--- a/app/helpers/autosign_helper.rb
+++ b/app/helpers/autosign_helper.rb
@@ -1,6 +1,6 @@
 module AutosignHelper
 
   def autosign_form
-    link_to "New", new_smart_proxy_autosign_path(@proxy) if authorized_for("SmartProxies::Autosign", :create)
+    link_to "New", new_smart_proxy_autosign_path(@proxy) if authorized_for(:controller => "SmartProxies::Autosign", :action => :create, :auth_object => @proxy)
   end
 end
diff --git a/app/helpers/bmc_helper.rb b/app/helpers/bmc_helper.rb
index 713750782..a4c816288 100644
--- a/app/helpers/bmc_helper.rb
+++ b/app/helpers/bmc_helper.rb
@@ -11,20 +11,22 @@ module BmcHelper
   def power_actions
     action_buttons(
       (PowerManager::SUPPORTED_ACTIONS - ['state']).map do |action|
-        display_link_if_authorized(_(action.to_s.capitalize), { :action => "power", :id => @host, :power_action => action},
+        display_link_if_authorized(_(action.to_s.capitalize),
+                                   { :action => "power", :id => @host, :power_action => action, :auth_object => @host },
                                    :confirm => _('Are you sure?'), :method => :put)
       end
     )
   end
 
   def boot_actions
-    controller_options = { :action => "ipmi_boot", :id => @host }
+    controller_options = { :action => "ipmi_boot", :id => @host, :auth_object => @host, :permission => 'ipmi_boot' }
 
     confirm = _('Are you sure?')
 
     links = HostsController::BOOT_DEVICES.map do |device,label|
-       display_link_if_authorized(_(label), controller_options.merge(:ipmi_device => device),
-                                  :confirm => confirm, :method => :put)
+      display_link_if_authorized(_(label),
+                                 controller_options.merge(:ipmi_device => device),
+                                 :confirm => confirm, :method => :put)
     end
     action_buttons("Select device", links)
   end
diff --git a/app/helpers/common_parameters_helper.rb b/app/helpers/common_parameters_helper.rb
index a0fc1f4e1..f27021589 100644
--- a/app/helpers/common_parameters_helper.rb
+++ b/app/helpers/common_parameters_helper.rb
@@ -1,10 +1,8 @@
 module CommonParametersHelper
   # Return true if user is authorized for controller/action OR controller/action@type, otherwise false
-  def authorized_via_my_scope(controller, action)
-    return true if authorized_for(controller, action)
-
-    operation = "#{action}_my_#{controller.singularize}".to_sym
-    User.current.allowed_to?(operation) and User.current.send(controller).include?(instance_variable_get("@#{controller.singularize}"))
+  # third argument may be specific object (usually for edit and destroy actions)
+  def authorized_via_my_scope(controller, action, object = nil)
+    authorized_for(:controller => controller, :action => action, :auth_object => object)
   end
 
   def parameters_title
diff --git a/app/helpers/compute_resources_helper.rb b/app/helpers/compute_resources_helper.rb
index 21c2aa01d..366757c9c 100644
--- a/app/helpers/compute_resources_helper.rb
+++ b/app/helpers/compute_resources_helper.rb
@@ -21,16 +21,16 @@ module ComputeResourcesHelper
     "class='label #{s ? "label-success" : "label-default"}'".html_safe
   end
 
-  def vm_power_action vm
-    opts = hash_for_power_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity)
+  def vm_power_action vm, authorizer = nil
+    opts = hash_for_power_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity).merge(:auth_object => @compute_resource, :permission => 'power_compute_resources_vms', :authorizer => authorizer)
     html = vm.ready? ? { :confirm =>_("Are you sure you want to power %{act} %{vm}?") % { :act => action_string(vm).downcase.strip, :vm => vm } , :class => "btn btn-danger" } :
                        { :class => "btn btn-info" }
 
     display_link_if_authorized "Power #{action_string(vm)}", opts, html.merge(:method => :put)
   end
 
-  def vm_pause_action vm
-    opts = hash_for_pause_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity)
+  def vm_pause_action vm, authorizer = nil
+    opts = hash_for_pause_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity).merge(:auth_object => @compute_resource, :permission => 'power_compute_resources_vms', :authorizer => authorizer)
     pause_action = vm.ready? ? _('Pause') : _('Resume')
     html = vm.state.downcase == 'paused' ? { :class => "btn btn-info" } :
                                            { :confirm =>_("Are you sure you want to %{act} %{vm}?") % { :act => pause_action.downcase, :vm => vm } , :class => "btn btn-danger" }
diff --git a/app/helpers/compute_resources_vms_helper.rb b/app/helpers/compute_resources_vms_helper.rb
index 49a2f91bf..1af1b3b9a 100644
--- a/app/helpers/compute_resources_vms_helper.rb
+++ b/app/helpers/compute_resources_vms_helper.rb
@@ -3,10 +3,10 @@ module ComputeResourcesVmsHelper
   # little helper to help show VM properties
   def prop method, title = nil
     content_tag :tr do
-      result = content_tag :td do
+      result = content_tag(:td) do
         title || method.to_s.humanize
       end
-      result += content_tag :td do
+      result += content_tag(:td) do
         value = @vm.send(method) rescue nil
         case value
         when Array
@@ -67,32 +67,32 @@ module ComputeResourcesVmsHelper
     end
   end
 
-  def available_actions(vm)
+  def available_actions(vm, authorizer = nil)
     case vm
     when Fog::Compute::OpenStack::Server
-      openstack_available_actions(vm)
+      openstack_available_actions(vm, authorizer)
     else
-      default_available_actions(vm)
+      default_available_actions(vm, authorizer)
     end
   end
 
-  def openstack_available_actions(vm)
+  def openstack_available_actions(vm, authorizer = nil)
     actions = []
     if vm.state == 'ACTIVE'
-      actions << vm_power_action(vm)
-      actions << vm_pause_action(vm)
+      actions << vm_power_action(vm, authorizer)
+      actions << vm_pause_action(vm, authorizer)
     elsif vm.state == 'PAUSED'
-      actions << vm_pause_action(vm)
+      actions << vm_pause_action(vm, authorizer)
     else
-      actions << vm_power_action(vm)
+      actions << vm_power_action(vm, authorizer)
     end
 
-    actions << display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity))
+    actions << display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity).merge(:auth_object => @compute_resource, :authorizer => authorizer))
   end
 
-  def default_available_actions(vm)
-    [vm_power_action(vm),
-     display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity))]
+  def default_available_actions(vm, authorizer = nil)
+    [vm_power_action(vm, authorizer),
+     display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity).merge(:auth_object => @compute_resource, :authorizer => authorizer))]
   end
 
   def vpc_security_group_hash(security_groups)
diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb
index 070af99a9..81d853ccd 100644
--- a/app/helpers/dashboard_helper.rb
+++ b/app/helpers/dashboard_helper.rb
@@ -39,7 +39,7 @@ module DashboardHelper
 
   def latest_events
     # 6 reports + header fits the events box nicely...
-    summary = Report.my_reports.interesting.search_for('reported > "7 days ago"').limit(6)
+    summary = Report.authorized(:view_reports).my_reports.interesting.search_for('reported > "7 days ago"').limit(6)
   end
 
   def translated_header(shortname, longname)
diff --git a/app/helpers/filters_helper.rb b/app/helpers/filters_helper.rb
new file mode 100644
index 000000000..05f0bf6b2
--- /dev/null
+++ b/app/helpers/filters_helper.rb
@@ -0,0 +1,22 @@
+module FiltersHelper
+  def search_path(type)
+    if type.nil?
+      ''
+    else
+      case type
+        when 'Image'
+          '' # images are nested resource for CR, we can't autocomplete
+        when 'HostClass'
+          '' # host classes is only used in API
+        when 'Parameter'
+          '' # parameter is only used in API
+        else
+          resource_path(type) + '/auto_complete_search'
+      end
+    end
+  end
+
+  def resource_path(type)
+    send(type.pluralize.underscore + '_path')
+  end
+end
diff --git a/app/helpers/hosts_and_hostgroups_helper.rb b/app/helpers/hosts_and_hostgroups_helper.rb
index 30ff7124e..244d2318b 100644
--- a/app/helpers/hosts_and_hostgroups_helper.rb
+++ b/app/helpers/hosts_and_hostgroups_helper.rb
@@ -8,7 +8,7 @@ module HostsAndHostgroupsHelper
   end
 
   def accessible_hostgroups
-    hg = (User.current.hostgroups.any? and !User.current.admin?) ? User.current.hostgroups.with_taxonomy_scope_override(@location,@organization) : Hostgroup.with_taxonomy_scope_override(@location,@organization)
+    hg = Hostgroup.with_taxonomy_scope_override(@location,@organization)
     hg.sort{ |l, r| l.to_label <=> r.to_label }
   end
 
@@ -19,7 +19,7 @@ module HostsAndHostgroupsHelper
   end
 
   def accessible_domains
-    (User.current.domains.any? and !User.current.admin?) ? User.current.domains.with_taxonomy_scope_override(@location,@organization) : Domain.with_taxonomy_scope_override(@location,@organization)
+    Domain.with_taxonomy_scope_override(@location,@organization)
   end
 
   def domain_subnets(domain=@domain)
diff --git a/app/helpers/hosts_helper.rb b/app/helpers/hosts_helper.rb
index 4ea526333..b2a49b59b 100644
--- a/app/helpers/hosts_helper.rb
+++ b/app/helpers/hosts_helper.rb
@@ -68,7 +68,8 @@ module HostsHelper
   end
 
   def authorized?
-    authorized_for(:hosts, :edit) or authorized_for(:hosts, :destroy)
+    authorized_for(:controller => :hosts, :action => :edit) or
+        authorized_for(:controller => :hosts, :action => :destroy)
   end
 
   def searching?
@@ -226,12 +227,14 @@ module HostsHelper
   def host_title_actions(host, vm)
     title_actions(
         button_group(
-            link_to_if_authorized(_("Edit"), hash_for_edit_host_path(:id => host), :title => _("Edit your host")),
+            link_to_if_authorized(_("Edit"), hash_for_edit_host_path(:id => host).merge(:auth_object => host), :title => _("Edit your host")),
             if host.build
-              link_to_if_authorized(_("Cancel Build"), hash_for_cancelBuild_host_path(:id => host), :disabled => host.can_be_built?,
+              link_to_if_authorized(_("Cancel Build"), hash_for_cancelBuild_host_path(:id => host).merge(:auth_object => host, :permission => 'build_hosts'),
+                                    :disabled => host.can_be_built?,
                                     :title                                                                 => _("Cancel build request for this host"))
             else
-              link_to_if_authorized(_("Build"), hash_for_setBuild_host_path(:id => host), :disabled => !host.can_be_built?,
+              link_to_if_authorized(_("Build"), hash_for_setBuild_host_path(:id => host).merge(:auth_object => host, :permission => 'build_hosts'),
+                                    :disabled => !host.can_be_built?,
                                     :title                                                       => _("Enable rebuild on next host boot"),
                                     :confirm                                                     => _("Rebuild %s on next reboot?\nThis would also delete all of its current facts and reports") % host)
             end
@@ -240,22 +243,24 @@ module HostsHelper
           button_group(
               if vm
                 html_opts = vm.ready? ? {:confirm => _('Are you sure?'), :class => "btn btn-danger"} : {:class => "btn btn-success"}
-                link_to_if_authorized _("Power%s") % state(vm.ready?), hash_for_power_host_path(:power_action => vm.ready? ? :stop : :start), html_opts.merge(:method => :put)
+                link_to_if_authorized _("Power%s") % state(vm.ready?), hash_for_power_host_path(:power_action => vm.ready? ? :stop : :start).merge(:auth_object => host, :permission => 'power_hosts'),
+                                      html_opts.merge(:method => :put)
               else
                 link_to(_("Unknown Power State"), '#', :disabled => true, :class => "btn btn-warning")
               end +
-                  link_to_if_authorized(_("Console"), hash_for_console_host_path(), {:disabled => vm.nil? || !vm.ready?, :class => "btn btn-info"})
+                  link_to_if_authorized(_("Console"), hash_for_console_host_path().merge(:auth_object => host, :permission => 'console_hosts'),
+                                        {:disabled => vm.nil? || !vm.ready?, :class => "btn btn-info"})
           )
         end,
         button_group(
           if host.try(:puppet_proxy)
-            link_to_if_authorized(_("Run puppet"), hash_for_puppetrun_host_path(:id => host).merge(:auth_action => :edit),
+            link_to_if_authorized(_("Run puppet"), hash_for_puppetrun_host_path(:id => host).merge(:auth_object => host, :permission => 'puppetrun_hosts'),
                                   :disabled => !Setting[:puppetrun],
                                   :title => _("Trigger a puppetrun on a node; requires that puppet run is enabled"))
           end
         ),
         button_group(
-            link_to_if_authorized(_("Delete"), hash_for_host_path(:id => host, :auth_action => :destroy),
+            link_to_if_authorized(_("Delete"), hash_for_host_path(:id => host).merge(:auth_object => host, :permission => 'destroy_hosts'),
                                   :class => "btn btn-danger", :confirm => _('Are you sure?'), :method => :delete)
         )
     )
diff --git a/app/helpers/layout_helper.rb b/app/helpers/layout_helper.rb
index c5bd2510b..8e709c096 100644
--- a/app/helpers/layout_helper.rb
+++ b/app/helpers/layout_helper.rb
@@ -125,6 +125,20 @@ module LayoutHelper
     end
   end
 
+  def autocomplete_f(f, attr, options = {})
+    field(f, attr, options) do
+      path = options.delete(:path) || send("#{f.object.class.pluralize.underscore}_path")
+      auto_complete_search(attr,
+                           f.object.send(attr).try(:squeeze, " "),
+                           options.merge(
+                               :placeholder => _("Filter") + ' ...',
+                               :path        => path,
+                                :name       => "#{f.object_name}[#{attr}]"
+                          )
+      ).html_safe
+    end
+  end
+
   def field(f, attr, options = {})
     error = f.object.errors[attr] if f && f.object.respond_to?(:errors)
     help_inline = help_inline(options.delete(:help_inline), error)
@@ -132,7 +146,9 @@ module LayoutHelper
     help_block  = content_tag(:span, options.delete(:help_block), :class => "help-block")
     size_class = options.delete(:size) || "col-md-4"
     content_tag(:div, :class=> "clearfix") do
-    content_tag :div, :class => "form-group #{error.empty? ? "" : 'has-error'}" do
+    content_tag :div, :class => "form-group #{error.empty? ? "" : 'has-error'}",
+               :id          => options.delete(:control_group_id) do
+
       label   = options.delete(:label)
       label ||= ((clazz = f.object.class).respond_to?(:gettext_translation_for_attribute_name) &&
                   s_(clazz.gettext_translation_for_attribute_name attr)) if f
diff --git a/app/models/architecture.rb b/app/models/architecture.rb
index 3931baca3..ec71a941d 100644
--- a/app/models/architecture.rb
+++ b/app/models/architecture.rb
@@ -1,5 +1,5 @@
 class Architecture < ActiveRecord::Base
-  include Authorization
+  include Authorizable
 
   before_destroy EnsureNotUsedBy.new(:hosts, :hostgroups)
 
diff --git a/app/models/auth_source.rb b/app/models/auth_source.rb
index 98eb2b88c..5912e8bb7 100644
--- a/app/models/auth_source.rb
+++ b/app/models/auth_source.rb
@@ -16,8 +16,8 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 class AuthSource < ActiveRecord::Base
-  include Authorization
   audited :allow_mass_assignment => true
+
   before_destroy EnsureNotUsedBy.new(:users)
   has_many :users
 
diff --git a/app/models/bookmark.rb b/app/models/bookmark.rb
index f24dd5de1..493fb7ddd 100644
--- a/app/models/bookmark.rb
+++ b/app/models/bookmark.rb
@@ -1,4 +1,6 @@
 class Bookmark < ActiveRecord::Base
+  include Authorizable
+
   belongs_to :owner, :polymorphic => true
   attr_accessible :name, :controller, :query, :public
   audited :allow_mass_assignment => true
diff --git a/app/models/cached_user_role.rb b/app/models/cached_user_role.rb
new file mode 100644
index 000000000..2c7cb32f4
--- /dev/null
+++ b/app/models/cached_user_role.rb
@@ -0,0 +1,10 @@
+class CachedUserRole < ActiveRecord::Base
+  attr_accessible :role_id, :user_id, :user_role_id, :role, :user, :user_role
+
+  belongs_to :user
+  belongs_to :role
+
+  # this UserRole created this cache
+  belongs_to :user_role
+
+end
diff --git a/app/models/cached_usergroup_member.rb b/app/models/cached_usergroup_member.rb
new file mode 100644
index 000000000..56f0dee35
--- /dev/null
+++ b/app/models/cached_usergroup_member.rb
@@ -0,0 +1,7 @@
+class CachedUsergroupMember < ActiveRecord::Base
+  attr_accessible :user_id, :usergroup_id, :user, :usergroup
+
+  belongs_to :user
+  belongs_to :usergroup
+
+end
diff --git a/app/models/compute_profile.rb b/app/models/compute_profile.rb
index 43e09b6bd..43af9e761 100644
--- a/app/models/compute_profile.rb
+++ b/app/models/compute_profile.rb
@@ -1,6 +1,7 @@
 class ComputeProfile < ActiveRecord::Base
 
-  include Authorization
+  include Authorizable
+
   attr_accessible :name
   audited
   has_associated_audits
diff --git a/app/models/compute_resource.rb b/app/models/compute_resource.rb
index afcfc18bd..5b2d3acfd 100644
--- a/app/models/compute_resource.rb
+++ b/app/models/compute_resource.rb
@@ -2,6 +2,7 @@ require 'fog_extensions'
 class ComputeResource < ActiveRecord::Base
   include Taxonomix
   include Encryptable
+  include Authorizable
   encrypts :password
   SUPPORTED_PROVIDERS = %w[Libvirt Ovirt EC2 Vmware Openstack Rackspace GCE]
   PROVIDERS = SUPPORTED_PROVIDERS.reject { |p| !SETTINGS[p.downcase.to_sym] }
@@ -13,12 +14,12 @@ class ComputeResource < ActiveRecord::Base
   STI_PREFIX= "Foreman::Model"
 
   before_destroy EnsureNotUsedBy.new(:hosts)
-  include Authorization
   has_and_belongs_to_many :users, :join_table => "user_compute_resources"
   validates :name, :uniqueness => true, :format => { :with => /\A(\S+)\Z/, :message => N_("can't be blank or contain white spaces.") }
   validates :provider, :presence => true, :inclusion => { :in => PROVIDERS }
   validates :url, :presence => true
   scoped_search :on => :name, :complete_value => :true
+  scoped_search :on => :id, :complete_value => :true
   before_save :sanitize_url
   has_many_hosts
   has_many :images, :dependent => :destroy
@@ -36,19 +37,6 @@ class ComputeResource < ActiveRecord::Base
     end
   }
 
-  scope :my_compute_resources, lambda {
-    user = User.current
-    if user.admin?
-      conditions = { }
-    else
-      conditions = sanitize_sql_for_conditions([" (compute_resources.id in (?))", user.compute_resource_ids])
-      conditions.sub!(/\s*\(\)\s*/, "")
-      conditions.sub!(/^(?:\(\))?\s?(?:and|or)\s*/, "")
-      conditions.sub!(/\(\s*(?:or|and)\s*\(/, "((")
-    end
-    where(conditions).reorder('type, name')
-  }
-
   # allows to create a specific compute class based on the provider.
   def self.new_provider args
     raise ::Foreman::Exception.new(N_("must provide a provider")) unless provider = args[:provider]
@@ -243,22 +231,6 @@ class ComputeResource < ActiveRecord::Base
 
   private
 
-  def enforce_permissions operation
-    # We get called again with the operation being set to create
-    return true if operation == "edit" and new_record?
-    current = User.current
-    if current.allowed_to?("#{operation}_compute_resources".to_sym)
-      # If you can create compute resources then you can create them anywhere
-      return true if operation == "create"
-      # edit or delete
-      if current.allowed_to?("#{operation}_compute_resources".to_sym)
-        return true if ComputeResource.my_compute_resources.include? self
-      end
-    end
-    errors.add :base, _("You do not have permission to %s this compute resource") % operation
-    false
-  end
-
   def set_attributes_hash
     self.attrs ||= {}
   end
diff --git a/app/models/compute_resources/foreman/model/ec2.rb b/app/models/compute_resources/foreman/model/ec2.rb
index f38736f4d..52f4ee78e 100644
--- a/app/models/compute_resources/foreman/model/ec2.rb
+++ b/app/models/compute_resources/foreman/model/ec2.rb
@@ -91,7 +91,7 @@ module Foreman::Model
     end
 
     def associated_host(vm)
-      Host.my_hosts.where(:ip => [vm.public_ip_address, vm.private_ip_address]).first
+      Host.authorized(:view_hosts, Host).where(:ip => [vm.public_ip_address, vm.private_ip_address]).first
     end
 
     private
diff --git a/app/models/compute_resources/foreman/model/libvirt.rb b/app/models/compute_resources/foreman/model/libvirt.rb
index 79c8a9c76..a73f5bd01 100644
--- a/app/models/compute_resources/foreman/model/libvirt.rb
+++ b/app/models/compute_resources/foreman/model/libvirt.rb
@@ -127,7 +127,7 @@ module Foreman::Model
     end
 
     def associated_host(vm)
-      Host.my_hosts.where(:mac => vm.mac).first
+      Host.authorized(:view_hosts, Host).where(:mac => vm.mac).first
     end
 
     protected
diff --git a/app/models/compute_resources/foreman/model/openstack.rb b/app/models/compute_resources/foreman/model/openstack.rb
index 7eba98a59..83a973741 100644
--- a/app/models/compute_resources/foreman/model/openstack.rb
+++ b/app/models/compute_resources/foreman/model/openstack.rb
@@ -83,7 +83,7 @@ module Foreman::Model
     end
 
     def associated_host(vm)
-      Host.my_hosts.where(:ip => [vm.floating_ip_address, vm.private_ip_address]).first
+      Host.authorized(:view_hosts, Host).where(:ip => [vm.floating_ip_address, vm.private_ip_address]).first
     end
 
     def flavor_name(flavor_ref)
diff --git a/app/models/compute_resources/foreman/model/ovirt.rb b/app/models/compute_resources/foreman/model/ovirt.rb
index d83944160..b724d37cb 100644
--- a/app/models/compute_resources/foreman/model/ovirt.rb
+++ b/app/models/compute_resources/foreman/model/ovirt.rb
@@ -207,7 +207,7 @@ module Foreman::Model
     end
 
     def associated_host(vm)
-      Host.my_hosts.where(:mac => vm.mac).first
+      Host.authorized(:view_hosts, Host).where(:mac => vm.mac).first
     end
 
     def provider_friendly_name
diff --git a/app/models/compute_resources/foreman/model/rackspace.rb b/app/models/compute_resources/foreman/model/rackspace.rb
index 98f932931..199b8f21c 100644
--- a/app/models/compute_resources/foreman/model/rackspace.rb
+++ b/app/models/compute_resources/foreman/model/rackspace.rb
@@ -88,7 +88,7 @@ module Foreman::Model
     end
 
     def associated_host(vm)
-      Host.my_hosts.where(:ip => [vm.public_ip_address, vm.private_ip_address]).first
+      Host.authorized(:view_hosts, Host).where(:ip => [vm.public_ip_address, vm.private_ip_address]).first
     end
 
     private
diff --git a/app/models/compute_resources/foreman/model/vmware.rb b/app/models/compute_resources/foreman/model/vmware.rb
index bd4ec9f81..476ebc49e 100644
--- a/app/models/compute_resources/foreman/model/vmware.rb
+++ b/app/models/compute_resources/foreman/model/vmware.rb
@@ -165,7 +165,7 @@ module Foreman::Model
     end
 
     def associated_host(vm)
-      Host.my_hosts.where(:mac => vm.mac).first
+      Host.authorized(:view_hosts, Host).where(:mac => vm.mac).first
     end
 
     def provider_friendly_name
diff --git a/app/models/concerns/audit_extensions.rb b/app/models/concerns/audit_extensions.rb
index b495fcbda..31f7593a5 100644
--- a/app/models/concerns/audit_extensions.rb
+++ b/app/models/concerns/audit_extensions.rb
@@ -29,6 +29,8 @@ module AuditExtensions
 
     before_save :ensure_username, :ensure_audtiable_and_associated_name
     after_validation :fix_auditable_type
+
+    include Authorizable
   end
 
   private
diff --git a/app/models/concerns/authorizable.rb b/app/models/concerns/authorizable.rb
new file mode 100644
index 000000000..a70913e80
--- /dev/null
+++ b/app/models/concerns/authorizable.rb
@@ -0,0 +1,23 @@
+module Authorizable
+  extend ActiveSupport::Concern
+
+  included do
+    # permission can be nil (therefore we use Proc instead of lambda)
+    # same applies for resource class
+    #
+    # e.g.
+    #   FactValue.authorized
+    #   FactValue.authorized(:view_facts)
+    #   Host::Base.authorized(:view_hosts, Host)
+    #
+    scope :authorized, Proc.new { |permission, resource|
+      if User.current.nil?
+        self.where('1=0')
+      elsif User.current.admin?
+        self.scoped
+      else
+        Authorizer.new(User.current).find_collection(resource || self, :permission => permission)
+      end
+    }
+  end
+end
diff --git a/app/models/concerns/authorization.rb b/app/models/concerns/authorization.rb
deleted file mode 100644
index 5aefc105b..000000000
--- a/app/models/concerns/authorization.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-module Authorization
-  extend ActiveSupport::Concern
-
-  included do
-    before_save    :enforce_edit_permissions
-    before_destroy :enforce_destroy_permissions
-    before_create  :enforce_create_permissions
-  end
-
-  # We must enforce the security model
-  def enforce_edit_permissions
-    enforce_permissions("edit") if enforce?
-  end
-
-  def enforce_destroy_permissions
-    enforce_permissions("destroy") if enforce?
-  end
-
-  def enforce_create_permissions
-    enforce_permissions("create") if enforce?
-  end
-
-  def enforce_permissions operation
-    # We get called again with the operation being set to create
-    return true if operation == "edit" and new_record?
-
-    if self.class < Operatingsystem
-      klass = 'operatingsystem'
-      klasses   = 'operatingsystems'
-    else
-      klass   = self.class.name.downcase
-      klasses   = self.class.name.tableize
-    end
-
-    #TODO: Extract all fo the specific implementations into each individual class
-    klasses.gsub!(/auth_source.*/, "authenticators")
-    klasses.gsub!(/common_parameters.*/, "global_variables")
-    klasses.gsub!(/lookup_key.*/, "external_variables")
-    klasses.gsub!(/lookup_value.*/, "external_variables")
-    # editing own user is a special case
-    if User.current
-      action = if klass == 'user'
-                 { :controller => 'users', :action => operation, :id => self.id }
-               else
-                 "#{operation}_#{klasses}".to_sym
-               end
-      return true if User.current.allowed_to?(action)
-    end
-
-    errors.add :base, _("You do not have permission to %{operation} this %{klass}") % { :operation => operation, :klass => klass }
-    @permission_failed = operation
-    false
-  end
-
-  # @return false or name of failed operation
-  def permission_failed?
-    return false unless @permission_failed
-    @permission_failed
-  end
-
-  private
-  def enforce?
-    return false if (User.current and User.current.admin?)
-    return true  if defined?(Rake) and Rails.env == "test"
-    return false if defined?(Rake)
-    true
-  end
-end
diff --git a/app/models/concerns/hostext/search.rb b/app/models/concerns/hostext/search.rb
index 1dfb6f7bf..f1548405c 100644
--- a/app/models/concerns/hostext/search.rb
+++ b/app/models/concerns/hostext/search.rb
@@ -11,6 +11,8 @@ module Hostext
       scoped_search :on => :ip,            :complete_value => true
       scoped_search :on => :comment,       :complete_value => true
       scoped_search :on => :enabled,       :complete_value => {:true => true, :false => false}, :rename => :'status.enabled'
+      scoped_search :on => :owner_type,    :complete_value => true, :only_explicit => true
+      scoped_search :on => :owner_id,      :complete_value => true, :only_explicit => true
       scoped_search :on => :puppet_status, :offset => 0, :word_size => Report::BIT_NUM*4, :complete_value => {:true => true, :false => false}, :rename => :'status.interesting'
       scoped_search :on => :puppet_status, :offset => Report::METRIC.index("applied"),         :word_size => Report::BIT_NUM, :rename => :'status.applied'
       scoped_search :on => :puppet_status, :offset => Report::METRIC.index("restarted"),       :word_size => Report::BIT_NUM, :rename => :'status.restarted'
@@ -19,12 +21,14 @@ module Hostext
       scoped_search :on => :puppet_status, :offset => Report::METRIC.index("skipped"),         :word_size => Report::BIT_NUM, :rename => :'status.skipped'
       scoped_search :on => :puppet_status, :offset => Report::METRIC.index("pending"),         :word_size => Report::BIT_NUM, :rename => :'status.pending'
 
-      scoped_search :in => :model,       :on => :name,    :complete_value => true, :rename => :model
-      scoped_search :in => :hostgroup,   :on => :name,    :complete_value => true, :rename => :hostgroup
-      scoped_search :in => :hostgroup,   :on => :title,   :complete_value => true, :rename => :hostgroup_fullname
-      scoped_search :in => :hostgroup,   :on => :title,   :complete_value => true, :rename => :hostgroup_title
-      scoped_search :in => :domain,      :on => :name,    :complete_value => true, :rename => :domain
-      scoped_search :in => :environment, :on => :name,    :complete_value => true, :rename => :environment
+      scoped_search :in => :model,       :on => :name,    :complete_value => true,  :rename => :model
+      scoped_search :in => :hostgroup,   :on => :name,    :complete_value => true,  :rename => :hostgroup
+      scoped_search :in => :hostgroup,   :on => :title,   :complete_value => true,  :rename => :hostgroup_fullname
+      scoped_search :in => :hostgroup,   :on => :title,   :complete_value => true,  :rename => :hostgroup_title
+      scoped_search :in => :hostgroup,   :on => :id,      :complete_value => false, :rename => :hostgroup_id, :only_explicit => true
+      scoped_search :in => :domain,      :on => :name,    :complete_value => true,  :rename => :domain
+      scoped_search :in => :domain,      :on => :id,      :complete_value => true,  :rename => :domain_id
+      scoped_search :in => :environment, :on => :name,    :complete_value => true,  :rename => :environment
       scoped_search :in => :architecture, :on => :name,    :complete_value => true, :rename => :architecture
       scoped_search :in => :puppet_proxy, :on => :name,    :complete_value => true, :rename => :puppetmaster
       scoped_search :in => :puppet_ca_proxy, :on => :name,    :complete_value => true, :rename => :puppet_ca
@@ -73,7 +77,7 @@ module Hostext
 
       def search_by_puppetclass(key, operator, value)
         conditions  = sanitize_sql_for_conditions(["puppetclasses.name #{operator} ?", value_to_sql(operator, value)])
-        hosts       = Host.my_hosts.all(:conditions => conditions, :joins => :puppetclasses, :select => 'DISTINCT hosts.id').map(&:id)
+        hosts       = Host.authorized(:view_hosts, Host).all(:conditions => conditions, :joins => :puppetclasses, :select => 'DISTINCT hosts.id').map(&:id)
         host_groups = Hostgroup.all(:conditions => conditions, :joins => :puppetclasses, :select => 'DISTINCT hostgroups.id').map(&:id)
 
         opts = ''
diff --git a/app/models/concerns/taxonomix.rb b/app/models/concerns/taxonomix.rb
index cd4633074..1fee1c693 100644
--- a/app/models/concerns/taxonomix.rb
+++ b/app/models/concerns/taxonomix.rb
@@ -11,14 +11,56 @@ module Taxonomix
     after_initialize :set_current_taxonomy
 
     scoped_search :in => :locations, :on => :name, :rename => :location, :complete_value => true
+    scoped_search :in => :locations, :on => :id, :rename => :location_id, :complete_value => true
     scoped_search :in => :organizations, :on => :name, :rename => :organization, :complete_value => true
+    scoped_search :in => :organizations, :on => :id, :rename => :organization_id, :complete_value => true
   end
 
   module ClassMethods
 
+    attr_accessor :which_ancestry_method, :which_location, :which_organization
+
     # default inner_method includes children (subtree_ids)
     def with_taxonomy_scope(loc = Location.current, org = Organization.current, inner_method = :subtree_ids)
+      self.which_ancestry_method = inner_method
+      self.which_location        = loc
+      self.which_organization    = org
       scope =  block_given? ? yield : where('1=1')
+      scope = scope.where(:id => taxable_ids) if taxable_ids
+      scope.readonly(false)
+    end
+
+    # default inner_method includes parents (path_ids)
+    def with_taxonomy_scope_override(loc = nil, org = nil, inner_method = :path_ids)
+      # need to .unscoped or default_scope {with_taxonomy_scope} overrides inner_method
+      unscoped.with_taxonomy_scope(loc, org, inner_method)
+    end
+
+    def used_taxonomy_ids
+      used_location_ids + used_organization_ids
+    end
+
+    def used_location_ids
+      enforce_default
+      return [] unless which_location && SETTINGS[:locations_enabled]
+      (which_location.send(which_ancestry_method) + which_location.ancestor_ids).uniq
+    end
+
+    def used_organization_ids
+      enforce_default
+      return [] unless which_organization && SETTINGS[:organizations_enabled]
+      (which_organization.send(which_ancestry_method) + which_organization.ancestor_ids).uniq
+    end
+
+    # default scope is not called if we just use #scoped therefore we have to enforce quering
+    # to get correct default values
+    def enforce_default
+      if which_ancestry_method.nil?
+        self.scoped.limit(0).all
+      end
+    end
+
+    def taxable_ids(loc = which_location, org = which_organization, inner_method = which_ancestry_method)
       if SETTINGS[:locations_enabled] && loc
         inner_ids_loc = if Location.ignore?(self.to_s)
                           self.pluck(:id)
@@ -38,16 +80,10 @@ module Taxonomix
       inner_ids ||= inner_ids_org if inner_ids_org
       # In the case of users we want the taxonomy scope to get both the users of the taxonomy and admins.
       inner_ids << admin_ids if inner_ids && self == User
-      scope = scope.where(:id => inner_ids) if inner_ids
-      scope.readonly(false)
-    end
-
-    # default inner_method includes parents (path_ids)
-    def with_taxonomy_scope_override(loc = nil, org = nil, inner_method = :path_ids)
-      with_taxonomy_scope(loc, org, inner_method)
+      inner_ids
     end
 
-    def inner_select(taxonomy, inner_method)
+    def inner_select(taxonomy, inner_method = which_ancestry_method)
       # always include ancestor_ids in inner select
       taxonomy_ids = (taxonomy.send(inner_method) + taxonomy.ancestor_ids).uniq
       TaxableTaxonomy.where(:taxable_type => self.name, :taxonomy_id => taxonomy_ids).pluck(:taxable_id).compact.uniq
diff --git a/app/models/config_template.rb b/app/models/config_template.rb
index 53c9f0b5d..70a0d4be3 100644
--- a/app/models/config_template.rb
+++ b/app/models/config_template.rb
@@ -1,5 +1,5 @@
 class ConfigTemplate < ActiveRecord::Base
-  include Authorization
+  include Authorizable
   include Taxonomix
   audited :allow_mass_assignment => true
   self.auditing_enabled = !(File.basename($0) == "rake" && ARGV.include?("db:migrate"))
@@ -78,16 +78,6 @@ class ConfigTemplate < ActiveRecord::Base
     template.is_a?(ConfigTemplate) ? template : nil
   end
 
-  def enforce_permissions operation
-    # We get called again with the operation being set to create
-    return true if operation == "edit" and new_record?
-
-    return true if User.current and User.current.allowed_to?("#{operation}_templates".to_sym)
-
-    errors.add :base, (_("You do not have permission to %s this template") % operation)
-    false
-  end
-
   def self.build_pxe_default(renderer)
     if (proxies = SmartProxy.tftp_proxies).empty?
       error_msg = _("No TFTP proxies defined, can't continue")
diff --git a/app/models/domain.rb b/app/models/domain.rb
index 31034538a..8059e68fd 100644
--- a/app/models/domain.rb
+++ b/app/models/domain.rb
@@ -1,7 +1,7 @@
 require "resolv"
 # This models a DNS domain and so represents a site.
 class Domain < ActiveRecord::Base
-  include Authorization
+  include Authorizable
   include Taxonomix
   audited :allow_mass_assignment => true
 
@@ -40,25 +40,6 @@ class Domain < ActiveRecord::Base
     "#{id}-#{name.parameterize}"
   end
 
-  def enforce_permissions operation
-    # We get called again with the operation being set to create
-    return true if operation == "edit" and new_record?
-
-    current = User.current
-
-    if current.allowed_to?("#{operation}_domains".to_sym)
-      # If you can create domains then you can create them anywhere
-      return true if operation == "create"
-      # However if you are editing or destroying and you have a domain list then you are constrained
-      if current.domains.empty? or current.domains.map(&:id).include? self.id
-        return true
-      end
-    end
-
-    errors.add(:base, _("You do not have permission to %s this domain") % operation)
-    false
-  end
-
   # return the primary name server for our domain based on DNS lookup
   # it first searches for SOA record, if it failed it will search for NS records
   def nameservers
diff --git a/app/models/environment.rb b/app/models/environment.rb
index ec41bf330..9665dc382 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -1,6 +1,6 @@
 class Environment < ActiveRecord::Base
   include Taxonomix
-  include Authorization
+  include Authorizable
 
   before_destroy EnsureNotUsedBy.new(:hosts)
 
diff --git a/app/models/fact_value.rb b/app/models/fact_value.rb
index 410003e21..667c0bb3a 100644
--- a/app/models/fact_value.rb
+++ b/app/models/fact_value.rb
@@ -1,4 +1,5 @@
 class FactValue < ActiveRecord::Base
+  include Authorizable
 
   belongs_to_host
   belongs_to :fact_name
@@ -22,9 +23,8 @@ class FactValue < ActiveRecord::Base
             }
   scope :my_facts, lambda {
     unless User.current.admin? and Organization.current.nil? and Location.current.nil?
-      #TODO: Remove pluck after upgrade to newer rails as it would be
-      #done via INNER select automatically
-      where(:fact_values => {:host_id => Host.my_hosts.pluck(:id)})
+      host_ids = Host.authorized(:view_hosts, Host).select('hosts.id').all
+      where(:fact_values => {:host_id => host_ids})
     end
   }
 
diff --git a/app/models/filter.rb b/app/models/filter.rb
new file mode 100644
index 000000000..143b27674
--- /dev/null
+++ b/app/models/filter.rb
@@ -0,0 +1,138 @@
+class Filter < ActiveRecord::Base
+  include Taxonomix
+  include Authorizable
+
+  # tune up taxonomix for filters, we don't want to set current taxonomy
+  def self.add_current_organization?
+    false
+  end
+
+  def self.add_current_location?
+    false
+  end
+
+  attr_accessible :search, :resource_type, :permission_ids, :role_id, :unlimited,
+                  :organization_ids, :location_ids
+  attr_writer :resource_type
+  attr_accessor :unlimited
+
+  belongs_to :role
+  has_many :filterings, :dependent => :destroy
+  has_many :permissions, :through => :filterings
+
+  default_scope lambda { order(['role_id', "#{self.table_name}.id"]) }
+  scope :unlimited, lambda { where(:search => nil, :taxonomy_search => nil) }
+  scope :limited, lambda { where("search IS NOT NULL OR taxonomy_search IS NOT NULL") }
+
+  scoped_search :on => :search, :complete_value => true
+  scoped_search :on => :limited, :complete_value => { :true => true, :false => false }, :ext_method => :search_by_limited
+  scoped_search :on => :unlimited, :complete_value => { :true => true, :false => false }, :ext_method => :search_by_unlimited
+  scoped_search :in => :role, :on => :id, :rename => :role_id
+  scoped_search :in => :role, :on => :name, :rename => :role
+  scoped_search :in => :permissions, :on => :resource_type, :rename => :resource
+
+  before_validation :build_taxonomy_search, :nilify_empty_searches
+
+  validates :search, :presence => true, :unless => Proc.new { |o| o.search.nil? }
+  validates :role, :presence => true
+  validate :same_resource_type_permissions, :not_empty_permissions
+
+  def self.search_by_unlimited(key, operator, value)
+    search_by_limited(key, operator, value == 'true' ? 'false' : 'true')
+  end
+
+  def self.search_by_limited(key, operator, value)
+    value      = value == 'true'
+    value      = !value if operator == '<>'
+    conditions = value ? limited.where_values.join(' AND ') : unlimited.where_values.map(&:to_sql).join(' AND ')
+    { :conditions => conditions }
+  end
+
+  def self.get_resource_class(resource_type)
+    resource_type.constantize
+  rescue NameError => e
+    Rails.logger.debug "unknown klass #{resource_type}, ignoring"
+    return nil
+  end
+
+  def unlimited?
+    search.nil? && taxonomy_search.nil?
+  end
+
+  def limited?
+    !unlimited?
+  end
+
+  def to_s
+    _('filter for %s role') % self.role.try(:name) || 'unknown'
+  end
+
+  def resource_type
+    type = @resource_type || permissions.first.try(:resource_type)
+    type.blank? ? nil : type
+  end
+
+  def resource_class
+    @resource_class ||= self.class.get_resource_class(resource_type)
+  end
+
+  # We detect granularity by inclusion of Authorizable module and scoped_search definition
+  # we can define exceptions for resources with more complex hierarchy (e.g. Host is proxy module)
+  def granular?
+    @granular ||= begin
+      return false if resource_class.nil?
+      return true if resource_type == 'Host'
+      resource_class.included_modules.include?(Authorizable) && resource_class.respond_to?(:search_for)
+    end
+  end
+
+  def search_condition
+    searches = [self.search, self.taxonomy_search].compact
+    searches = searches.map { |s| parenthesize(s) } if searches.size > 1
+    searches.join(' and ')
+  end
+
+  private
+
+  def build_taxonomy_search
+    orgs = build_taxonomy_search_string('organization')
+    locs = build_taxonomy_search_string('location')
+
+    if self.organizations.empty? && self.locations.empty?
+      self.taxonomy_search = nil
+    else
+      taxonomies = [orgs, locs].reject {|t| t.blank? }
+      self.taxonomy_search = taxonomies.join(' and ')
+    end
+  end
+
+  def build_taxonomy_search_string(name)
+    relation = name.pluralize
+    taxes = self.send(relation).empty? ? [] : self.send(relation).map { |t| "#{name}_id = #{t.id}" }
+    taxes = taxes.join(' or ')
+    parenthesize(taxes)
+  end
+
+  def nilify_empty_searches
+    self.search = nil if self.search.empty? || self.unlimited == '1'
+    self.taxonomy_search = nil if self.taxonomy_search.empty?
+  end
+
+  def parenthesize(string)
+    if string.blank? || (string.start_with?('(') && string.end_with?(')'))
+      string
+    else
+      "(#{string})"
+    end
+  end
+
+  # if we have 0 types, empty validation will set error, we can't have more than one type
+  def same_resource_type_permissions
+    errors.add(:permissions, _('Permissions must be of same resource type')) if self.permissions.map(&:resource_type).uniq.size > 1
+  end
+
+  def not_empty_permissions
+    errors.add(:permissions, _('You must select at least one permission')) if self.permissions.blank? && self.filterings.blank?
+  end
+
+end
diff --git a/app/models/filtering.rb b/app/models/filtering.rb
new file mode 100644
index 000000000..59df77f2a
--- /dev/null
+++ b/app/models/filtering.rb
@@ -0,0 +1,6 @@
+class Filtering < ActiveRecord::Base
+  attr_accessible :filter_id, :permission_id
+
+  belongs_to :filter
+  belongs_to :permission
+end
diff --git a/app/models/host/base.rb b/app/models/host/base.rb
index 749c78a07..3cbf81919 100644
--- a/app/models/host/base.rb
+++ b/app/models/host/base.rb
@@ -3,6 +3,7 @@ require 'facts_parser'
 module Host
   class Base < ActiveRecord::Base
     include Foreman::STI
+    include Authorizable
     self.table_name = :hosts
     OWNER_TYPES = %w(User Usergroup)
 
@@ -20,42 +21,6 @@ module Host
                            :allow_blank => true,
                            :message     => (_("Owner type needs to be one of the following: %s") % OWNER_TYPES.join(', '))
 
-    scope :my_hosts, lambda {
-      user                 = User.current
-      return if user.admin? # Admin can see all hosts
-
-      owner_conditions             = sanitize_sql_for_conditions(["((hosts.owner_id in (?) AND hosts.owner_type = 'Usergroup') OR (hosts.owner_id = ? AND hosts.owner_type = 'User'))", user.my_usergroups.map(&:id), user.id])
-      domain_conditions            = sanitize_sql_for_conditions([" (hosts.domain_id in (?))",dms = (user.domain_ids)])
-      compute_resource_conditions  = sanitize_sql_for_conditions([" (hosts.compute_resource_id in (?))",(crs = user.compute_resource_ids)])
-      hostgroup_conditions         = sanitize_sql_for_conditions([" (hosts.hostgroup_id in (?))",(hgs = user.hostgroup_ids)])
-      organization_conditions      = sanitize_sql_for_conditions([" (hosts.organization_id in (?))",orgs = (user.organization_ids)])
-      location_conditions          = sanitize_sql_for_conditions([" (hosts.location_id in (?))",locs = (user.location_ids)])
-
-      fact_conditions = ""
-      for user_fact in (ufs = user.user_facts)
-        fact_conditions += sanitize_sql_for_conditions ["(hosts.id = fact_values.host_id and fact_values.fact_name_id = ? and fact_values.value #{user_fact.operator} ?)", user_fact.fact_name_id, user_fact.criteria]
-        fact_conditions = user_fact.andor == "and" ? "(#{fact_conditions}) and " : "#{fact_conditions} or  "
-      end
-      if (match = fact_conditions.match(/\A(.*).....\Z/))
-        fact_conditions = "(#{match[1]})"
-      end
-
-      conditions = ""
-      if user.filtering?
-        conditions  = "#{owner_conditions}"                                                                                                                                 if     user.filter_on_owner
-        (conditions = (user.domains_andor           == "and") ? "(#{conditions}) and #{domain_conditions} "           : "#{conditions} or #{domain_conditions} ")           unless dms.empty?
-        (conditions = (user.compute_resources_andor == "and") ? "(#{conditions}) and #{compute_resource_conditions} " : "#{conditions} or #{compute_resource_conditions} ") unless crs.empty?
-        (conditions = (user.hostgroups_andor        == "and") ? "(#{conditions}) and #{hostgroup_conditions} "        : "#{conditions} or #{hostgroup_conditions} ")        unless hgs.empty?
-        (conditions = (user.facts_andor             == "and") ? "(#{conditions}) and #{fact_conditions} "             : "#{conditions} or #{fact_conditions} ")             unless ufs.empty?
-        (conditions = (user.organizations_andor     == "and") ? "(#{conditions}) and #{organization_conditions} "     : "#{conditions} or #{organization_conditions} ")     unless orgs.empty?
-        (conditions = (user.locations_andor         == "and") ? "(#{conditions}) and #{location_conditions} "         : "#{conditions} or #{location_conditions} ")         unless locs.empty?
-        conditions.sub!(/\s*\(\)\s*/, "")
-        conditions.sub!(/\A(?:\(\))?\s?(?:and|or)\s*/, "")
-        conditions.sub!(/\(\s*(?:or|and)\s*\(/, "((")
-      end
-
-      joins(ufs.empty? ? nil : :fact_values).where(conditions)
-    }
     def self.attributes_protected_by_default
       super - [ inheritance_column ]
     end
diff --git a/app/models/host/managed.rb b/app/models/host/managed.rb
index 8b0a6e243..d6d454352 100644
--- a/app/models/host/managed.rb
+++ b/app/models/host/managed.rb
@@ -1,5 +1,4 @@
 class Host::Managed < Host::Base
-  include Authorization
   include ReportCommon
   include Hostext::Search
 
@@ -94,8 +93,6 @@ class Host::Managed < Host::Base
 
   scope :alerts_enabled, lambda { where(:enabled => true) }
 
-  scope :completer_scope, lambda { |opts| my_hosts }
-
   scope :run_distribution, lambda { |fromtime,totime|
     if fromtime.nil? or totime.nil?
       raise ::Foreman.Exception.new(N_("invalid time range"))
@@ -457,11 +454,13 @@ class Host::Managed < Host::Base
   # counts each association of a given host
   # e.g. how many hosts belongs to each os
   # returns sorted hash
-  def self.count_distribution assocication
+  def self.count_distribution association
     output = []
-    group(assocication).count.each do |k,v|
+    data = group("#{Host.table_name}.#{association}_id").reorder('').count
+    associations = association.to_s.camelize.constantize.where(:id => data.keys).all
+    data.each do |k,v|
       begin
-        output << {:label => k.to_label, :data => v }  unless v == 0
+        output << {:label => associations.detect {|a| a.id == k }.to_label, :data => v }  unless v == 0
       rescue
         logger.info "skipped #{k} as it has has no label"
       end
@@ -474,7 +473,7 @@ class Host::Managed < Host::Base
   # e.g. how many hosts belongs to each os
   # returns sorted hash
   def self.count_habtm association
-    counter = Host::Managed.joins(association.tableize.to_sym).group("#{association.tableize.to_sym}.id").count
+    counter = Host::Managed.joins(association.tableize.to_sym).group("#{association.tableize.to_sym}.id").reorder('').count
     #Puppetclass.find(counter.keys.compact)...
     association.camelize.constantize.find(counter.keys.compact).map {|i| {:label=>i.to_label, :data =>counter[i.id]}}
   end
@@ -489,28 +488,6 @@ class Host::Managed < Host::Base
     managed? and SETTINGS[:unattended] and capabilities.include?(:build) ? build == false : false
   end
 
-  def enforce_permissions operation
-    if operation == "edit" and new_record?
-      return true # We get called again with the operation being set to create
-    end
-    current = User.current
-    if (operation == "edit") or operation == "destroy"
-      if current.allowed_to?("#{operation}_hosts".to_sym)
-        return true if Host::Base.my_hosts.include? self
-      end
-    else # create
-      if current.allowed_to?(:create_hosts)
-        # We are unconstrained
-        return true if current.domains.empty? and current.hostgroups.empty?
-        # We are constrained and the constraint is matched
-        return true if (!current.domains.empty?    and current.domains.include?(domain)) or
-        (!current.hostgroups.empty? and current.hostgroups.include?(hostgroup))
-      end
-    end
-    errors.add(:base, _("You do not have permission to %s this host") % operation)
-    false
-  end
-
   def jumpstart?
     operatingsystem.family == "Solaris" and architecture.name =~/Sparc/i rescue false
   end
diff --git a/app/models/host_class.rb b/app/models/host_class.rb
index 6f9b881b6..a5bac8af3 100644
--- a/app/models/host_class.rb
+++ b/app/models/host_class.rb
@@ -1,5 +1,5 @@
 class HostClass < ActiveRecord::Base
-  include Authorization
+  include Authorizable
   audited :associated_with => :host, :allow_mass_assignment => true
   belongs_to_host :foreign_key => :host_id
   belongs_to :puppetclass
@@ -11,18 +11,4 @@ class HostClass < ActiveRecord::Base
     "#{host} - #{puppetclass}"
   end
 
-  private
-
-  def enforce_permissions operation
-    if operation == "edit" and new_record?
-      return true # We get called again with the operation being set to create
-    end
-    if User.current.allowed_to?(:edit_classes) && Host.my_hosts.pluck(:id).include?(self.host_id)
-      return true
-    else
-      errors.add(:base, _("You do not have permission to edit Puppet classes on this host"))
-      return false
-    end
-  end
-
 end
diff --git a/app/models/hostgroup.rb b/app/models/hostgroup.rb
index 8f492b921..8b72459be 100644
--- a/app/models/hostgroup.rb
+++ b/app/models/hostgroup.rb
@@ -1,5 +1,6 @@
 class Hostgroup < ActiveRecord::Base
-  include Authorization
+  has_ancestry :orphan_strategy => :restrict
+  include Authorizable
   include Taxonomix
   include HostCommon
   include NestedAncestryCommon
@@ -36,6 +37,7 @@ class Hostgroup < ActiveRecord::Base
   scoped_search :in => :hosts, :on => :name, :complete_value => :true, :rename => "host"
   scoped_search :in => :puppetclasses, :on => :name, :complete_value => true, :rename => :class, :operators => ['= ', '~ ']
   scoped_search :in => :environment, :on => :name, :complete_value => :true, :rename => :environment
+  scoped_search :on => :id, :complete_value => :true
   if SETTINGS[:unattended]
     scoped_search :in => :architecture, :on => :name, :complete_value => :true, :rename => :architecture
     scoped_search :in => :operatingsystem, :on => :name, :complete_value => true, :rename => :os
diff --git a/app/models/hostgroup_class.rb b/app/models/hostgroup_class.rb
index 1eb9d9af6..7c37cef56 100644
--- a/app/models/hostgroup_class.rb
+++ b/app/models/hostgroup_class.rb
@@ -1,5 +1,5 @@
 class HostgroupClass < ActiveRecord::Base
-  include Authorization
+  include Authorizable
   audited :associated_with => :hostgroup, :allow_mass_assignment => true
   belongs_to :hostgroup
   belongs_to :puppetclass
@@ -13,18 +13,4 @@ class HostgroupClass < ActiveRecord::Base
     "#{hostgroup} - #{puppetclass}"
   end
 
-  private
-
-  def enforce_permissions operation
-    if operation == "edit" and new_record?
-      return true # We get called again with the operation being set to create
-    end
-    if User.current.allowed_to?(:edit_classes) && Hostgroup.my_groups.pluck(:id).include?(self.hostgroup_id)
-      return true
-    else
-      errors.add(:base, _("You do not have permission to edit Puppet classes on this host group"))
-      return false
-    end
-  end
-
 end
diff --git a/app/models/image.rb b/app/models/image.rb
index cdee841c5..ee35ccfc3 100644
--- a/app/models/image.rb
+++ b/app/models/image.rb
@@ -1,4 +1,5 @@
 class Image < ActiveRecord::Base
+  include Authorizable
 
   audited :allow_mass_assignment => true
 
diff --git a/app/models/lookup_key.rb b/app/models/lookup_key.rb
index 843719f31..b3acb5a7f 100644
--- a/app/models/lookup_key.rb
+++ b/app/models/lookup_key.rb
@@ -1,5 +1,5 @@
 class LookupKey < ActiveRecord::Base
-  include Authorization
+  include Authorizable
 
   KEY_TYPES = %w( string boolean integer real array hash yaml json )
   VALIDATOR_TYPES = %w( regexp list )
diff --git a/app/models/lookup_value.rb b/app/models/lookup_value.rb
index 6c9028aa6..b39151d60 100644
--- a/app/models/lookup_value.rb
+++ b/app/models/lookup_value.rb
@@ -1,5 +1,5 @@
 class LookupValue < ActiveRecord::Base
-  include Authorization
+  include Authorizable
   belongs_to :lookup_key, :counter_cache => true
   validates :match, :presence => true, :uniqueness => {:scope => :lookup_key_id}
   delegate :key, :to => :lookup_key
@@ -69,24 +69,4 @@ class LookupValue < ActiveRecord::Base
     errors.add(:match, _("%{match} does not match an existing host group") % { :match => match }) and return false
   end
 
-  private
-
-  def enforce_permissions operation
-    # We get called again with the operation being set to create
-    return true if operation == "edit" and new_record?
-    allowed = case match
-      when /^fqdn=(.*)/
-        # check if current fqdn is in our allowed list
-        Host.my_hosts.where(:name => $1).exists? || self.host_or_hostgroup.try(:new_record?)
-      when /^hostgroup=(.*)/
-        # check if current hostgroup is in our allowed list
-        Hostgroup.my_groups.where(:title => $1).exists? || self.host_or_hostgroup.try(:new_record?)
-      else
-        false
-    end
-    return true if allowed
-    errors.add :base, _("You do not have permission to %s this Smart Variable") % operation
-    return false
-  end
-
 end
diff --git a/app/models/medium.rb b/app/models/medium.rb
index ddc552e20..5b96e86ee 100644
--- a/app/models/medium.rb
+++ b/app/models/medium.rb
@@ -1,5 +1,5 @@
 class Medium < ActiveRecord::Base
-  include Authorization
+  include Authorizable
   include Taxonomix
   include ValidateOsFamily
   audited :allow_mass_assignment => true
diff --git a/app/models/model.rb b/app/models/model.rb
index 75280fa43..6a1f289a2 100644
--- a/app/models/model.rb
+++ b/app/models/model.rb
@@ -1,5 +1,5 @@
 class Model < ActiveRecord::Base
-  include Authorization
+  include Authorizable
 
   has_many_hosts
   has_many :trends, :as => :trendable, :class_name => "ForemanTrend"
diff --git a/app/models/operatingsystem.rb b/app/models/operatingsystem.rb
index c1fc884ac..30deccfdb 100644
--- a/app/models/operatingsystem.rb
+++ b/app/models/operatingsystem.rb
@@ -2,7 +2,7 @@ require 'ostruct'
 require 'uri'
 
 class Operatingsystem < ActiveRecord::Base
-  include Authorization
+  include Authorizable
   include ValidateOsFamily
 
   before_destroy EnsureNotUsedBy.new(:hosts, :hostgroups)
diff --git a/app/models/operatingsystems/archlinux.rb b/app/models/operatingsystems/archlinux.rb
index 5d36158ad..48d16714d 100644
--- a/app/models/operatingsystems/archlinux.rb
+++ b/app/models/operatingsystems/archlinux.rb
@@ -19,6 +19,7 @@ class Archlinux < Operatingsystem
     pxedir + "/" + PXEFILES[file]
   end
 
+
   def display_family
     "Arch Linux"
   end
diff --git a/app/models/parameter.rb b/app/models/parameter.rb
index 2c5a7f4ac..7dbe9e7ff 100644
--- a/app/models/parameter.rb
+++ b/app/models/parameter.rb
@@ -1,6 +1,6 @@
 class Parameter < ActiveRecord::Base
   belongs_to_host :foreign_key => :reference_id
-  include Authorization
+  include Authorizable
 
   validates :value, :presence => true
   validates :name, :presence => true, :format => {:with => /\A\S*\Z/, :message => N_("can't contain white spaces")}
diff --git a/app/models/parameters/domain_parameter.rb b/app/models/parameters/domain_parameter.rb
index a72fced77..45c2b9090 100644
--- a/app/models/parameters/domain_parameter.rb
+++ b/app/models/parameters/domain_parameter.rb
@@ -2,21 +2,4 @@ class DomainParameter < Parameter
   belongs_to :domain, :foreign_key => :reference_id
   audited :except => [:priority], :associated_with => :domain, :allow_mass_assignment => true
   validates :name, :uniqueness => {:scope => :reference_id}
-
-  private
-  def enforce_permissions operation
-    # We get called again with the operation being set to create
-    return true if operation == "edit" and new_record?
-
-    current = User.current
-
-    if current.allowed_to?("#{operation}_domains".to_sym)
-      if current.domains.empty? or current.domains.include? domain
-        return true
-      end
-    end
-
-    errors.add(:base, _("You do not have permission to %s this domain parameter") % operation)
-    false
-  end
 end
diff --git a/app/models/parameters/group_parameter.rb b/app/models/parameters/group_parameter.rb
index ce3a8de59..ceaedfc48 100644
--- a/app/models/parameters/group_parameter.rb
+++ b/app/models/parameters/group_parameter.rb
@@ -3,20 +3,4 @@ class GroupParameter < Parameter
   audited :except => [:priority], :associated_with => :hostgroup, :allow_mass_assignment => true
   validates :name, :uniqueness => {:scope => :reference_id}
 
-  private
-  def enforce_permissions operation
-    # We get called again with the operation being set to create
-    return true if operation == "edit" and new_record?
-
-    current = User.current
-
-    if current.allowed_to?("#{operation}_params".to_sym)
-      if current.hostgroups.empty? or current.hostgroups.include? hostgroup
-        return true
-      end
-    end
-
-    errors.add(:base, _("You do not have permission to %s this group parameter") % operation)
-    false
-  end
 end
diff --git a/app/models/parameters/host_parameter.rb b/app/models/parameters/host_parameter.rb
index 1b3fdb52c..165be6ae4 100644
--- a/app/models/parameters/host_parameter.rb
+++ b/app/models/parameters/host_parameter.rb
@@ -7,14 +7,4 @@ class HostParameter < Parameter
     "#{host.id ? host.name : "unassociated"}: #{name} = #{value}"
   end
 
-  private
-  def enforce_permissions operation
-    # We get called again with the operation being set to create
-    return true if operation == "edit" and new_record?
-
-    (auth = User.current.allowed_to?("#{operation}_params".to_sym)) and Host.my_hosts.include?(host)
-
-    errors.add(:base, _("You do not have permission to %s this domain") % operation) unless auth
-    auth
-  end
 end
diff --git a/app/models/parameters/os_parameter.rb b/app/models/parameters/os_parameter.rb
index 8ba30ff89..2e259e4d5 100644
--- a/app/models/parameters/os_parameter.rb
+++ b/app/models/parameters/os_parameter.rb
@@ -3,14 +3,4 @@ class OsParameter < Parameter
   audited :except => [:priority], :associated_with => :operatingsystem, :allow_mass_assignment => true
   validates :name, :uniqueness => {:scope => :reference_id}
 
-  private
-  def enforce_permissions operation
-    # We get called again with the operation being set to create
-    return true if operation == "edit" and new_record?
-    return true if User.current.allowed_to?("#{operation}_operatingsystems".to_sym)
-
-    errors.add(:base, _("You do not have permission to %s this Operating System parameter") % operation)
-    false
-  end
-
 end
diff --git a/app/models/permission.rb b/app/models/permission.rb
new file mode 100644
index 000000000..b5f86ca04
--- /dev/null
+++ b/app/models/permission.rb
@@ -0,0 +1,20 @@
+class Permission < ActiveRecord::Base
+  attr_accessible :name, :resource_type
+
+  validates :name, :presence => true, :uniqueness => { :scope => :resource_type }
+
+  has_many :filterings, :dependent => :destroy
+  has_many :filters, :through => :filterings
+
+  def self.resources
+    @all_resources ||= Permission.uniq.order(:resource_type).pluck(:resource_type).compact
+  end
+
+  def self.resources_with_translations
+    with_translations.sort { |a, b| a.first <=> b.first }
+  end
+
+  def self.with_translations
+    resources.map { |r| [_(Filter.get_resource_class(r).try(:humanize_class_name) || r), r] }
+  end
+end
diff --git a/app/models/ptable.rb b/app/models/ptable.rb
index ee0a55926..461786ccd 100644
--- a/app/models/ptable.rb
+++ b/app/models/ptable.rb
@@ -3,7 +3,7 @@
 # A host object may contain a reference to one of these ptables or, alternatively, it may contain a
 # modified version of one of these in textual form
 class Ptable < ActiveRecord::Base
-  include Authorization
+  include Authorizable
   include ValidateOsFamily
   audited :allow_mass_assignment => true
 
diff --git a/app/models/puppetclass.rb b/app/models/puppetclass.rb
index f953e3dc7..1fe9282ab 100644
--- a/app/models/puppetclass.rb
+++ b/app/models/puppetclass.rb
@@ -1,5 +1,5 @@
 class Puppetclass < ActiveRecord::Base
-  include Authorization
+  include Authorizable
   before_destroy EnsureNotUsedBy.new(:hosts, :hostgroups)
   has_many :environment_classes, :dependent => :destroy
   has_many :environments, :through => :environment_classes, :uniq => true
diff --git a/app/models/report.rb b/app/models/report.rb
index 8ea60fdc4..4f924f0f2 100644
--- a/app/models/report.rb
+++ b/app/models/report.rb
@@ -1,5 +1,5 @@
 class Report < ActiveRecord::Base
-  include Authorization
+  include Authorizable
   include ReportCommon
 
   belongs_to_host
@@ -33,7 +33,8 @@ class Report < ActiveRecord::Base
   # returns reports for hosts in the User's filter set
   scope :my_reports, lambda {
     unless User.current.admin? and Organization.current.nil? and Location.current.nil?
-      where(:reports => {:host_id => Host.my_hosts.select("hosts.id")})
+      host_ids = Host.authorized(:view_hosts, Host).select("hosts.id").all
+      where(:reports => {:host_id => host_ids})
     end
   }
 
@@ -171,18 +172,6 @@ class Report < ActiveRecord::Base
 
   private
 
-  def enforce_permissions operation
-    # No one can edit a report
-    return false if operation == "edit"
-
-    # Anyone can create a report
-    return true if operation == "create"
-    return true if operation == "destroy" and User.current.allowed_to?(:destroy_reports)
-
-    errors.add(:base, _("You do not have permission to %s this report") % operation)
-    false
-  end
-
   # puppet report status table column name
   def self.report_status
     "status"
diff --git a/app/models/role.rb b/app/models/role.rb
index c24a47b29..bfbc1e37b 100644
--- a/app/models/role.rb
+++ b/app/models/role.rb
@@ -31,12 +31,17 @@ class Role < ActiveRecord::Base
   before_destroy :check_deletable
 
   has_many :user_roles, :dependent => :destroy
-  has_many :users, :through => :user_roles
+  has_many :users, :through => :user_roles, :source => :owner, :source_type => 'User'
+  has_many :usergroups, :through => :user_roles, :source => :owner, :source_type => 'Usergroup'
+  has_many :cached_user_roles, :dependent => :destroy
+  has_many :cached_users, :through => :cached_user_roles, :source => :user
 
-  serialize :permissions, Array
+  has_many :filters, :dependent => :destroy
+
+  has_many :permissions, :through => :filters
   attr_protected :builtin
 
-  validates :name, :presence => true, :uniqueness => true,  :length => {:maximum => 30}, :format => {:with => /\A\w[\w\s\'\-]*\w\Z/i}
+  validates :name, :presence => true, :uniqueness => true, :format => {:with => /\A\w[\w\s\'\-]*\w\Z/i}
   validates :builtin, :inclusion => { :in => 0..2 }
 
   scoped_search :on => :name, :complete_value => true
@@ -46,36 +51,13 @@ class Role < ActiveRecord::Base
     self.builtin = 0
   end
 
-  def permissions
-    read_attribute(:permissions) || []
-  end
-
-  def permissions=(perms)
-    perms = perms.collect {|p| p.to_sym unless p.blank? }.compact.uniq if perms
-    write_attribute(:permissions, perms)
-  end
-
-  def add_permission!(*perms)
-    self.permissions = [] unless permissions.is_a?(Array)
-
-    permissions_will_change!
-    perms.each do |p|
-      p = p.to_sym
-      permissions << p unless permissions.include?(p)
-    end
-    save!
-  end
-
-  def remove_permission!(*perms)
-    return unless permissions.is_a?(Array)
-    permissions_will_change!
-    perms.each { |p| permissions.delete(p.to_sym) }
-    save!
-  end
-
   # Returns true if the role has the given permission
   def has_permission?(perm)
-    !permissions.nil? && permissions.include?(perm.to_sym)
+    permission_names.include?(perm.to_sym)
+  end
+
+  def permission_names
+    @permission_names ||= permissions.map { |p| p.name.to_sym }
   end
 
   # Return true if the role is a builtin role
@@ -94,19 +76,13 @@ class Role < ActiveRecord::Base
   # * a permission Symbol (eg. :edit_project)
   def allowed_to?(action)
     if action.is_a? Hash
+      action[:controller] = action[:controller][1..-1] if action[:controller].starts_with?('/')
       allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
     else
       allowed_permissions.include? action
     end
   end
 
-  # Return all the permissions that can be given to the role
-  def setable_permissions
-    setable_permissions  = Foreman::AccessControl.permissions - Foreman::AccessControl.public_permissions
-    setable_permissions -= Foreman::AccessControl.loggedin_only_permissions if self.builtin == BUILTIN_ANONYMOUS
-    setable_permissions
-  end
-
   # Find all the roles that can be given to a user
   def self.find_all_givable
     all(:conditions => {:builtin => 0}, :order => 'name')
@@ -138,9 +114,35 @@ class Role < ActiveRecord::Base
     anonymous_role
   end
 
+  # options can have following keys
+  # :search - scoped search applied to built filters
+  def add_permissions(permissions, options = {})
+    permissions = Array(permissions)
+    search = options.delete(:search)
+
+    collection = Permission.where(:name => permissions).all
+    raise ArgumentError, 'some permissions were not found' if collection.size != permissions.size
+
+    collection.group_by(&:resource_type).each do |resource_type, grouped_permissions|
+      filter = self.filters.build(:search => search)
+      filter.role ||= self
+
+      grouped_permissions.each do |permission|
+        filtering = filter.filterings.build
+        filtering.filter = filter
+        filtering.permission = permission
+      end
+    end
+  end
+
+  def add_permissions!(*args)
+    add_permissions(*args)
+    save!
+  end
+
 private
   def allowed_permissions
-    @allowed_permissions ||= permissions + Foreman::AccessControl.public_permissions.collect {|p| p.name}
+    @allowed_permissions ||= permission_names + Foreman::AccessControl.public_permissions.map(&:name)
   end
 
   def allowed_actions
diff --git a/app/models/setting/general.rb b/app/models/setting/general.rb
index e6a69296f..13f6a65e5 100644
--- a/app/models/setting/general.rb
+++ b/app/models/setting/general.rb
@@ -13,6 +13,7 @@ class Setting::General < Setting
         self.set('foreman_url', N_("URL where your Foreman instance is reachable (see also Provisioning > unattended_url)"), "#{protocol}://#{Facter.fqdn}"),
         self.set('email_reply_address', N_("Email reply address for emails that Foreman is sending"), "Foreman-noreply@#{domain}"),
         self.set('entries_per_page', N_("Number of records shown per page in Foreman"), 20),
+        self.set('fix_db_cache', N_('Fix DB cache on next Foreman restart'),false),
         self.set('authorize_login_delegation', N_("Authorize login delegation with REMOTE_USER environment variable"),false),
         self.set('authorize_login_delegation_api', N_("Authorize login delegation with REMOTE_USER environment variable for API calls too"),false),
         self.set('idle_timeout', N_("Log out idle users after a certain number of minutes"),60),
diff --git a/app/models/smart_proxy.rb b/app/models/smart_proxy.rb
index 3e3adc289..e0a45af7a 100644
--- a/app/models/smart_proxy.rb
+++ b/app/models/smart_proxy.rb
@@ -1,5 +1,5 @@
 class SmartProxy < ActiveRecord::Base
-  include Authorization
+  include Authorizable
   include Taxonomix
   audited :allow_mass_assignment => true
 
@@ -22,6 +22,8 @@ class SmartProxy < ActiveRecord::Base
   # There should be no problem with associating features before the proxy is saved as the whole operation is in a transaction
   before_save :sanitize_url, :associate_features
 
+  scoped_search :on => :name, :complete_value => :true
+
   # with proc support, default_scope can no longer be chained
   # include all default scoping here
   default_scope lambda {
@@ -30,12 +32,6 @@ class SmartProxy < ActiveRecord::Base
     end
   }
 
-  scope :my_proxies, lambda {
-    user       = User.current
-    conditions = user.admin? || user.allowed_to?({ :controller => :smart_proxy, :action => :index }) ? {} : '1 = 0'
-    where(conditions)
-  }
-
   Feature.name_map.each { |f, v| scope "#{f}_proxies".to_sym, where(:features => { :name => v }).joins(:features) }
 
   def hostname
diff --git a/app/models/subnet.rb b/app/models/subnet.rb
index b94cbed80..2ac65fdea 100644
--- a/app/models/subnet.rb
+++ b/app/models/subnet.rb
@@ -1,6 +1,6 @@
 require 'ipaddr'
 class Subnet < ActiveRecord::Base
-  include Authorization
+  include Authorizable
   include Taxonomix
   audited :allow_mass_assignment => true
 
diff --git a/app/models/taxonomy.rb b/app/models/taxonomy.rb
index c2b344e39..6bbf6a5b7 100644
--- a/app/models/taxonomy.rb
+++ b/app/models/taxonomy.rb
@@ -1,5 +1,6 @@
 class Taxonomy < ActiveRecord::Base
-  include Authorization
+  include Authorizable
+
   include NestedAncestryCommon
 
   serialize :ignore_types, Array
diff --git a/app/models/template_combination.rb b/app/models/template_combination.rb
index cf1c11e92..090eaa9c5 100644
--- a/app/models/template_combination.rb
+++ b/app/models/template_combination.rb
@@ -5,8 +5,4 @@ class TemplateCombination < ActiveRecord::Base
   validates :environment_id, :uniqueness => {:scope => [:hostgroup_id, :config_template_id]}
   validates :hostgroup_id, :uniqueness => {:scope => [:environment_id, :config_template_id]}
 
-  # process_resource_error relies on presence of this method
-  def permission_failed?
-    false
-  end
 end
diff --git a/app/models/user.rb b/app/models/user.rb
index e9fd8baea..c8e0fc88e 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,7 +1,7 @@
 require 'digest/sha1'
 
 class User < ActiveRecord::Base
-  include Authorization
+  include Authorizable
   include Foreman::ThreadSession::UserModel
   include Taxonomix
   audited :except => [:last_login_on, :password, :password_hash, :password_salt, :password_confirmation], :allow_mass_assignment => true
@@ -16,10 +16,16 @@ class User < ActiveRecord::Base
   has_many :auditable_changes, :class_name => '::Audit', :as => :user
   has_many :usergroup_member, :as => :member, :dependent => :destroy
   has_many :usergroups, :through => :usergroup_member
+  has_many :cached_usergroup_members
+  has_many :cached_usergroups, :through => :cached_usergroup_members, :source => :usergroup
   has_many :direct_hosts, :as => :owner, :class_name => "Host"
   has_and_belongs_to_many :notices, :join_table => 'user_notices'
-  has_many :user_roles, :dependent => :destroy
-  has_many :roles, :through => :user_roles
+  has_many :user_roles, :dependent => :destroy, :foreign_key => 'owner_id', :conditions => {:owner_type => self.to_s}
+  has_many :roles, :through => :user_roles, :dependent => :destroy
+  has_many :cached_user_roles, :dependent => :destroy
+  has_many :cached_roles, :through => :cached_user_roles, :source => :role, :uniq => true
+  has_many :filters, :through => :cached_roles
+  has_many :permissions, :through => :filters
   has_and_belongs_to_many :compute_resources, :join_table => "user_compute_resources"
   has_and_belongs_to_many :domains,           :join_table => "user_domains"
   has_many :user_hostgroups, :dependent => :destroy
@@ -28,8 +34,16 @@ class User < ActiveRecord::Base
   has_many :facts, :through => :user_facts, :source => :fact_name
   attr_name :login
 
-  scope :except_admin, lambda { where(:admin => false) }
-  scope :only_admin, lambda { where(:admin => true) }
+  scope :except_admin, lambda {
+    includes(:cached_usergroups).
+        where(["(#{self.table_name}.admin = ? OR #{self.table_name}.admin IS NULL) AND " +
+                   "(#{Usergroup.table_name}.admin = ? OR #{Usergroup.table_name}.admin IS NULL)",
+               false, false])
+  }
+  scope :only_admin, lambda {
+    includes(:cached_usergroups).
+        where(["#{self.table_name}.admin = ? OR #{Usergroup.table_name}.admin = ?", true, true])
+  }
 
   accepts_nested_attributes_for :user_facts, :reject_if => lambda { |a| a[:criteria].blank? }, :allow_destroy => true
 
@@ -57,9 +71,10 @@ class User < ActiveRecord::Base
   scoped_search :on => :firstname, :complete_value => :true
   scoped_search :on => :lastname, :complete_value => :true
   scoped_search :on => :mail, :complete_value => :true
-  scoped_search :on => :admin, :complete_value => {:true => true, :false => false}
+  scoped_search :on => :admin, :complete_value => { :true => true, :false => false }, :ext_method => :search_by_admin
   scoped_search :on => :last_login_on, :complete_value => :true, :only_explicit => true
   scoped_search :in => :roles, :on => :name, :rename => :role, :complete_value => true
+  scoped_search :in => :cached_usergroups, :on => :name, :rename => :usergroup, :complete_value => true
 
   default_scope lambda {
     with_taxonomy_scope do
@@ -67,6 +82,35 @@ class User < ActiveRecord::Base
     end
   }
 
+  def can?(permission, subject = nil)
+    if self.admin?
+      true
+    else
+      @authorizer ||= Authorizer.new(self)
+      @authorizer.can?(permission, subject)
+    end
+  end
+
+  def self.search_by_admin(key, operator, value)
+    value      = value == 'true'
+    value      = !value if operator == '<>'
+    conditions = [self.table_name, Usergroup.table_name].map do |base|
+      "(#{base}.admin = ?" + (value ? ')' : " OR #{base}.admin IS NULL)")
+    end
+    conditions = conditions.join(value ? ' OR ' : ' AND ')
+
+    {
+        :include    => :cached_usergroups,
+        :conditions => sanitize_sql_for_conditions([conditions, value, value])
+    }
+  end
+
+  # note that if you assign user new usergroups which change the admin flag you must save
+  # the record before #admin? will reflect this
+  def admin?
+    read_attribute(:admin) || cached_usergroups.any?(&:admin?)
+  end
+
   def to_label
     (firstname.present? || lastname.present?) ? "#{firstname} #{lastname}" : login
   end
@@ -195,7 +239,7 @@ class User < ActiveRecord::Base
       action[:controller] = action[:controller].to_s.gsub(/::/, "_").sub(/^\//,'').underscore
       return true if editing_self?(action)
     end
-    roles.detect {|role| role.allowed_to?(action)}.present?
+    cached_roles.detect {|role| role.allowed_to?(action)}.present?
   end
 
   def logged?
diff --git a/app/models/user_role.rb b/app/models/user_role.rb
index d78d7c549..d2364ae43 100644
--- a/app/models/user_role.rb
+++ b/app/models/user_role.rb
@@ -16,9 +16,53 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 class UserRole < ActiveRecord::Base
-  belongs_to :user
+  belongs_to :owner, :polymorphic => true
   belongs_to :role
 
+  has_many :cached_user_roles, :dependent => :destroy
+
   validates :role_id, :presence => true
-  validates :user_id, :presence => true, :uniqueness => {:scope => :role_id, :message => N_("has this role already")}
+  validates :owner_id, :presence => true, :uniqueness => {:scope => [:role_id, :owner_type],
+                                                          :message => N_("has this role already")}
+  def user_role?
+    self.owner_type == 'User'
+  end
+
+  def user_group_role?
+    self.owner_type == 'Usergroup'
+  end
+
+  before_save :remove_cache!
+  after_save :cache_user_roles!
+  before_destroy :remove_cache!
+
+  private
+
+  def remove_cache!
+    cached_user_roles.destroy_all
+  end
+
+  def cache_user_roles!
+    if self.user_role?
+      built = build_user_role_cache
+    elsif self.user_group_role?
+      built = build_user_group_role_cache(self.owner)
+    else
+      raise 'unknown UserRole owner type'
+    end
+
+    built.all?(&:save!)
+  end
+
+  def build_user_role_cache
+    [ self.cached_user_roles.build(:user => owner, :role => role) ]
+  end
+
+  def build_user_group_role_cache(owner)
+    cache = []
+    cache += owner.users.map { |m| self.cached_user_roles.build(:user => m, :role => role) }
+    cache += owner.usergroups.map { |g| build_user_group_role_cache(g) }
+    cache.flatten
+  end
+
 end
diff --git a/app/models/usergroup.rb b/app/models/usergroup.rb
index 1ee38e41d..84757012e 100644
--- a/app/models/usergroup.rb
+++ b/app/models/usergroup.rb
@@ -1,10 +1,19 @@
 class Usergroup < ActiveRecord::Base
-  include Authorization
   audited :allow_mass_assignment => true
+  include Authorizable
+
+  has_many :user_roles, :dependent => :destroy, :foreign_key => 'owner_id', :conditions => {:owner_type => self.to_s}
+  has_many :roles, :through => :user_roles, :dependent => :destroy
 
   has_many :usergroup_members, :dependent => :destroy
-  has_many :users,      :through => :usergroup_members, :source => :member, :source_type => 'User'
-  has_many :usergroups, :through => :usergroup_members, :source => :member, :source_type => 'Usergroup'
+  has_many :users,      :through => :usergroup_members, :source => :member, :source_type => 'User', :dependent => :destroy
+  has_many :usergroups, :through => :usergroup_members, :source => :member, :source_type => 'Usergroup', :dependent => :destroy
+
+  has_many :cached_usergroup_members
+  has_many :usergroup_parents, :dependent => :destroy, :foreign_key => 'member_id',
+           :conditions => "member_type = 'Usergroup'", :class_name => 'UsergroupMember'
+  has_many :parents,    :through => :usergroup_parents, :source => :usergroup, :dependent => :destroy
+
 
   has_many_hosts :as => :owner
   validates :name, :uniqueness => true
diff --git a/app/models/usergroup_member.rb b/app/models/usergroup_member.rb
index 6fea6dd53..8b1f9d319 100644
--- a/app/models/usergroup_member.rb
+++ b/app/models/usergroup_member.rb
@@ -2,4 +2,112 @@ class UsergroupMember < ActiveRecord::Base
   belongs_to :member, :polymorphic => true
   belongs_to :usergroup
 
+  before_validation :ensure_no_cycle
+  before_update :remove_old_cache_for_old_record
+  after_save :add_new_cache
+  after_destroy :remove_old_cache
+
+  scope :user_memberships, lambda { where("member_type = 'User'") }
+  scope :usergroup_memberships, lambda { where("member_type = 'Usergroup'") }
+
+  private
+
+  def ensure_no_cycle
+    current = UsergroupMember.usergroup_memberships
+    EnsureNoCycle.new(current, :usergroup_id, :member_id).ensure(self)
+  end
+
+  def add_new_cache
+    find_all_affected_users.each do |user|
+      find_all_user_roles.each do |user_role|
+        CachedUserRole.create!(:user      => user, :role => user_role.role,
+                               :user_role => user_role)
+      end
+
+      find_all_usergroups.each do |group|
+        CachedUsergroupMember.create!(:user => user, :usergroup => group)
+      end
+    end
+  end
+
+  def remove_old_cache_for_old_record
+    klass = member_type_changed? ? self.member_type_was.constantize : self.member_type.constantize
+    users = member_id_changed? ? find_all_affected_users_for(klass.find(member_id_was)).flatten : find_all_affected_users
+    roles = usergroup_id_changed? ? find_all_user_roles_for(Usergroup.find(usergroup_id_was)).flatten : find_all_user_roles
+
+    drop_role_cache(users, roles)
+
+    groups = usergroup_id_changed? ? find_all_usergroups_for(Usergroup.find(usergroup_id_was)).flatten : find_all_usergroups
+    drop_group_cache(users, groups)
+  end
+
+  def remove_old_cache
+    users = find_all_affected_users
+    drop_role_cache(users, find_all_user_roles)
+    drop_group_cache(users, find_all_usergroups)
+
+    # we need to recache records that may got deleted unintentionally
+    # we can't detect exact records to delete since we'd have to distinguish by whole path
+    recache_memberships
+  end
+
+  def recache_memberships
+    find_all_affected_memberships.each(&:save!)
+  end
+
+  def drop_role_cache(users, user_roles)
+    CachedUserRole.where(:user_role_id => user_roles.map(&:id), :user_id => users.map(&:id)).destroy_all
+  end
+
+  def drop_group_cache(users, groups)
+    CachedUsergroupMember.where(:user_id => users.map(&:id), :usergroup_id => groups.map(&:id)).destroy_all
+  end
+
+  def find_all_affected_users
+    find_all_affected_users_for(member).flatten.uniq
+  end
+
+  def find_all_affected_users_for(member)
+    if member.is_a?(User)
+      [member]
+    elsif member.is_a?(Usergroup)
+      [member.users + member.usergroups.map { |g| find_all_affected_users_for(g) }]
+    else
+      raise ArgumentError, "Unknown member type #{member}"
+    end
+  end
+
+  def find_all_affected_memberships
+    [
+        find_all_affected_memberships_for(member, :usergroups),
+        find_all_affected_memberships_for(usergroup, :parents)
+    ].flatten
+  end
+
+  def find_all_affected_memberships_for(member, direction = :usergroups)
+    if member.is_a?(User)
+      [ member.usergroup_member ]
+    elsif member.is_a?(Usergroup)
+      [ member.usergroup_members.user_memberships +
+           member.send(direction).map { |g| find_all_affected_memberships_for(g, direction) } ]
+    else
+      raise ArgumentError, "Unknown member type #{member}"
+    end
+  end
+
+  def find_all_user_roles
+    find_all_user_roles_for(usergroup).flatten
+  end
+
+  def find_all_user_roles_for(usergroup)
+    usergroup.user_roles + usergroup.parents.map { |g| find_all_user_roles_for(g) }
+  end
+
+  def find_all_usergroups
+    find_all_usergroups_for(usergroup).flatten
+  end
+
+  def find_all_usergroups_for(usergroup)
+    [ usergroup ] + usergroup.parents.map { |p| find_all_usergroups_for(p) }
+  end
 end
diff --git a/app/services/authorizer.rb b/app/services/authorizer.rb
new file mode 100644
index 000000000..aec2a7c1d
--- /dev/null
+++ b/app/services/authorizer.rb
@@ -0,0 +1,136 @@
+class Authorizer
+  attr_accessor :user, :base_collection, :organization_ids, :location_ids
+
+  def initialize(user, options = {})
+
+    @cache = HashWithIndifferentAccess.new { |h, k| h[k] = HashWithIndifferentAccess.new }
+    self.user = user
+    self.base_collection = options.delete(:collection)
+  end
+
+  def can?(permission, subject = nil)
+    if subject.nil?
+      user.permissions.where(:name => permission).present?
+    else
+      return true if user.admin?
+      collection = @cache[subject.class.to_s][permission] ||= find_collection(subject.class, :permission => permission)
+      collection.include?(subject)
+    end
+  end
+
+  def find_collection(resource_class, options = {})
+    permission = options.delete :permission
+
+    base = user.filters.joins(:permissions).where(["#{Permission.table_name}.resource_type = ?", resource_name(resource_class)])
+    all_filters = permission.nil? ? base : base.where(["#{Permission.table_name}.name = ?", permission])
+
+    organization_ids = allowed_organizations(resource_class)
+    location_ids     = allowed_locations(resource_class)
+
+    organizations, locations, values = taxonomy_conditions(organization_ids, location_ids)
+    all_filters = all_filters.joins(taxonomy_join).where(["#{TaxableTaxonomy.table_name}.id IS NULL " +
+                                                              "OR (#{organizations}) " +
+                                                              "OR (#{locations})",
+                                                          *values]).uniq
+
+    all_filters = all_filters.all # load all records, so #empty? does not call extra COUNT(*) query
+    return resource_class.where('1=0') if all_filters.empty?
+
+    unless @base_collection.nil?
+      if @base_collection.empty?
+        return resource_class.where('1=0')
+      else
+        resource_class = resource_class.where(:id => base_ids)
+      end
+    end
+
+    return resource_class.scoped if all_filters.any?(&:unlimited?)
+
+    search_string = build_scoped_search_condition(all_filters.select(&:limited?))
+    resource_class.search_for(search_string)
+  end
+
+  def build_scoped_search_condition(filters)
+    raise ArgumentError if filters.blank?
+
+    strings = filters.map { |f| "(#{f.search_condition.blank? ? '1=1' : f.search_condition})" }
+    strings.join(' OR ')
+  end
+
+  private
+
+  def allowed_organizations(resource_class)
+    allowed_taxonomies(resource_class, 'organization')
+  end
+
+  def allowed_locations(resource_class)
+    allowed_taxonomies(resource_class, 'location')
+  end
+
+  # return array of taxonomies that were used by default scope
+  # if model does not support taxonomies, we return empty array indicating
+  #   we should not filter on taxonomies
+  # otherwise we fetch it from model, if it's empty
+  #   for admin user we return empty array which means don't limit
+  #   for normal user we allow user taxonomies only
+  def allowed_taxonomies(resource_class, type)
+    taxonomy_ids = []
+    if resource_class.respond_to?("used_#{type}_ids")
+      taxonomy_ids = resource_class.send("used_#{type}_ids")
+      if taxonomy_ids.empty? && !User.current.try(:admin?)
+        taxonomy_ids = User.current.try("#{type}_ids")
+      end
+    end
+    taxonomy_ids
+  end
+
+  def taxonomy_join
+    "LEFT JOIN #{TaxableTaxonomy.table_name} ON " +
+        "(#{Filter.table_name}.id = #{TaxableTaxonomy.table_name}.taxable_id AND taxable_type = 'Filter') " +
+        "LEFT JOIN #{Taxonomy.table_name} ON " +
+        "(#{Taxonomy.table_name}.id = #{TaxableTaxonomy.table_name}.taxonomy_id)"
+  end
+
+  def taxonomy_conditions(organization_ids, location_ids)
+    values = []
+
+    organizations = "#{Taxonomy.table_name}.type = ?"
+    values.push 'Organization'
+    unless organization_ids.empty?
+      organizations += " AND #{Taxonomy.table_name}.id IN (?)"
+      values.push organization_ids
+    end
+
+    locations = "#{Taxonomy.table_name}.type = ?"
+    values.push 'Location'
+    unless location_ids.empty?
+      locations += " AND #{Taxonomy.table_name}.id IN (?)"
+      values.push location_ids
+    end
+
+    return [organizations, locations, values]
+  end
+
+  # sometimes we need exceptions however we don't want to just split namespaces
+  def resource_name(klass)
+    return 'Operatingsystem' if klass <= Operatingsystem
+
+    case name = klass.to_s
+      when 'Audited::Adapters::ActiveRecord::Audit'
+        'Audit'
+      when /\AHost::.*\Z/
+        'Host'
+      when /\AForeman::Model::.*\Z/
+        'ComputeResource'
+      else
+        name
+      end
+  end
+
+  def base_ids
+    raise ArgumentError, 'you must set base_collection to get base_ids' if @base_collection.nil?
+
+    @base_ids ||= @base_collection.all? { |i| i.is_a?(Fixnum) } ? @base_collection : @base_collection.map(&:id)
+  end
+
+end
diff --git a/app/services/cache_manager.rb b/app/services/cache_manager.rb
new file mode 100644
index 000000000..2f1509777
--- /dev/null
+++ b/app/services/cache_manager.rb
@@ -0,0 +1,23 @@
+class CacheManager
+  def self.delete_old_permission_cache
+    CachedUsergroupMember.delete_all
+    CachedUserRole.delete_all
+  end
+
+  def self.create_new_permission_cache
+    UsergroupMember.all.map &:save!
+    UserRole.all.map &:save!
+  end
+
+  def self.create_new_filter_cache
+    Filter.all.each &:save!
+  end
+
+  def self.recache!
+    Rails.logger.warn 'Recreating the whole DB cache'
+    delete_old_permission_cache
+    create_new_permission_cache
+    create_new_filter_cache
+    Setting::General[:fix_db_cache] = false
+  end
+end
diff --git a/app/services/dashboard.rb b/app/services/dashboard.rb
index 3ebb4cb27..5bf974822 100644
--- a/app/services/dashboard.rb
+++ b/app/services/dashboard.rb
@@ -13,7 +13,7 @@ class Dashboard
   end
 
   def hosts
-    @hosts ||= Host.my_hosts.search_for(filter)
+    @hosts ||= Host.authorized(:view_hosts, Host).search_for(filter)
   end
 
   private
diff --git a/app/services/foreman/access_permissions.rb b/app/services/foreman/access_permissions.rb
index a52f37448..d9315c5f5 100644
--- a/app/services/foreman/access_permissions.rb
+++ b/app/services/foreman/access_permissions.rb
@@ -2,6 +2,11 @@ require 'foreman/access_control'
 
 # Permissions
 Foreman::AccessControl.map do |map|
+  map.security_block :public do |map|
+    map.permission :user_logout, { :users => [:logout] }, :public => true
+    map.permission :my_account, { :users => [:edit] }, :public => true
+  end
+
   map.security_block :architectures do |map|
     map.permission :view_architectures,
                    :architectures => [:index, :show, :auto_complete_search],
@@ -194,6 +199,17 @@ Foreman::AccessControl.map do |map|
                                                  :"api/v2/override_values" => [:create, :update, :destroy]}
   end
 
+  map.security_block :filters do |map|
+    map.permission :view_filters,    {:filters => [:index, :auto_complete_search],
+                                      :'api/v2/filters' => [:index, :show]}
+    map.permission :create_filters,  {:filters => [:new, :create],
+                                      :'api/v2/filters' => [:create]}
+    map.permission :edit_filters,    {:filters => [:edit, :update], :permissions => [:index],
+                                      :'api/v2/filters' => [:update], :'api/v2/permissions' => [:index, :show]}
+    map.permission :destroy_filters, {:filters => [:destroy],
+                                      :'api/v2/filters' => [:destroy]}
+  end
+
   map.security_block :global_variables do |map|
     map.permission :view_globals,    {:common_parameters => [:index, :show, :auto_complete_search],
                                       :"api/v1/common_parameters" => [:index, :show],
@@ -469,8 +485,19 @@ Foreman::AccessControl.map do |map|
                                             }
   end
 
+  map.security_block :roles do |map|
+    map.permission :view_roles,    {:roles => [:index, :auto_complete_search],
+                                    :'api/v2/roles' => [:index, :show]}
+    map.permission :create_roles,  {:roles => [:new, :create, :clone],
+                                    :'api/v2/roles' => [:create]}
+    map.permission :edit_roles,    {:roles => [:edit, :update],
+                                    :'api/v2/roles' => [:update]}
+    map.permission :destroy_roles, {:roles => [:destroy],
+                                    :'api/v2/roles' => [:destroy]}
+  end
+
   map.security_block :smart_proxies do |map|
-    map.permission :view_smart_proxies,    {:smart_proxies => [:index, :ping],
+    map.permission :view_smart_proxies,    {:smart_proxies => [:index, :ping, :auto_complete_search],
                                           :"api/v1/smart_proxies" => [:index, :show],
                                           :"api/v2/smart_proxies" => [:index, :show]
     }
@@ -546,7 +573,7 @@ Foreman::AccessControl.map do |map|
   end
 
   map.security_block :usergroups do |map|
-    map.permission :view_usergroups,    {:usergroups => [:index, :show],
+    map.permission :view_usergroups,    {:usergroups => [:index, :show, :auto_complete_search],
                                          :"api/v1/usergroups" => [:index, :show],
                                          :"api/v2/usergroups" => [:index, :show]
     }
diff --git a/app/services/foreman/plugin.rb b/app/services/foreman/plugin.rb
index 759794544..e5818fc72 100644
--- a/app/services/foreman/plugin.rb
+++ b/app/services/foreman/plugin.rb
@@ -164,7 +164,13 @@ module Foreman #:nodoc:
     end
 
     # Defines a permission called name for the given controller=>actions
+    # :options can contain :resource_type key which is the string of resource
+    #   class to which this permissions is related, rest of options is passed
+    #   to AccessControl
     def permission(name, hash, options={})
+      resource_type = options.delete(:resource_type)
+      Permission.first rescue return false
+      Permission.find_or_create_by_name_and_resource_type(name, resource_type)
       options.merge!(:security_block => @security_block)
       Foreman::AccessControl.map do |map|
         map.permission name, hash, options
@@ -175,7 +181,8 @@ module Foreman #:nodoc:
     def role(name, permissions)
       Role.transaction do
         role = Role.find_or_create_by_name(name)
-        role.update_attribute :permissions, permissions if role.permissions.empty?
+        Permission.first rescue return false
+        role.add_permissions!(permissions) if role.permissions.empty?
       end
     end
 
diff --git a/app/views/about/index.html.erb b/app/views/about/index.html.erb
index 2bc7cfbc5..74bd6c76f 100644
--- a/app/views/about/index.html.erb
+++ b/app/views/about/index.html.erb
@@ -15,7 +15,7 @@
       </ul>
       <div class="tab-content">
         <div class="tab-pane active" id="smart_proxies">
-          <% if @proxies.empty? %>
+          <% if @smart_proxies.empty? %>
             <p class="ca"><%= _("No smart proxies to show") %></p>
           <% else %>
             <table class="table table-striped">
@@ -24,7 +24,7 @@
                 <th><%= _("Features") %></th>
                 <th><%= _("Status") %></th>
               </tr>
-              <% @proxies.each do |proxy| %>
+              <% @smart_proxies.each do |proxy| %>
                 <tr>
                   <td><%= link_to_if_authorized proxy.name, hash_for_edit_smart_proxy_path(:id => proxy.id) %></td>
                   <td><%=h proxy.features.to_sentence %></td>
diff --git a/app/views/api/v2/filters/base.json.rabl b/app/views/api/v2/filters/base.json.rabl
new file mode 100644
index 000000000..5397cbe54
--- /dev/null
+++ b/app/views/api/v2/filters/base.json.rabl
@@ -0,0 +1,3 @@
+object @filter
+
+attributes :id
diff --git a/app/views/api/v2/filters/create.json.rabl b/app/views/api/v2/filters/create.json.rabl
new file mode 100644
index 000000000..359008c6f
--- /dev/null
+++ b/app/views/api/v2/filters/create.json.rabl
@@ -0,0 +1,3 @@
+object @filter
+
+extends "api/v2/filters/show"
diff --git a/app/views/api/v2/filters/index.json.rabl b/app/views/api/v2/filters/index.json.rabl
new file mode 100644
index 000000000..f33e6bb7d
--- /dev/null
+++ b/app/views/api/v2/filters/index.json.rabl
@@ -0,0 +1,3 @@
+collection @filters
+
+extends "api/v2/filters/main"
diff --git a/app/views/api/v2/filters/main.json.rabl b/app/views/api/v2/filters/main.json.rabl
new file mode 100644
index 000000000..fe8c68f7c
--- /dev/null
+++ b/app/views/api/v2/filters/main.json.rabl
@@ -0,0 +1,5 @@
+object @filter
+
+extends "api/v2/filters/base"
+
+attributes :search, :resource_type, :role_id, :created_at, :updated_at
diff --git a/app/views/api/v2/filters/show.json.rabl b/app/views/api/v2/filters/show.json.rabl
new file mode 100644
index 000000000..0849e7198
--- /dev/null
+++ b/app/views/api/v2/filters/show.json.rabl
@@ -0,0 +1,20 @@
+object @filter
+
+extends "api/v2/filters/main"
+
+child :permissions => :permissions do
+  extends "api/v2/permissions/base"
+end
+
+# TODO: Fix when https://github.com/theforeman/foreman/pull/1208 is merged
+if SETTINGS[:organizations_enabled]
+  child :organizations => :organizations do
+    attributes :id, :name
+  end
+end
+
+if SETTINGS[:locations_enabled]
+  child :locations => :locations do
+    attributes :id, :name
+  end
+end
diff --git a/app/views/api/v2/permissions/base.json.rabl b/app/views/api/v2/permissions/base.json.rabl
new file mode 100644
index 000000000..725904d64
--- /dev/null
+++ b/app/views/api/v2/permissions/base.json.rabl
@@ -0,0 +1,3 @@
+object @permission
+
+attributes :name, :id
\ No newline at end of file
diff --git a/app/views/api/v2/permissions/index.json.rabl b/app/views/api/v2/permissions/index.json.rabl
new file mode 100644
index 000000000..a29ab7095
--- /dev/null
+++ b/app/views/api/v2/permissions/index.json.rabl
@@ -0,0 +1,3 @@
+collection @permissions
+
+extends "api/v2/permissions/main"
\ No newline at end of file
diff --git a/app/views/api/v2/permissions/main.json.rabl b/app/views/api/v2/permissions/main.json.rabl
new file mode 100644
index 000000000..6e4feeebc
--- /dev/null
+++ b/app/views/api/v2/permissions/main.json.rabl
@@ -0,0 +1,3 @@
+object @permission
+
+extends "api/v2/permissions/base"
diff --git a/app/views/api/v2/permissions/show.json.rabl b/app/views/api/v2/permissions/show.json.rabl
new file mode 100644
index 000000000..251b354b3
--- /dev/null
+++ b/app/views/api/v2/permissions/show.json.rabl
@@ -0,0 +1,3 @@
+object @permission
+
+extends "api/v2/permissions/main"
diff --git a/app/views/api/v2/roles/main.json.rabl b/app/views/api/v2/roles/main.json.rabl
index 8b5903678..38b38ffd1 100644
--- a/app/views/api/v2/roles/main.json.rabl
+++ b/app/views/api/v2/roles/main.json.rabl
@@ -2,4 +2,4 @@ object @role
 
 extends "api/v2/roles/base"
 
-attributes :builtin, :permissions, :created_at, :updated_at
+attributes :builtin, :created_at, :updated_at
diff --git a/app/views/api/v2/roles/show.json.rabl b/app/views/api/v2/roles/show.json.rabl
index 5ceec525c..874afd838 100644
--- a/app/views/api/v2/roles/show.json.rabl
+++ b/app/views/api/v2/roles/show.json.rabl
@@ -1,3 +1,7 @@
 object @role
 
 extends "api/v2/roles/main"
+
+child :filters => :filters do
+  extends "api/v2/filters/base"
+end
diff --git a/app/views/architectures/index.html.erb b/app/views/architectures/index.html.erb
index 10ee5b879..c011662a4 100644
--- a/app/views/architectures/index.html.erb
+++ b/app/views/architectures/index.html.erb
@@ -9,10 +9,12 @@
   </tr>
   <% for architecture in @architectures %>
     <tr>
-      <td class='col-md-3'><%= link_to_if_authorized(h(architecture.name), hash_for_edit_architecture_path(:id => architecture)) %></td>
+      <td class='col-md-3'><%= link_to_if_authorized(h(architecture.name),
+                                                     hash_for_edit_architecture_path(:id => architecture).merge(:auth_object => architecture, :authorizer => authorizer)) %></td>
       <td><%=h architecture.operatingsystems.map(&:to_label).to_sentence %></td>
       <td align="right">
-        <%= display_delete_if_authorized hash_for_architecture_path(:id => architecture.name), :confirm => "Delete #{architecture.name}?" %>
+        <%= display_delete_if_authorized hash_for_architecture_path(:id => architecture.name).merge(:auth_object => architecture, :authorizer => authorizer),
+                                         :confirm => "Delete #{architecture.name}?" %>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/audits/show.html.erb b/app/views/audits/show.html.erb
index 478dce3cd..0482e1bb0 100644
--- a/app/views/audits/show.html.erb
+++ b/app/views/audits/show.html.erb
@@ -1,11 +1,11 @@
 <% title "#{@audit.action.camelize} #{audited_type @audit}: #{audit_title @audit}" %>
 
 <%= title_actions link_to_if_authorized(_("Host details"),
-                                        hash_for_host_path(:id => @audit.auditable.to_param),
+                                        hash_for_host_path(:id => @audit.auditable.to_param).merge(:auth_object => @audit.auditable, :auth_action => 'view'),
                                         :title => _("Host details"),
                                         :class => 'btn btn-info') if @audit.auditable_type == 'Host' && @audit.auditable %>
 <%= title_actions link_to 'Back', :back, :class=>'btn btn-default' %>
-<% tmplt =  audit_template?(@audit) %>
+<% tmplt = audit_template?(@audit) %>
 
 <div class="tabbable">
   <ul class="nav nav-tabs">
diff --git a/app/views/autosign/index.html.erb b/app/views/autosign/index.html.erb
index 43559c42f..a6bb556ec 100644
--- a/app/views/autosign/index.html.erb
+++ b/app/views/autosign/index.html.erb
@@ -1,8 +1,7 @@
 <% title _("Autosign entries for %s") % @proxy %>
 
-<% title_actions display_link_if_authorized(_("Certificates"), hash_for_smart_proxy_puppetca_index_path(:smart_proxy_id => @proxy)),
+<% title_actions display_link_if_authorized(_("Certificates"), hash_for_smart_proxy_puppetca_index_path(:smart_proxy_id => @proxy).merge(:auth_object => @proxy, :permission => 'view_smart_proxies_puppetca')),
   autosign_form %>
-
 <table class='table table-bordered table-striped table-condensed'>
   <tr>
     <th><%= _("Name") %></th>
@@ -12,7 +11,7 @@
       <tr>
         <td><%= h cert%> </td>
         <td>
-          <%= action_buttons(display_delete_if_authorized hash_for_smart_proxy_autosign_path(:smart_proxy_id => @proxy, :id => cert), :class => 'delete')%>
+          <%= action_buttons(display_delete_if_authorized hash_for_smart_proxy_autosign_path(:smart_proxy_id => @proxy, :id => cert).merge(:auth_object => @proxy), :class => 'delete')%>
         </td>
       </tr>
   <% end %>
diff --git a/app/views/common_parameters/index.html.erb b/app/views/common_parameters/index.html.erb
index 4d1115011..4304e0ea6 100644
--- a/app/views/common_parameters/index.html.erb
+++ b/app/views/common_parameters/index.html.erb
@@ -10,10 +10,11 @@
   </tr>
   <% for common_parameter in @common_parameters %>
     <tr>
-      <td><%= link_to_if_authorized h(common_parameter), hash_for_edit_common_parameter_path(:id => common_parameter.id)%></td>
+      <td><%= link_to_if_authorized h(common_parameter), hash_for_edit_common_parameter_path(:id => common_parameter.id).merge(:auth_object => common_parameter, :authorizer => authorizer, :permission => 'edit_globals') %></td>
       <td style='overflow-wrap: break-word;'><%= trunc(common_parameter.value,80  ) %></td>
       <td class="ra">
-        <%= display_delete_if_authorized hash_for_common_parameter_path(:id => common_parameter), :confirm => _("Delete %s?") % common_parameter.name %>
+        <%= display_delete_if_authorized hash_for_common_parameter_path(:id => common_parameter).merge(:auth_object => common_parameter, :authorizer => authorizer, :permission => 'destroy_globals'),
+                                         :confirm => _("Delete %s?") % common_parameter.name %>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/compute_profiles/index.html.erb b/app/views/compute_profiles/index.html.erb
index f1c6aafc9..1dd17e2ef 100644
--- a/app/views/compute_profiles/index.html.erb
+++ b/app/views/compute_profiles/index.html.erb
@@ -9,11 +9,11 @@
   </tr>
   <% @compute_profiles.each do |compute_profile| %>
     <tr>
-      <td><%= link_to_if_authorized h(compute_profile.name), hash_for_compute_profile_path(compute_profile)%></td>
+      <td><%= link_to_if_authorized h(compute_profile.name), hash_for_compute_profile_path(compute_profile).merge(:auth_object => compute_profile, :authorizer => authorizer, :auth_action => 'edit') %></td>
       <td><%= action_buttons(
-                display_link_if_authorized(_('Edit'), hash_for_compute_profile_path(compute_profile)),
-                display_link_if_authorized(_('Rename'), hash_for_edit_compute_profile_path(compute_profile)),
-                display_delete_if_authorized(hash_for_compute_profile_path(compute_profile), :confirm => _('Delete %s?') % compute_profile.name))%>
+                display_link_if_authorized(_('Edit'), hash_for_compute_profile_path(compute_profile).merge(:auth_object => compute_profile, :authorizer => authorizer, :auth_action => 'edit')),
+                display_link_if_authorized(_('Rename'), hash_for_edit_compute_profile_path(compute_profile).merge(:auth_object => compute_profile, :authorizer => authorizer)),
+                display_delete_if_authorized(hash_for_compute_profile_path(compute_profile).merge(:auth_object => compute_profile, :authorizer => authorizer), :confirm => _('Delete %s?') % compute_profile.name))%>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/compute_profiles/show.html.erb b/app/views/compute_profiles/show.html.erb
index 9485e2061..c390fd74c 100644
--- a/app/views/compute_profiles/show.html.erb
+++ b/app/views/compute_profiles/show.html.erb
@@ -13,7 +13,7 @@
     <th><%= _("Compute Resource") %></th>
     <th><%= _("VM Attributes (%s)") % @compute_profile.name %></th>
   </tr>
-    <% ComputeResource.my_compute_resources.each do |compute_resource| %>
+    <% ComputeResource.authorized(:view_compute_resources).each do |compute_resource| %>
       <% compute_attribute = ComputeAttribute.where(:compute_profile_id => @compute_profile.id, :compute_resource_id => compute_resource.id).first %>
       <% which_path = if compute_attribute.try(:id)
                         edit_compute_profile_compute_attribute_path(@compute_profile.to_param, compute_attribute.id)
diff --git a/app/views/compute_resources/index.html.erb b/app/views/compute_resources/index.html.erb
index 8cb5a6371..5f83b9e44 100644
--- a/app/views/compute_resources/index.html.erb
+++ b/app/views/compute_resources/index.html.erb
@@ -13,8 +13,8 @@
     <td><%= link_to compute.name, compute %></td>
     <td><%= compute.provider_friendly_name %></td>
     <td>
-      <%= action_buttons(link_to(_('Edit'), edit_compute_resource_path(compute)),
-                         display_delete_if_authorized(hash_for_compute_resource_path(:id => compute), :confirm => _("Delete %s?") % compute.name)) %>
+      <%= action_buttons(link_to_if_authorized(_('Edit'), hash_for_edit_compute_resource_path(compute).merge(:auth_object => compute, :authorizer => authorizer)),
+                         display_delete_if_authorized(hash_for_compute_resource_path(:id => compute).merge(:auth_object => compute, :authorizer => authorizer), :confirm => _("Delete %s?") % compute.name)) %>
     </td>
   </tr>
 <% end %>
diff --git a/app/views/compute_resources/show.html.erb b/app/views/compute_resources/show.html.erb
index fbc8d4800..580f6fa8d 100644
--- a/app/views/compute_resources/show.html.erb
+++ b/app/views/compute_resources/show.html.erb
@@ -1,8 +1,10 @@
 <%= javascript 'compute_resource', 'lookup_keys'%>
 <% title @compute_resource.name %>
 
-<% title_actions display_link_if_authorized(_("Associate VMs"), hash_for_associate_compute_resource_path(:compute_resource_id => @compute_resource), :title=> _("Associate VMs to Foreman hosts"), :method => :put, :class=>"btn btn-default"),
-   link_to(_('Edit'), edit_compute_resource_path(@compute_resource), :class => "btn btn-default")  %>
+<% title_actions display_link_if_authorized(_("Associate VMs"),
+                                            hash_for_associate_compute_resource_path(:compute_resource_id => @compute_resource).merge(:auth_object => @compute_resource, :permission => 'edit_compute_resources'),
+                                            :title => _("Associate VMs to Foreman hosts"), :method => :put, :class=>"btn btn-default"),
+   link_to_if_authorized(_('Edit'), hash_for_edit_compute_resource_path(@compute_resource).merge(:auth_object => @compute_resource), :class => "btn btn-default")  %>
 
 <ul class="nav nav-tabs" data-tabs="tabs">
   <li class="active"><a href="#primary" data-toggle="tab"><%= _("Compute Resource") %></a></li>
diff --git a/app/views/compute_resources_vms/index/_ec2.html.erb b/app/views/compute_resources_vms/index/_ec2.html.erb
index bad97eb26..38f38c75b 100644
--- a/app/views/compute_resources_vms/index/_ec2.html.erb
+++ b/app/views/compute_resources_vms/index/_ec2.html.erb
@@ -10,13 +10,14 @@
   </thead>
   <% @vms.each do |vm| %>
     <tr>
-      <td><%= link_to_if_authorized vm.name, hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity) %></td>
+      <td><%= link_to_if_authorized vm.name, hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity).merge(:auth_object => @compute_resource, :authorizer => authorizer) %></td>
       <td><%= vm.dns %></td>
       <td><%= vm.flavor_id %></td>
       <td> <span <%= vm_power_class(vm.ready?) %>> <%= vm_state(vm) %></span> </td>
       <td>
         <%= unless vm.state == 'terminated' or vm.state == 'pending'
-              action_buttons(vm_power_action(vm), display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.id)))
+              action_buttons(vm_power_action(vm, authorizer),
+                             display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.id).merge(:auth_object => @compute_resource, :authorizer => authorizer)))
             end
         %>
       </td>
diff --git a/app/views/compute_resources_vms/index/_gce.html.erb b/app/views/compute_resources_vms/index/_gce.html.erb
index b2d68ca39..58f05dc03 100644
--- a/app/views/compute_resources_vms/index/_gce.html.erb
+++ b/app/views/compute_resources_vms/index/_gce.html.erb
@@ -9,11 +9,12 @@
   </thead>
   <% @vms.each do |vm| %>
     <tr>
-      <td><%= link_to_if_authorized vm.name, hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity) %></td>
+      <td><%= link_to_if_authorized vm.name, hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity).merge(:auth_object => @compute_resource, :authorizer => authorizer) %></td>
       <td><%= vm.pretty_machine_type %></td>
       <td><%= vm.state.downcase %></td>
       <td>
-        <%= action_buttons(vm_power_action(vm), display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity))) %>
+        <%= action_buttons(vm_power_action(vm, authorizer),
+                           display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity).merge(:auth_object => @compute_resource, :authorizer => authorizer))) %>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/compute_resources_vms/index/_libvirt.html.erb b/app/views/compute_resources_vms/index/_libvirt.html.erb
index 307a01b77..287c90a74 100644
--- a/app/views/compute_resources_vms/index/_libvirt.html.erb
+++ b/app/views/compute_resources_vms/index/_libvirt.html.erb
@@ -10,13 +10,13 @@
   </thead>
   <% @vms.each do |vm| %>
     <tr>
-      <td><%= link_to_if_authorized vm.name, hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.uuid) %></td>
+      <td><%= link_to_if_authorized vm.name, hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.uuid).merge(:auth_object => @compute_resource, :authorizer => authorizer) %></td>
       <td><%= vm.cpus %></td>
       <td> <%= number_to_human_size vm.memory_size*1024 %> </td>
       <td> <span <%= vm_power_class(vm.ready?) %>> <%= vm_state(vm) %></span> </td>
       <td>
-        <%= action_buttons(vm_power_action(vm),
-            display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.uuid))) %>
+        <%= action_buttons(vm_power_action(vm, authorizer),
+            display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.uuid).merge(:auth_object => @compute_resource, :authorizer => authorizer))) %>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/compute_resources_vms/index/_ovirt.html.erb b/app/views/compute_resources_vms/index/_ovirt.html.erb
index 0122e564e..0c5a1931c 100644
--- a/app/views/compute_resources_vms/index/_ovirt.html.erb
+++ b/app/views/compute_resources_vms/index/_ovirt.html.erb
@@ -10,13 +10,13 @@
   </thead>
   <% @vms.each do |vm| %>
     <tr>
-      <td><%= link_to_if_authorized vm.name, hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity) %></td>
+      <td><%= link_to_if_authorized vm.name, hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity).merge(:auth_object => @compute_resource, :authorizer => authorizer) %></td>
       <td><%= vm.cores %></td>
       <td> <%= number_to_human_size vm.memory %> </td>
       <td> <span <%= vm_power_class(vm.ready?) %>> <%= vm_state(vm) %></span> </td>
       <td>
-        <%= action_buttons(vm_power_action(vm),
-                           display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.id))) %>
+        <%= action_buttons(vm_power_action(vm, authorizer),
+                           display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.id).merge(:auth_object => @compute_resource, :authorizer => authorizer))) %>
       </td>
 
     </tr>
diff --git a/app/views/compute_resources_vms/index/_rackspace.html.erb b/app/views/compute_resources_vms/index/_rackspace.html.erb
index 0b6057df7..25883cefd 100644
--- a/app/views/compute_resources_vms/index/_rackspace.html.erb
+++ b/app/views/compute_resources_vms/index/_rackspace.html.erb
@@ -14,8 +14,8 @@
       <td> <span <%= vm_power_class(vm.ready?) %>> <%= vm_state(vm) %></span> </td>
       <td>
         <%= action_buttons(
-                vm_power_action(vm),
-                display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.id))) %>
+                vm_power_action(vm, authorizer),
+                display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.id).merge(:auth_object => @compute_resource, :authorizer => authorizer))) %>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/compute_resources_vms/index/_vmware.html.erb b/app/views/compute_resources_vms/index/_vmware.html.erb
index 7ae23128c..46ab07e26 100644
--- a/app/views/compute_resources_vms/index/_vmware.html.erb
+++ b/app/views/compute_resources_vms/index/_vmware.html.erb
@@ -12,15 +12,15 @@
   <tbody>
   <% @vms.each do |vm| %>
     <tr>
-      <td><%= link_to_if_authorized vm.name, hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity) %></td>
+      <td><%= link_to_if_authorized vm.name, hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity).merge(:auth_object => @compute_resource, :authorizer => authorizer) %></td>
       <td><%= vm.path %></td>
       <td><%= vm.cpus %></td>
-      <td><%= number_to_human_size vm.memory %></td> 
+      <td><%= number_to_human_size vm.memory %></td>
       <td><span <%= vm_power_class(vm.ready?) %>> <%= vm_state(vm) %></span> </td>
       <td>
-        <%= action_buttons(vm_power_action(vm),
-                           show_console_action(vm.ready?, display_link_if_authorized("Console", hash_for_console_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity))),
-                           display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity))) %>
+        <%= action_buttons(vm_power_action(vm, authorizer),
+                           show_console_action(vm.ready?, display_link_if_authorized("Console", hash_for_console_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity).merge(:auth_object => @compute_resource, :authorizer => authorizer))),
+                           display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity).merge(:auth_object => @compute_resource, :authorizer => authorizer))) %>
       </td>
 
     </tr>
diff --git a/app/views/compute_resources_vms/show.html.erb b/app/views/compute_resources_vms/show.html.erb
index 522bf7eaf..58c33e3b0 100644
--- a/app/views/compute_resources_vms/show.html.erb
+++ b/app/views/compute_resources_vms/show.html.erb
@@ -5,8 +5,8 @@
 <%
   all_actions = available_actions(@vm).push(
                   show_console_action(@vm.ready?,
-                  link_to_if_authorized(_("Console"), hash_for_console_compute_resource_vm_path, {:disabled => @vm.nil? || !@vm.ready?, :class => "btn btn-info"})),
-                  display_link_if_authorized(_("Associate VM"), hash_for_associate_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => @vm.identity), :title=> _("Associate VM to a Foreman host"), :method => :put, :class=>"btn"),
+                  link_to_if_authorized(_("Console"), hash_for_console_compute_resource_vm_path.merge(:auth_object => @compute_resource), {:disabled => @vm.nil? || !@vm.ready?, :class => "btn btn-info"})),
+                  display_link_if_authorized(_("Associate VM"), hash_for_associate_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => @vm.identity).merge(:auth_object => @compute_resource, :permission => 'edit_compute_resources'), :title=> _("Associate VM to a Foreman host"), :method => :put, :class=>"btn"),
                   link_to(_("Back"), compute_resource_path(@compute_resource), :class=>'btn btn-default'))
   title_actions *all_actions
 %>
diff --git a/app/views/config_templates/index.html.erb b/app/views/config_templates/index.html.erb
index bdc320b50..35fe4b4c8 100644
--- a/app/views/config_templates/index.html.erb
+++ b/app/views/config_templates/index.html.erb
@@ -18,11 +18,11 @@
   </tr>
   <% for config_template in @config_templates %>
     <tr>
-      <td><%= link_to_if_authorized h(config_template), hash_for_edit_config_template_path(:id => config_template.to_param) %></td>
+      <td><%= link_to_if_authorized h(config_template), hash_for_edit_config_template_path(:id => config_template.to_param).merge(:auth_object => config_template, :authorizer => authorizer, :permission => 'edit_templates') %></td>
       <td><%= combination config_template %></td>
       <td><%= config_template.try(:template_kind) %></td>
       <td><%= checked_icon config_template.snippet %></td>
-      <td><%= display_delete_if_authorized hash_for_config_template_path(:id => config_template.to_param),
+      <td><%= display_delete_if_authorized hash_for_config_template_path(:id => config_template.to_param).merge(:auth_object => config_template, :authorizer => authorizer, :permission => 'destroy_templates'),
                                            :confirm => _("Delete %s?") % config_template %> </td>
     </tr>
   <% end %>
diff --git a/app/views/domains/index.html.erb b/app/views/domains/index.html.erb
index 21114a250..cbf672b5e 100644
--- a/app/views/domains/index.html.erb
+++ b/app/views/domains/index.html.erb
@@ -10,9 +10,9 @@
   </tr>
   <% for domain in @domains %>
     <tr>
-      <td><%= link_to_if_authorized h(domain.fullname.empty? ? domain.name : domain.fullname), hash_for_edit_domain_path(:id => domain)%></td>
+      <td><%= link_to_if_authorized h(domain.fullname.empty? ? domain.name : domain.fullname), hash_for_edit_domain_path(:id => domain).merge(:auth_object => domain, :authorizer => authorizer) %></td>
       <td><%= link_to @host_counter[domain.id] || 0, hosts_path(:search => "domain = #{domain}") %>
-      <td><%= display_delete_if_authorized hash_for_domain_path(:id => domain), :confirm => _("Delete %s?") % domain.name %></td>
+      <td><%= display_delete_if_authorized hash_for_domain_path(:id => domain).merge(:auth_object => domain, :authorizer => authorizer), :confirm => _("Delete %s?") % domain.name %></td>
     </tr>
   <% end %>
 </table>
diff --git a/app/views/environments/index.html.erb b/app/views/environments/index.html.erb
index 8b156a800..cca5dda28 100644
--- a/app/views/environments/index.html.erb
+++ b/app/views/environments/index.html.erb
@@ -12,13 +12,13 @@
   <% @environments.each do |environment| %>
     <tr>
       <td>
-        <%= link_to_if_authorized environment.name, hash_for_edit_environment_path(:id => environment.name) %>
+        <%= link_to_if_authorized environment.name, hash_for_edit_environment_path(:id => environment.name).merge(:auth_object => environment, :authorizer => authorizer) %>
       </td>
       <td><%= link_to @host_counter[environment.id] || 0, hosts_path(:search => "environment = #{environment}") %>
       <td>
-        <%= action_buttons(link_to(_("Classes"), puppetclasses_path(:search => "environment = #{environment}")),
+        <%= action_buttons(link_to(_('Classes'), puppetclasses_path(:search => "environment = #{environment}")),
                            import_proxy_links(hash_for_import_environments_environments_path(:env => environment.name)),
-                           display_delete_if_authorized(hash_for_environment_path(:id => environment.name), :confirm => "Delete #{environment.name}?")) %>
+                           display_delete_if_authorized(hash_for_environment_path(:id => environment.name).merge(:auth_object => environment, :authorizer => authorizer), :confirm => "Delete #{environment.name}?")) %>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/filters/_form.html.erb b/app/views/filters/_form.html.erb
new file mode 100644
index 000000000..ffd4c683d
--- /dev/null
+++ b/app/views/filters/_form.html.erb
@@ -0,0 +1,69 @@
+<% javascript 'filters' %>
+
+<%= form_for @filter do |f| %>
+  <%= base_errors_for f.object %>
+  <ul class="nav nav-tabs" data-tabs="tabs">
+    <li class="active"><a href="#primary" data-toggle="tab"><%= _("Filter") %></a></li>
+    <% if show_location_tab? %>
+      <li><a href="#locations" data-toggle="tab"><%= _("Locations") %></a></li>
+    <% end %>
+    <% if show_organization_tab? %>
+      <li><a href="#organizations" data-toggle="tab"><%= _("Organizations") %></a></li>
+    <% end %>
+  </ul>
+
+  <div class="tab-content">
+
+    <div class="tab-pane active" id="primary">
+      <% if @role.present? %>
+        <%= field(f, :role_id, :label => _('Selected role')) do %>
+          <%= @role.name %>
+          <%= f.hidden_field :role_id, :value => f.object.role_id || @role.id %>
+        <% end %>
+      <% else %>
+        <%= select_f(f, :role_id, Role.scoped, :id, :name) %>
+      <% end %>
+
+      <%= selectable_f(f, :resource_type, Permission.resources_with_translations,
+                       {:include_blank => _('(Miscellaneous)')}, {:'data-url' => permissions_path}) %>
+
+      <%= multiple_selects f, :permissions, Permission.where(:resource_type => f.object.resource_type),
+                           f.object.permissions.map(&:id), :label => _("Permission") %>
+
+      <%= hidden_field_tag :role_id, @role.try(:id) %>
+
+
+      <div class="controls" id="granular_warning" style="<%= f.object.granular? ? 'display:none' : '' %>">
+          <span class="help-block">
+            <strong><%= _("Selected resource type does not support granular filtering, therefore you can't configure granularity") %></strong>
+          </span>
+      </div>
+
+      <div id="granular_form" style="<%= !f.object.granular? ? 'display:none' : '' %>">
+        <%= checkbox_f f, :unlimited?,
+                       :help_inline => _("By unchecking this you can specify filter using Foreman search syntax in search field. If filter remains unlimited (this field is checked) it applies on all resources of the selected type") %>
+
+        <%= autocomplete_f f, :search,
+                           :disabled => f.object.unlimited?,
+                           :path => resource_path(f.object.resource_type), :control_group_id => 'search_group' %>
+      </div>
+    </div>
+
+    <% if show_location_tab? %>
+      <div class="tab-pane" id="locations">
+        <% collection = User.current.locations.empty? ? Location : User.current.locations %>
+        <%= multiple_checkboxes f, :locations, f.object, collection, :label => _("Locations") %>
+      </div>
+    <% end %>
+
+    <% if show_organization_tab? %>
+      <div class="tab-pane" id="organizations">
+        <% collection = User.current.organizations.empty? ? Organization : User.current.organizations %>
+        <%= multiple_checkboxes f, :organizations, f.object, collection, {},
+                                {:label => _("Organizations")} %>
+      </div>
+    <% end %>
+  </div>
+
+  <%= submit_or_cancel f, false, :cancel_path => :back %>
+<% end %>
diff --git a/app/views/filters/edit.html.erb b/app/views/filters/edit.html.erb
new file mode 100644
index 000000000..2188e33ad
--- /dev/null
+++ b/app/views/filters/edit.html.erb
@@ -0,0 +1,3 @@
+<% title _("Edit Filter") %>
+
+<%= render 'form' %>
diff --git a/app/views/filters/index.html.erb b/app/views/filters/index.html.erb
new file mode 100644
index 000000000..70fd12549
--- /dev/null
+++ b/app/views/filters/index.html.erb
@@ -0,0 +1,48 @@
+<% title _("Filters") %>
+
+<% title_actions link_to_if_authorized(_("New filter"), hash_for_new_filter_path(:role_id => @role)) %>
+
+<table class="table table-bordered table-striped">
+  <thead>
+  <tr>
+    <% unless params[:role_id] %>
+      <th><%= sort :role, :as => _("Role") %></th>
+    <% end %>
+    <th><%= sort :resource, :as => s_("Filter|Resource") %></th>
+    <th><%= sort :search, :as => s_("Filter|Unlimited") %></th>
+    <th><%= sort :search, :as => s_("Filter|Search") %></th>
+    <th></th>
+  </tr>
+  </thead>
+  <tbody>
+  <% @filters.each do |filter| %>
+    <tr>
+      <% unless params[:role_id] %>
+        <td>
+          <%= content_tag('span', link_to_if_authorized(filter.role.name, hash_for_roles_path(:search => "name = #{filter.role.name}").
+                  merge(:auth_object => filter.role, :authorizer => @roles_authorizer))) %>
+        </td>
+      <% end %>
+      <td><%= _(filter.resource_class.try(:humanize_class_name) || filter.resource_type || '(Miscellaneous)') %></td>
+      <td><%= checked_icon filter.unlimited? %></td>
+      <td>
+        <%= content_tag('span', link_to_if_authorized(filter.search || _('none'),
+                                                      hash_for_edit_filter_path(:id => filter, :role_id => @role).
+                                                              merge(:auth_object => filter, :authorizer => authorizer))) %>
+      </td>
+      <td>
+        <%= action_buttons(
+                    display_link_if_authorized(_("Edit"), hash_for_edit_filter_path(:id => filter, :role_id => @role).
+                            merge(:auth_object => filter, :authorizer => authorizer)),
+                    display_delete_if_authorized(hash_for_filter_path(:id => filter, :role_id => @role).
+                                                         merge(:auth_object => filter, :authorizer => authorizer),
+                                                 :confirm => (_("Delete filter?")))
+            )
+        %>
+      </td>
+    </tr>
+  <% end %>
+  </tbody>
+</table>
+<%= page_entries_info @filters %>
+<%= will_paginate @filters %>
diff --git a/app/views/filters/new.html.erb b/app/views/filters/new.html.erb
new file mode 100644
index 000000000..f814bdfb5
--- /dev/null
+++ b/app/views/filters/new.html.erb
@@ -0,0 +1,3 @@
+<% title _("New Filter") %>
+
+<%= render 'form' %>
diff --git a/app/views/hostgroups/index.html.erb b/app/views/hostgroups/index.html.erb
index f72b14846..0f7761eeb 100644
--- a/app/views/hostgroups/index.html.erb
+++ b/app/views/hostgroups/index.html.erb
@@ -9,12 +9,12 @@
   </tr>
   <% @hostgroups.each do |hostgroup| %>
     <tr>
-      <td> <%= label_with_link(hostgroup) %> </td>
+      <td> <%= label_with_link(hostgroup, 1000, authorizer) %> </td>
       <td>
         <%= action_buttons(
                     display_link_if_authorized(_('Nest'),    hash_for_nest_hostgroup_path(:id => hostgroup)),
                     display_link_if_authorized(_('Clone'),   hash_for_clone_hostgroup_path(:id => hostgroup)),
-                    display_delete_if_authorized(hash_for_hostgroup_path(:id => hostgroup), :confirm => warning_message(hostgroup))) %>
+                    display_delete_if_authorized(hash_for_hostgroup_path(:id => hostgroup).merge(:auth_object => hostgroup, :authorizer => authorizer), :confirm => warning_message(hostgroup))) %>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/hosts/_form.html.erb b/app/views/hosts/_form.html.erb
index dafe3ad34..60af4466f 100644
--- a/app/views/hosts/_form.html.erb
+++ b/app/views/hosts/_form.html.erb
@@ -15,7 +15,7 @@
       <% if SETTINGS[:unattended] and @host.managed %>
         <li><a href="#network" data-toggle="tab"><%= _('Network') %></a></li>
         <li><a href="#os" data-toggle="tab"><%= _('Operating System') %></a></li>
-        <% if authorized_for("Compute::Resources::Vms", :create) %>
+        <% if authorized_for(:controller => "Compute::Resources::Vms", :action => :create) %>
           <li id="compute_resource_tab" <%= display? !(@host.compute_resource_id  || params[:host] && params[:host][:compute_resource_id].present?)%>><a href="#compute_resource" data-toggle="tab"><%= _('Virtual Machine') %></a></li>
         <% end %>
       <% end %>
@@ -30,7 +30,7 @@
 
         <% if show_organization_tab?  %>
           <%= select_f f, :organization_id, Organization.my_organizations, :id, :to_label,
-            { :include_blank => !@host.managed?, :selected => @organization.id},
+            { :include_blank => !@host.managed?, :selected => @organization.try(:id)},
             { :disabled => !@host.new_record?,
               :onchange => 'organization_changed(this);', :label => _("Organization"), :'data-host-id' => @host.id,
             :'data-url' => process_taxonomy_hosts_path,
@@ -39,7 +39,7 @@
 
         <% if show_location_tab? %>
           <%= select_f f, :location_id, Location.my_locations, :id, :to_label,
-            { :include_blank => !@host.managed?, :selected => @location.id },
+            { :include_blank => !@host.managed?, :selected => @location.try(:id) },
             { :disabled => !@host.new_record?,
               :onchange => 'location_changed(this);', :label => _("Location"), :"data-host-id" => @host.id,
               :'data-url' => process_taxonomy_hosts_path, :selected => Location.my_locations,
@@ -52,7 +52,7 @@
             :'data-url' => (@host.new_record? || @host.type_changed?) ? process_hostgroup_hosts_path : hostgroup_or_environment_selected_hosts_path,
             :help_inline => :indicator } %>
 
-        <%= select_f f, :compute_resource_id, ComputeResource.my_compute_resources.with_taxonomy_scope_override(@location,@organization), :id, :to_label,
+        <%= select_f f, :compute_resource_id, ComputeResource.with_taxonomy_scope_override(@location,@organization).authorized(:view_compute_resources), :id, :to_label,
           { :include_blank => _('Bare Metal') },
           {:label => _('Deploy on'), :disabled => !@host.new_record?, :'data-url' => compute_resource_selected_hosts_path,
             :onchange => 'computeResourceSelected(this);',
diff --git a/app/views/hosts/_list.html.erb b/app/views/hosts/_list.html.erb
index c63b53f5f..09a6e2fe4 100644
--- a/app/views/hosts/_list.html.erb
+++ b/app/views/hosts/_list.html.erb
@@ -20,13 +20,13 @@
       <td class="hidden-xs"><%= (icon(host.os, :size => "18x18") + trunc(" #{host.os.to_label}",14)).html_safe if host.os %></td>
       <td class="hidden-xs"><%= trunc(host.try(:environment), 14) %></td>
       <td class="hidden-tablet hidden-xs ellipsis"><%= model_name host %></td>
-      <td class="hidden-tablet hidden-xs ellipsis"><%= label_with_link host.hostgroup, 26 %></td>
+      <td class="hidden-tablet hidden-xs ellipsis"><%= label_with_link host.hostgroup, 26, @hostgroup_authorizer %></td>
       <td class="hidden-tablet hidden-xs"><%= last_report_column(host) %></td>
       <td>
         <%= action_buttons(
-                    display_link_if_authorized(_("Edit"), hash_for_edit_host_path(:id => host)),
-                    display_link_if_authorized(_("Clone"), hash_for_clone_host_path(:id => host)),
-                    display_delete_if_authorized(hash_for_host_path(:id => host), :confirm => _("Delete %s?") % host.name, :action => :destroy))%>
+                    display_link_if_authorized(_("Edit"), hash_for_edit_host_path(:id => host).merge(:auth_object => host, :authorizer => authorizer)),
+                    display_link_if_authorized(_("Clone"), hash_for_clone_host_path(:id => host).merge(:auth_object => host, :authorizer => authorizer)),
+                    display_delete_if_authorized(hash_for_host_path(:id => host).merge(:auth_object => host, :authorizer => authorizer), :confirm => _("Delete %s?") % host.name, :action => :destroy))%>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/hosts/_provisioning.html.erb b/app/views/hosts/_provisioning.html.erb
index f53bb5364..93de4ae22 100644
--- a/app/views/hosts/_provisioning.html.erb
+++ b/app/views/hosts/_provisioning.html.erb
@@ -3,7 +3,7 @@
   <% templates.sort{|t,x| t.template_kind <=> x.template_kind}.each do |tmplt| %>
       <div class="form-group">
         <div class="control-label col-md-4"> <%= _("%s Template") % tmplt.template_kind %> </div>
-        <%= link_to_if_authorized h(tmplt), hash_for_edit_config_template_path(:id => tmplt.to_param), :rel=>"external" %>
+        <%= link_to_if_authorized h(tmplt), hash_for_edit_config_template_path(:id => tmplt.to_param, :auth_object => tmplt, :permission => 'edit_templates'), :rel=>"external" %>
       </div>
   <% end %>
 </div>
diff --git a/app/views/hosts/edit.html.erb b/app/views/hosts/edit.html.erb
index a1dcd5927..3e4277a6f 100644
--- a/app/views/hosts/edit.html.erb
+++ b/app/views/hosts/edit.html.erb
@@ -1,9 +1,9 @@
 <% title(_("Edit %s") % @host) %>
 <% if SETTINGS[:unattended] %>
   <% title_actions display_link_if_authorized((@host.managed? ? _("Unmanage host") : _("Manage host")),
-    hash_for_toggle_manage_host_path(:id => @host.name), {:method => :put}),
+    hash_for_toggle_manage_host_path(:id => @host.name).merge(:auth_object => @host, :permission => 'edit_hosts'), {:method => :put}),
     display_link_if_authorized(_("Disassociate host"),
-    hash_for_disassociate_host_path(:id => @host.name), :method => :put) %>
+    hash_for_disassociate_host_path(:id => @host.name).merge(:auth_object => @host, :permission => 'edit_hosts'), :method => :put) %>
 <% end %>
 
 <%= render :partial => 'hosts/form' %>
diff --git a/app/views/lookup_keys/index.html.erb b/app/views/lookup_keys/index.html.erb
index 949762588..6f3917719 100644
--- a/app/views/lookup_keys/index.html.erb
+++ b/app/views/lookup_keys/index.html.erb
@@ -11,11 +11,12 @@
 
 <% @lookup_keys.each do |lookup_key| %>
   <tr>
-    <td><%= link_to_if_authorized h(lookup_key.key), hash_for_edit_lookup_key_path(:id => lookup_key) %></td>
+    <td><%= link_to_if_authorized h(lookup_key.key), hash_for_edit_lookup_key_path(:id => lookup_key).merge(:auth_object => lookup_key, :permission => 'edit_external_variables', :authorizer => authorizer) %></td>
     <% puppet_class = lookup_key.param_class %>
-    <td><%= link_to_if_authorized h(puppet_class), hash_for_edit_puppetclass_path(:id => puppet_class) if puppet_class %></td>
+    <td><%= link_to_if_authorized h(puppet_class), hash_for_edit_puppetclass_path(:id => puppet_class).merge(:auth_object => puppet_class, :authorizer => @puppetclass_authorizer) if puppet_class %></td>
     <td><%=h lookup_key.lookup_values.count %></td>
-    <td><%= display_delete_if_authorized hash_for_lookup_key_path(:id => lookup_key), :confirm => _("Delete %s?") % lookup_key.key %></td>
+    <td><%= display_delete_if_authorized hash_for_lookup_key_path(:id => lookup_key).merge(:auth_object => lookup_key, :permission => 'destroy_external_variables', :authorizer => authorizer),
+                                         :confirm => _("Delete %s?") % lookup_key.key %></td>
   </tr>
 <% end %>
 </table>
diff --git a/app/views/media/index.html.erb b/app/views/media/index.html.erb
index b831f6500..54f2aa41c 100644
--- a/app/views/media/index.html.erb
+++ b/app/views/media/index.html.erb
@@ -12,12 +12,13 @@
   </tr>
   <% for medium in @media %>
     <tr>
-      <td><%= link_to_if_authorized medium, hash_for_edit_medium_path(:id => medium.id) %></td>
+      <td><%= link_to_if_authorized medium, hash_for_edit_medium_path(:id => medium.id).merge(:auth_object => medium, :authorizer => authorizer) %></td>
       <td><%= medium.path %></td>
       <td><%= lookup_family(medium.os_family) %></td>
       <td><%= medium.operatingsystems.map(&:to_label).to_sentence %></td>
       <td>
-        <%= display_delete_if_authorized hash_for_medium_path(:id => medium), :confirm => _("Delete %s?") % medium.name %>
+        <%= display_delete_if_authorized hash_for_medium_path(:id => medium).merge(:auth_object => medium, :authorizer => authorizer),
+                                         :confirm => _("Delete %s?") % medium.name %>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/models/index.html.erb b/app/views/models/index.html.erb
index 4d889023f..3da369cb3 100644
--- a/app/views/models/index.html.erb
+++ b/app/views/models/index.html.erb
@@ -12,12 +12,13 @@
   </tr>
   <% for model in @models %>
     <tr>
-      <td><%=link_to_if_authorized h(model.name), hash_for_edit_model_path(:id => model)%></td>
+      <td><%=link_to_if_authorized h(model.name), hash_for_edit_model_path(:id => model).merge(:auth_object => model, :authorizer => authorizer) %></td>
       <td><%=h(model.vendor_class)%></td>
       <td><%=h(model.hardware_model)%></td>
       <td class="ra"><%= link_to @host_counter[model.id] || 0, hosts_path(:search => "model = \"#{model}\"") %></td>
       <td class="ra">
-        <%= display_delete_if_authorized hash_for_model_path(:id => model), :confirm => _("Delete %s?") % model.name %>
+        <%= display_delete_if_authorized hash_for_model_path(:id => model).merge(:auth_object => model, :authorizer => authorizer),
+                                         :confirm => _("Delete %s?") % model.name %>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/operatingsystems/index.html.erb b/app/views/operatingsystems/index.html.erb
index b5ca2cb65..e76786534 100644
--- a/app/views/operatingsystems/index.html.erb
+++ b/app/views/operatingsystems/index.html.erb
@@ -9,10 +9,11 @@
   </tr>
   <% for operatingsystem in @operatingsystems %>
     <tr>
-      <td><%= link_to_if_authorized(os_name(operatingsystem), hash_for_edit_operatingsystem_path(:id => operatingsystem.id)) %></td>
+      <td><%= link_to_if_authorized(os_name(operatingsystem), hash_for_edit_operatingsystem_path(:id => operatingsystem.id).merge(:auth_object => operatingsystem, :authorizer => authorizer)) %></td>
       <td class="ra"><%= link_to @host_counter[operatingsystem.id] || 0, hosts_path(:search => "os = #{operatingsystem.name}") %></td>
       <td class="ra">
-        <%= display_delete_if_authorized hash_for_operatingsystem_path(:id => operatingsystem), :confirm => _("Delete %s?") % operatingsystem.fullname %>
+        <%= display_delete_if_authorized hash_for_operatingsystem_path(:id => operatingsystem).merge(:auth_object => operatingsystem, :authorizer => authorizer),
+                                         :confirm => _("Delete %s?") % operatingsystem.fullname %>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/permissions/index.js.erb b/app/views/permissions/index.js.erb
new file mode 100644
index 000000000..6ebf4a74c
--- /dev/null
+++ b/app/views/permissions/index.js.erb
@@ -0,0 +1,20 @@
+var permissions = $('#filter_permission_ids');
+permissions.html("<%= escape_javascript(options_for_select(@permissions.map {|p| [p.name, p.id] })) %>");
+permissions.multiSelect('refresh');
+
+var search = $('#search');
+var path = '<%= escape_javascript(@search_path) %>'
+search.attr('data-url', path);
+search.val('');
+search.catcomplete({source: function( request, response ) {
+    $.getJSON( search.attr('data-url'), { search: request.term }, response );
+}})
+
+<% if @granular %>
+  $('#granular_warning').hide();
+  $('#granular_form').show();
+<% else %>
+  $('#granular_warning').show();
+  $('#granular_form').hide();
+  $('#filter_unlimited').prop('checked', true);
+<% end %>
diff --git a/app/views/ptables/index.html.erb b/app/views/ptables/index.html.erb
index 2d87ba862..7df75bd8d 100644
--- a/app/views/ptables/index.html.erb
+++ b/app/views/ptables/index.html.erb
@@ -10,11 +10,11 @@
   </tr>
   <% for ptable in @ptables %>
     <tr>
-      <td><%= link_to_if_authorized ptable, hash_for_edit_ptable_path(:id => ptable.id) %></td>
+      <td><%= link_to_if_authorized ptable, hash_for_edit_ptable_path(:id => ptable.id).merge(:auth_object => ptable, :authorizer => authorizer) %></td>
       <td><%= lookup_family(ptable.os_family) %></td>
       <td><%= ptable.operatingsystems.map(&:to_label).to_sentence %></td>
       <td>
-        <%= display_delete_if_authorized hash_for_ptable_path(:id => ptable), :confirm => _("Delete %s?") % ptable.name%>
+        <%= display_delete_if_authorized hash_for_ptable_path(:id => ptable).merge(:auth_object => ptable, :authorizer => authorizer), :confirm => _("Delete %s?") % ptable.name%>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/puppetclasses/_class_selection.html.erb b/app/views/puppetclasses/_class_selection.html.erb
index 02149f7f0..173fdcefe 100644
--- a/app/views/puppetclasses/_class_selection.html.erb
+++ b/app/views/puppetclasses/_class_selection.html.erb
@@ -8,7 +8,7 @@
     <%# hidden field to ensure that classes gets removed if none are defined %>
     <%= hidden_field_tag obj.class.model_name.downcase + "[puppetclass_ids][]" %>
     <ul id="selected_classes">
-      <% if authorized_for(:host_editing, :edit_classes) %>
+      <% if authorized_for(:controller => :host_editing, :action => :edit_classes) %>
         <%= render :partial => "puppetclasses/selectedClasses",
           :collection => obj.puppetclasses ,:as => :klass,
           :locals => { :type => obj.class.model_name.downcase, :host => obj } %>
diff --git a/app/views/puppetclasses/_classes.html.erb b/app/views/puppetclasses/_classes.html.erb
index 0cc36b880..d6ca1c9e9 100644
--- a/app/views/puppetclasses/_classes.html.erb
+++ b/app/views/puppetclasses/_classes.html.erb
@@ -6,7 +6,7 @@
         <li><%= link_to_function image_tag("bullet_toggle_plus.png") + " " + list.first, "$('#pc_#{list.first}').fadeToggle('slow')" %>
           <ul id="pc_<%= list.first %>" style="display: none;">
             <% for klass in list.last.sort %>
-              <% unless authorized_for(:host_editing, :edit_classes) %>
+              <% unless authorized_for(:controller => :host_editing, :action => :edit_classes) %>
                 <li data-original-title="<%= _('Not authorized to edit classes') %>" rel="twipsy"><%= h klass.name %></li>
               <% else %>
                 <% style = selected_puppet_classes.include?(klass) ? "hide" : "" %>
diff --git a/app/views/puppetclasses/index.html.erb b/app/views/puppetclasses/index.html.erb
index 0c56343b9..95273a381 100644
--- a/app/views/puppetclasses/index.html.erb
+++ b/app/views/puppetclasses/index.html.erb
@@ -13,17 +13,18 @@
   </tr>
   <% for puppetclass in @puppetclasses %>
     <tr>
-      <td><%=link_to_if_authorized h(puppetclass.name), hash_for_edit_puppetclass_path(:id => puppetclass) %></td>
+      <td><%=link_to_if_authorized h(puppetclass.name), hash_for_edit_puppetclass_path(:id => puppetclass).merge(:auth_object => puppetclass, :authorizer => authorizer) %></td>
       <td>
         <% puppetclass.environments.uniq.each do |environment| %>
           <%= link_to_function environment, 'show_rdoc(this)', :'data-url' => rdoc_classes_path(environment, puppetclass.name) %>
         <% end %>
       </td>
-      <td><%= puppetclass.hostgroups.map {|hg| link_to_if_authorized hg, hash_for_edit_hostgroup_path(:id=>hg)}.to_sentence.html_safe %></td>
+      <td><%= puppetclass.hostgroups.map {|hg| link_to_if_authorized hg, hash_for_edit_hostgroup_path(:id=>hg).merge(:auth_object => hg, :authorizer => @hostgroups_authorizer)}.to_sentence.html_safe %></td>
       <td> <%= link_to host_counter(puppetclass), hosts_path(:search => "class = #{puppetclass.name}")%></td>
       <td><%= @keys_counter[puppetclass.name] || 0 %> </td>
       <td>
-        <%= display_delete_if_authorized hash_for_puppetclass_path(:id => puppetclass), :confirm => _("Delete %s?") % puppetclass.name %>
+        <%= display_delete_if_authorized hash_for_puppetclass_path(:id => puppetclass).merge(:auth_object => puppetclass, :authorizer => authorizer),
+                                         :confirm => _("Delete %s?") % puppetclass.name %>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/reports/_list.html.erb b/app/views/reports/_list.html.erb
index 29166bbf1..151609f81 100644
--- a/app/views/reports/_list.html.erb
+++ b/app/views/reports/_list.html.erb
@@ -14,8 +14,12 @@
   </tr>
   <% for report in @reports %>
     <tr>
-      <% unless params[:host_id] %>
-        <td><%= link_to h(report.host), host_reports_path(report.host) %></td>
+      <% if params[:host_id].nil? %>
+        <% if report.host.nil? %>
+            <td></td>
+        <% else %>
+            <td><%= link_to report.host, host_reports_path(report.host) %></td>
+        <% end %>
       <% end %>
       <td><%= reported_at_column(report) %></td>
       <td><%= report_event_column(report.applied, "label-info") %></td>
@@ -25,7 +29,8 @@
       <td><%= report_event_column(report.skipped, "label-info") %></td>
       <td><%= report_event_column(report.pending, "label-info") %></td>
       <td align="right">
-        <%= display_delete_if_authorized hash_for_report_path(:id => report.id), :confirm => _("Delete report for %s?") % report.host.name %>
+        <%= display_delete_if_authorized hash_for_report_path(:id => report.id).merge(:auth_object => report, :authorizer => authorizer),
+                                         :confirm => _("Delete report for %s?") % report.host.try(:name) %>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/roles/_form.html.erb b/app/views/roles/_form.html.erb
index bf3eb1214..947232ead 100644
--- a/app/views/roles/_form.html.erb
+++ b/app/views/roles/_form.html.erb
@@ -1,39 +1,8 @@
-<% title_actions check_all_roles_links %>
-
 <%= form_for @role do |f| %>
   <%= base_errors_for @role %>
 
   <%= text_f f, :name, :class => @role.builtin? ? "disabled" : ""  %>
+  <%= hidden_field_tag :original_role_id, @original_role_id if @cloned_role %>
 
-  <div class='form-group'>
-    <label class='control-label'><%= _('Permissions') %></label>
-    <input placeholder="<%= _('Filter permissions') %>" class="search-query" onkeyup="filter_permissions(this);" type='text'>
-  </div>
-
-  <% perms_by_block = @role.setable_permissions.group_by {|p| p.security_block.to_s} %>
-  <div class='row'>
-  <% perms_by_block.keys.sort.in_groups(2,false) do |column| %>
-    <div class="col-md-4">
-      <% column.each do |block| %>
-        <div class="form-group">
-          <%= label_tag 'role[permissions]', image_tag("bullet_toggle_plus.png") + " " + block.humanize,:class=>"control-label", :data=>{:toggle=>'collapse', :target=>"##{block}"} %>
-          <div class="collapse" id="<%= block %>">
-            <ul class="inputs-list">
-              <% perms_by_block[block].each do |permission| %>
-                <li>
-                  <%= content_tag :div, 'role[permissions][]' do %>
-                    <%= check_box_tag 'role[permissions][]', permission.name, (@role.permissions.include? permission.name), :class => "role_checkbox" %>
-                    <%= permission.name %>
-                    <%= hidden_field_tag 'role[permissions][]' %>
-                  <% end %>
-                </li>
-              <% end %>
-            </ul>
-          </div>
-        </div>
-      <% end %>
-    </div>
-  <% end %>
-  </div>
   <%= submit_or_cancel f %>
 <% end %>
diff --git a/app/views/roles/index.html.erb b/app/views/roles/index.html.erb
index f5d121064..9210670ff 100644
--- a/app/views/roles/index.html.erb
+++ b/app/views/roles/index.html.erb
@@ -1,6 +1,5 @@
 <% title _("Roles") %>
-<% title_actions link_to(_("New role"), new_role_path),
-   link_to_if_authorized(_("Permissions report"), :action => 'report') %>
+<% title_actions link_to_if_authorized(_("New role"), hash_for_new_role_path) %>
 
 <table class="table table-bordered table-striped table-two-pane">
   <thead>
@@ -13,12 +12,14 @@
     <% for role in @roles %>
       <tr>
         <td>
-          <%= content_tag(role.builtin? ? 'em' : 'span', link_to_if_authorized(role.name, :action => 'edit', :id => role)) %>
+          <%= content_tag(role.builtin? ? 'em' : 'span', role.builtin? ? role.name : link_to_if_authorized(role.name, hash_for_edit_role_path(:id => role))) %>
         </td>
-        <td class="buttons">
-          <%= display_delete_if_authorized(hash_for_role_path(:id => role),
-                                    :confirm => (_("Delete %s?") % role.name)) unless role.builtin?
-          %>
+        <td>
+          <% links = [display_link_if_authorized(_('Filters and permissions'), hash_for_filters_path(:role_id => role)),
+                      display_link_if_authorized(_('Add permission'), hash_for_new_filter_path(:role_id => role)),
+                      display_link_if_authorized(_('Clone'), hash_for_clone_role_path(:id => role))] %>
+          <% links.push(display_delete_if_authorized(hash_for_role_path(:id => role), :confirm => (_("Delete %s?") % role.name))) unless role.builtin? %>
+          <%= action_buttons(*links) %>
         </td>
       </tr>
     <% end %>
diff --git a/app/views/roles/report.html.erb b/app/views/roles/report.html.erb
deleted file mode 100644
index dd19f9c9a..000000000
--- a/app/views/roles/report.html.erb
+++ /dev/null
@@ -1,52 +0,0 @@
-<% title _("Permissions report") %>
-
-<%= form_tag(report_roles_path, :id => 'permissions_form') do %>
-  <%= hidden_field_tag 'permissions[0]', '', :id => nil %>
-  <table class="table table-bordered table-striped">
-    <thead>
-      <tr>
-        <th><%= _("Permissions") %></th>
-        <% @roles.each do |role| %>
-          <th>
-            <%= content_tag(role.builtin? ? 'em' : 'span', h(role.name)) %>
-            <%= link_to_function(image_tag('toggle_check.png'), "toggleCheckboxesBySelector('input.role-#{role.id}')",
-                                 :title => _("Check all/Uncheck all"))
-            %>
-          </th>
-        <% end %>
-      </tr>
-    </thead>
-    <tbody>
-      <% perms_by_block = @permissions.group_by {|p| p.security_block.to_s} %>
-      <% perms_by_block.keys.sort.each do |block| %>
-        <% unless block.blank? %>
-          <tr class="group open">
-            <td colspan="<%= @roles.size + 1 %>">
-              <span class="expander" onclick="toggleRowGroup(this); return false;">&nbsp;</span>
-              <%= block.humanize %>
-            </td>
-          </tr>
-        <% end %>
-        <% perms_by_block[block].each do |permission| %>
-          <tr class="<%= cycle('odd', 'even') %> permission-<%= permission.name %>">
-            <td>
-              <%= link_to_function(image_tag('toggle_check.png'), "toggleCheckboxesBySelector('.permission-#{permission.name} input')",
-                                   :title => _("Check all/Uncheck all"))
-              %>
-              <%= permission.name.to_s.humanize %>
-            </td>
-            <% @roles.each do |role| %>
-              <td align="center">
-                <% if role.setable_permissions.include? permission %>
-                  <%= check_box_tag "permissions[#{role.id}][]", permission.name, (role.permissions.include? permission.name),
-                    :id => nil, :class => "role-#{role.id}" %>
-                <% end %>
-              </td>
-            <% end %>
-          </tr>
-        <% end %>
-      <% end %>
-    </tbody>
-  </table>
-  <p><%= submit_tag _("Save") %></p>
-<% end %>
diff --git a/app/views/smart_proxies/_form.html.erb b/app/views/smart_proxies/_form.html.erb
index 32a4bb0e6..24dafbd15 100644
--- a/app/views/smart_proxies/_form.html.erb
+++ b/app/views/smart_proxies/_form.html.erb
@@ -1,5 +1,5 @@
-<%= form_for @proxy do |f| %>
-  <%= base_errors_for @proxy %>
+<%= form_for @smart_proxy do |f| %>
+  <%= base_errors_for @smart_proxy %>
   <ul class="nav nav-tabs" data-tabs="tabs">
     <li class="active"><a href="#primary" data-toggle="tab"><%= _("Smart Proxy") %></a></li>
     <% if show_location_tab? %>
@@ -16,8 +16,8 @@
       <%= text_f(f, :url, :size => "col-md-8" ) %>
     </div>
 
-    <%= render 'taxonomies/loc_org_tabs', :f => f, :obj => @proxy %>
+    <%= render 'taxonomies/loc_org_tabs', :f => f, :obj => @smart_proxy %>
 
     <%= submit_or_cancel f %>
   </div>
-<% end %>
\ No newline at end of file
+<% end %>
diff --git a/app/views/smart_proxies/index.html.erb b/app/views/smart_proxies/index.html.erb
index 69fd8e2e6..9dfaf4fd5 100644
--- a/app/views/smart_proxies/index.html.erb
+++ b/app/views/smart_proxies/index.html.erb
@@ -10,29 +10,29 @@
     <th><%= _("Features") %></th>
     <th></th>
   </tr>
-  <% for proxy in @proxies %>
+  <% for proxy in @smart_proxies %>
     <tr>
-      <td><%= link_to_if_authorized proxy.name, hash_for_edit_smart_proxy_path(:id => proxy.id) %></td>
+      <td><%= link_to_if_authorized proxy.name, hash_for_edit_smart_proxy_path(:id => proxy.id).merge(:auth_object => proxy, :authorizer => authorizer) %></td>
       <td><%=h proxy.url %></td>
       <td><%=h proxy.features.to_sentence %></td>
       <td>
         <% ca = proxy.features.detect{|f| f.name == "Puppet CA"} %>
         <%= action_buttons(
           if ca
-            display_link_if_authorized(_("Certificates"), hash_for_smart_proxy_puppetca_index_path(:smart_proxy_id => proxy))
+            display_link_if_authorized(_("Certificates"), hash_for_smart_proxy_puppetca_index_path(:smart_proxy_id => proxy).merge(:auth_object => proxy, :permission => 'view_smart_proxies_puppetca', :authorizer => authorizer))
           end,
           if ca
-            display_link_if_authorized(_("Autosign"), hash_for_smart_proxy_autosign_index_path(:smart_proxy_id => proxy))
+            display_link_if_authorized(_("Autosign"), hash_for_smart_proxy_autosign_index_path(:smart_proxy_id => proxy).merge(:auth_object => proxy, :permission => 'view_smart_proxies_autosign', :authorizer => authorizer))
           end,
           if SETTINGS[:unattended] and proxy.features.detect{|f| f.name == "DHCP" }
             display_link_if_authorized(_("Import subnets"), hash_for_import_subnets_path(:smart_proxy_id => proxy))
           end,
-          display_link_if_authorized(_("Refresh features"), hash_for_refresh_smart_proxy_path(:id => proxy), :method => :put),
-          display_delete_if_authorized(hash_for_smart_proxy_path(:id => proxy), :confirm => _("Delete %s?") % proxy.name))%>
+          display_link_if_authorized(_("Refresh features"), hash_for_refresh_smart_proxy_path(:id => proxy).merge(:auth_object => proxy, :permission => 'edit_smart_proxies', :authorizer => authorizer), :method => :put),
+          display_delete_if_authorized(hash_for_smart_proxy_path(:id => proxy).merge(:auth_object => proxy, :authorizer => authorizer), :confirm => _("Delete %s?") % proxy.name))%>
       </td>
     </tr>
   <% end %>
 </table>
 
-<%= page_entries_info @proxies %>
-<%= will_paginate @proxies %>
+<%= page_entries_info @smart_proxies %>
+<%= will_paginate @smart_proxies %>
diff --git a/app/views/subnets/index.html.erb b/app/views/subnets/index.html.erb
index fe9fc37dd..d9a5d8d4e 100644
--- a/app/views/subnets/index.html.erb
+++ b/app/views/subnets/index.html.erb
@@ -13,13 +13,13 @@
   </tr>
   <% for subnet in @subnets %>
     <tr>
-      <td><%=link_to_if_authorized h(subnet.name), hash_for_edit_subnet_path(:id => subnet)%></td>
+      <td><%=link_to_if_authorized h(subnet.name), hash_for_edit_subnet_path(:id => subnet).merge(:auth_object => subnet, :authorizer => authorizer) %></td>
       <td><%=subnet.network_address %></td>
       <td><%=subnet.domains.to_sentence %></td>
       <td><%=subnet.vlanid %></td>
       <td><%=subnet.dhcp %></td>
       <td align="right">
-        <%= display_delete_if_authorized hash_for_subnet_path(:id => subnet), :confirm => _("Delete %s?") % subnet.name %>
+        <%= display_delete_if_authorized hash_for_subnet_path(:id => subnet).merge(:auth_object => subnet, :authorizer => authorizer), :confirm => _("Delete %s?") % subnet.name %>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/usergroups/_form.html.erb b/app/views/usergroups/_form.html.erb
index 8d0779401..ac367600b 100644
--- a/app/views/usergroups/_form.html.erb
+++ b/app/views/usergroups/_form.html.erb
@@ -1,7 +1,24 @@
 <%= form_for @usergroup do |f| %>
   <%= base_errors_for @usergroup %>
-  <%= text_f f, :name %>
-  <%= multiple_checkboxes f, :usergroups, @usergroup, Usergroup, :label => _("User Groups") %>
-  <%= multiple_checkboxes f, :users, @usergroup, User, :label => _("Users") %>
+
+  <ul class="nav nav-tabs" data-tabs="tabs">
+    <li class="active"><a href="#primary" data-toggle="tab"><%= _("Usergroup") %></a></li>
+    <li><a href="#roles" data-toggle="tab"><%= _("Roles") %></a></li>
+  </ul>
+
+  <div class="tab-content">
+    <div class="tab-pane active" id="primary">
+
+      <%= text_f f, :name %>
+      <%= multiple_checkboxes f, :usergroups, @usergroup, Usergroup, :label => _("User Groups") %>
+      <%= multiple_checkboxes f, :users, @usergroup, User, :label => _("Users") %>
+    </div>
+
+    <div class="tab-pane" id="roles">
+      <%= checkbox_f f, :admin if User.current.can_change_admin_flag? %>
+      <%= multiple_checkboxes f, :roles, @usergroup, Role.givable.for_current_user, {:label => _('Roles')} %>
+    </div>
+  </div>
+
   <%= submit_or_cancel f %>
 <% end %>
diff --git a/app/views/usergroups/index.html.erb b/app/views/usergroups/index.html.erb
index fe1736554..6803fb986 100644
--- a/app/views/usergroups/index.html.erb
+++ b/app/views/usergroups/index.html.erb
@@ -11,11 +11,12 @@
   </tr>
   <% for usergroup in @usergroups %>
     <tr>
-      <td><%= link_to_if_authorized h(usergroup.name), hash_for_edit_usergroup_path(:id => usergroup.id) %></td>
+      <td><%= link_to_if_authorized h(usergroup.name), hash_for_edit_usergroup_path(:id => usergroup.id).merge(:auth_object => usergroup, :authorizer => authorizer) %></td>
       <td><%= h usergroup.users.map(&:login).to_sentence %></td>
       <td><%= h usergroup.usergroups.map(&:name).to_sentence %></td>
       <td>
-        <%= display_delete_if_authorized hash_for_usergroup_path(:id => usergroup), :confirm => (_("Delete %s?") % usergroup.name) %>
+        <%= display_delete_if_authorized hash_for_usergroup_path(:id => usergroup).merge(:auth_object => usergroup, :authorizer => authorizer),
+                                         :confirm => (_("Delete %s?") % usergroup.name) %>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/users/_filters.html.erb b/app/views/users/_filters.html.erb
index 49cb78366..e289a0bfc 100644
--- a/app/views/users/_filters.html.erb
+++ b/app/views/users/_filters.html.erb
@@ -37,7 +37,7 @@
       <%= f.fields_for :user_facts do |builder| %>
         <%= render "user_fact", :f => builder %>
       <% end %>
-      <% if authorized_for(params[:controller], params[:action]) %>
+      <% if authorized_for(:controller => params[:controller], :action => params[:action]) %>
         <%= link_to_add_fields('+ ' + _("Add Filter"), f, :user_facts, "user_fact", :title => _("Add a fact filter")) %>
       <% end %>
     </div>
diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb
index 717a525c3..237ce6440 100644
--- a/app/views/users/_form.html.erb
+++ b/app/views/users/_form.html.erb
@@ -5,7 +5,6 @@
     <li class="active"><a href="#primary" data-toggle="tab"><%= _("User") %></a></li>
     <% unless @editing_self %>
       <li><a href="#roles" data-toggle="tab"><%= _("Roles") %></a></li>
-      <li><a href="#filters" data-toggle="tab"><%= _("Filters") %></a></li>
       <% if show_location_tab? %>
         <li><a href="#locations" data-toggle="tab"><%= _("Locations") %></a></li>
       <% end %>
@@ -38,23 +37,14 @@
         <%= multiple_checkboxes f, :roles, @user, Role.givable.for_current_user, {:label => _('Roles')} %>
       </div>
 
-      <div class="tab-pane" id="filters">
-        <%= render("filters", :f => f) %>
-      </div>
       <% if show_location_tab? %>
         <div class="tab-pane" id="locations">
-          <div class="col-md-2">
-            <%= f.select :locations_andor, [[_("must be"), "and"], [_("plus all"), "or"]], {}, :class => "form-control" %>
-          </div>
           <%= location_selects f, @user.used_or_selected_location_ids, {:disabled => @user.used_location_ids, :label => ''} %>
         </div>
       <% end %>
 
       <% if show_organization_tab? %>
         <div class="tab-pane" id="organizations">
-          <div class="col-md-2">
-            <%= f.select :organizations_andor, [[_("must be"), "and"], [_("plus all"), "or"]], {}, :class => "form-control" %>
-          </div>
           <%= organization_selects f, @user.used_or_selected_organization_ids, {:disabled => @user.used_organization_ids, :label => ''} %>
         </div>
       <% end %>
diff --git a/app/views/users/_user_fact.html.erb b/app/views/users/_user_fact.html.erb
index 7d14b12a2..007b540cb 100644
--- a/app/views/users/_user_fact.html.erb
+++ b/app/views/users/_user_fact.html.erb
@@ -9,6 +9,6 @@
     <div class="col-sm-5">
       <%= f.text_field :criteria, :class=>"form-control" %>
     </div>
-    <%= authorized_for(params[:controller], params[:action]) ? link_to_remove_fields('', f, :class=>'btn') : '' %>
+    <%= authorized_for(:controller => params[:controller], :action => params[:action]) ? link_to_remove_fields('', f, :class=>'btn') : '' %>
   </div>
 </div>
diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb
index 891e17993..0165bc1b4 100644
--- a/app/views/users/index.html.erb
+++ b/app/views/users/index.html.erb
@@ -15,14 +15,15 @@
   </tr>
   <% for user in @users %>
     <tr>
-      <td><%= gravatar_image_tag user.mail, :class => "gravatar" %> <%=link_to_if_authorized h(user.login), hash_for_edit_user_path(:id => user.id) %></td>
+      <td><%= gravatar_image_tag user.mail, :class => "gravatar" %> <%=link_to_if_authorized h(user.login), hash_for_edit_user_path(:id => user.id).merge(:auth_object => user, :authorizer => authorizer) %></td>
       <td><%=h user.firstname %></td>
       <td><%=h user.lastname %></td>
       <td><%=h user.mail %></td>
       <td><%=checked_icon user.admin? %></td>
       <td><%=h last_login_on_column(user)%></td>
       <td><%=h auth_source_column(user)%></td>
-      <td><%= display_delete_if_authorized hash_for_user_path(:id => user), :confirm => _("Delete %s?") % user.name %></td>
+      <td><%= display_delete_if_authorized hash_for_user_path(:id => user).merge(:auth_object => user, :authorizer => authorizer),
+                                           :confirm => _("Delete %s?") % user.name %></td>
     </tr>
   <% end %>
 </table>
diff --git a/bundler.d/test.rb b/bundler.d/test.rb
index 5839939ab..37daa6825 100644
--- a/bundler.d/test.rb
+++ b/bundler.d/test.rb
@@ -16,4 +16,5 @@ group :test do
   gem 'launchy'
   gem 'spork'
   gem 'factory_girl_rails', '~> 1.2', :require => false
+  gem 'oj'
 end
diff --git a/config/application.rb b/config/application.rb
index e2c2a7f78..54e0694bd 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -37,6 +37,7 @@ SETTINGS[:gce] = defined?(::Fog) && defined?(::Google::APIClient::VERSION)
 SETTINGS[:openstack] = SETTINGS[:rackspace] = SETTINGS[:ec2] = !! defined?(::Fog)
 
 require File.expand_path('../../lib/timed_cached_store.rb', __FILE__)
+require File.expand_path('../../lib/foreman/exception', __FILE__)
 require File.expand_path('../../lib/core_extensions', __FILE__)
 
 Bundler.require(:jsonp) if SETTINGS[:support_jsonp]
diff --git a/config/initializers/fix_cache.rb b/config/initializers/fix_cache.rb
new file mode 100644
index 000000000..5121ac252
--- /dev/null
+++ b/config/initializers/fix_cache.rb
@@ -0,0 +1,12 @@
+# if fix db cache flag is enabled and we are not running rake task, we want to
+# recreate cache
+
+if (Setting.table_exists? rescue(false))
+  flag = Setting::General.find_or_initialize_by_name('fix_db_cache',
+                                                     :description   => 'Fix DB cache on next Foreman restart',
+                                                     :settings_type => 'boolean', :default => false)
+end
+
+if File.basename($0) != "rake" && flag.value
+  CacheManager.recache!
+end
diff --git a/config/initializers/foreman.rb b/config/initializers/foreman.rb
index 8655af1ea..902170237 100644
--- a/config/initializers/foreman.rb
+++ b/config/initializers/foreman.rb
@@ -1,5 +1,4 @@
 require 'foreman/access_permissions'
-require 'foreman/default_data/loader'
 require 'menu/loader'
 require 'foreman/renderer'
 require 'foreman/controller'
@@ -18,9 +17,6 @@ if (Setting.table_exists? rescue(false))
   Setting.descendants.each(&:load_defaults)
 end
 
-# We load the default settings for the roles if they are not already present
-Foreman::DefaultData::Loader.load(false)
-
 #load topbar
 Menu::Loader.load
 
diff --git a/config/routes.rb b/config/routes.rb
index 1d9d84dae..e489511ec 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -171,6 +171,9 @@ Foreman::Application.routes.draw do
       resources :puppetca, :only => [:index, :update, :destroy]
       resources :autosign, :only => [:index, :new, :create, :destroy]
     end
+    collection do
+      get 'auto_complete_search'
+    end
   end
 
   resources :fact_values, :only => [:index] do
@@ -187,7 +190,11 @@ Foreman::Application.routes.draw do
   end
 
   if SETTINGS[:login]
-    resources :usergroups, :except => [:show]
+    resources :usergroups, :except => [:show] do
+      collection do
+        get 'auto_complete_search'
+      end
+    end
     resources :users, :except => [:show] do
       collection do
         get 'login'
@@ -200,13 +207,22 @@ Foreman::Application.routes.draw do
       end
     end
     resources :roles, :except => [:show] do
+      member do
+        get 'clone'
+      end
+      collection do
+        get 'auto_complete_search'
+      end
+    end
+
+    resources :filters, :except => [:show] do
       collection do
-        get 'report'
-        post 'report'
         get 'auto_complete_search'
       end
     end
 
+    resources :permissions, :only => [:index]
+
     resources :auth_source_ldaps, :except => [:show]
   end
 
diff --git a/config/routes/api/v2.rb b/config/routes/api/v2.rb
index e566839d8..bf454d43a 100644
--- a/config/routes/api/v2.rb
+++ b/config/routes/api/v2.rb
@@ -102,7 +102,18 @@ Foreman::Application.routes.draw do
         get :last, :on => :collection
       end
 
-      resources :roles, :except => [:new, :edit]
+      resources :roles, :except => [:new, :edit] do
+        resources :filters, :except => [:new, :edit] do
+          (resources :locations, :only => [:index, :show]) if SETTINGS[:locations_enabled]
+          (resources :organizations, :only => [:index, :show]) if SETTINGS[:organizations_enabled]
+        end
+      end
+      resources :permissions, :only => [:index, :show]
+
+      resources :filters, :except => [:new, :edit] do
+        (resources :locations, :only => [:index, :show]) if SETTINGS[:locations_enabled]
+        (resources :organizations, :only => [:index, :show]) if SETTINGS[:organizations_enabled]
+      end
 
       resources :settings, :only => [:index, :show, :update]
 
@@ -210,6 +221,7 @@ Foreman::Application.routes.draw do
           resources :compute_resources, :only => [:index, :show]
           resources :media, :only => [:index, :show]
           resources :smart_proxies, :only => [:index, :show]
+          resources :filters, :only => [:index, :show]
 
           resources :parameters, :except => [:new, :edit] do
             collection do
@@ -228,6 +240,7 @@ Foreman::Application.routes.draw do
             resources :compute_resources, :only => [:index, :show]
             resources :media, :only => [:index, :show]
             resources :smart_proxies, :only => [:index, :show]
+            resources :filters, :only => [:index, :show]
           end
 
         end
@@ -246,6 +259,7 @@ Foreman::Application.routes.draw do
           resources :compute_resources, :only => [:index, :show]
           resources :media, :only => [:index, :show]
           resources :smart_proxies, :only => [:index, :show]
+          resources :filters, :only => [:index, :show]
 
           resources :parameters, :except => [:new, :edit] do
             collection do
@@ -264,6 +278,7 @@ Foreman::Application.routes.draw do
             resources :compute_resources, :only => [:index, :show]
             resources :media, :only => [:index, :show]
             resources :smart_proxies, :only => [:index, :show]
+            resources :filters, :only => [:index, :show]
           end
 
         end
diff --git a/db/migrate/20131114084718_extend_user_role.rb b/db/migrate/20131114084718_extend_user_role.rb
new file mode 100644
index 000000000..a34261486
--- /dev/null
+++ b/db/migrate/20131114084718_extend_user_role.rb
@@ -0,0 +1,23 @@
+class ExtendUserRole < ActiveRecord::Migration
+  def up
+    remove_foreign_key 'user_roles', :name => 'user_roles_user_id_fk'
+    add_column :user_roles, :owner_type, :string, :default => 'User', :null => false
+    rename_column :user_roles, :user_id, :owner_id
+
+    add_index :user_roles, :owner_type
+    add_index :user_roles, :owner_id
+    add_index :user_roles, [:owner_id, :owner_type]
+
+    change_column :user_roles, :owner_id, :integer, :null => false
+  end
+
+  def down
+    remove_index :user_roles, [:owner_id, :owner_type]
+    remove_index :user_roles, :owner_id
+    remove_index :user_roles, :owner_type
+
+    rename_column :user_roles, :owner_id, :user_id
+    remove_column :user_roles, :owner_type
+    add_foreign_key 'user_roles', 'users', :name => 'user_roles_user_id_fk'
+  end
+end
diff --git a/db/migrate/20131114094841_create_cached_user_roles.rb b/db/migrate/20131114094841_create_cached_user_roles.rb
new file mode 100644
index 000000000..7ad8c6e45
--- /dev/null
+++ b/db/migrate/20131114094841_create_cached_user_roles.rb
@@ -0,0 +1,15 @@
+class CreateCachedUserRoles < ActiveRecord::Migration
+  def change
+    create_table :cached_user_roles do |t|
+      t.integer :user_id, :null => false
+      t.integer :role_id, :null => false
+      t.integer :user_role_id, :null => false
+
+      t.timestamps
+    end
+
+    add_index :cached_user_roles, :user_id
+    add_index :cached_user_roles, :role_id
+    add_index :cached_user_roles, :user_role_id
+  end
+end
diff --git a/db/migrate/20131122093940_calculate_cache_for_user_role.rb b/db/migrate/20131122093940_calculate_cache_for_user_role.rb
new file mode 100644
index 000000000..03d5aafc4
--- /dev/null
+++ b/db/migrate/20131122093940_calculate_cache_for_user_role.rb
@@ -0,0 +1,10 @@
+class CalculateCacheForUserRole < ActiveRecord::Migration
+  def up
+    UserRole.all.each do |user_role|
+      user_role.role && user_role.owner ? user_role.save! : user_role.delete
+    end
+  end
+
+  def down
+  end
+end
diff --git a/db/migrate/20131122170726_create_cached_usergroup_members.rb b/db/migrate/20131122170726_create_cached_usergroup_members.rb
new file mode 100644
index 000000000..46c710196
--- /dev/null
+++ b/db/migrate/20131122170726_create_cached_usergroup_members.rb
@@ -0,0 +1,15 @@
+class CreateCachedUsergroupMembers < ActiveRecord::Migration
+  def change
+    create_table :cached_usergroup_members do |t|
+      t.integer :user_id
+      t.integer :usergroup_id
+
+      t.timestamps
+    end
+
+    add_index :cached_usergroup_members, :user_id
+    add_index :cached_usergroup_members, :usergroup_id
+
+    UsergroupMember.all.each(&:save!)
+  end
+end
diff --git a/db/migrate/20131128150357_add_admin_flag_to_usergroup.rb b/db/migrate/20131128150357_add_admin_flag_to_usergroup.rb
new file mode 100644
index 000000000..7158238b6
--- /dev/null
+++ b/db/migrate/20131128150357_add_admin_flag_to_usergroup.rb
@@ -0,0 +1,5 @@
+class AddAdminFlagToUsergroup < ActiveRecord::Migration
+  def change
+    add_column :usergroups, :admin, :boolean, :null => false, :default => false
+  end
+end
diff --git a/db/migrate/20131202120621_create_permissions.rb b/db/migrate/20131202120621_create_permissions.rb
new file mode 100644
index 000000000..4c17336ef
--- /dev/null
+++ b/db/migrate/20131202120621_create_permissions.rb
@@ -0,0 +1,12 @@
+class CreatePermissions < ActiveRecord::Migration
+  def change
+    create_table :permissions do |t|
+      t.string :name, :null => false
+      t.string :resource_type
+
+      t.timestamps
+    end
+
+    add_index :permissions, [:name, :resource_type]
+  end
+end
diff --git a/db/migrate/20131202131847_create_filters.rb b/db/migrate/20131202131847_create_filters.rb
new file mode 100644
index 000000000..5cc4e1956
--- /dev/null
+++ b/db/migrate/20131202131847_create_filters.rb
@@ -0,0 +1,10 @@
+class CreateFilters < ActiveRecord::Migration
+  def change
+    create_table :filters do |t|
+      t.text :search
+      t.integer :role_id
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/migrate/20131202144415_create_filterings.rb b/db/migrate/20131202144415_create_filterings.rb
new file mode 100644
index 000000000..9bcd61e98
--- /dev/null
+++ b/db/migrate/20131202144415_create_filterings.rb
@@ -0,0 +1,13 @@
+class CreateFilterings < ActiveRecord::Migration
+  def change
+    create_table :filterings do |t|
+      t.integer :filter_id
+      t.integer :permission_id
+
+      t.timestamps
+    end
+
+    add_index :filterings, :filter_id
+    add_index :filterings, :permission_id
+  end
+end
diff --git a/db/migrate/20140110164135_add_foreign_keys_to_filters_and_filterings.rb b/db/migrate/20140110164135_add_foreign_keys_to_filters_and_filterings.rb
new file mode 100644
index 000000000..64151e1f4
--- /dev/null
+++ b/db/migrate/20140110164135_add_foreign_keys_to_filters_and_filterings.rb
@@ -0,0 +1,13 @@
+class AddForeignKeysToFiltersAndFilterings < ActiveRecord::Migration
+  def self.up
+    add_foreign_key "filters", "roles", :name => "filters_roles_id_fk"
+    add_foreign_key "filterings", "filters", :name => "filterings_filters_id_fk"
+    add_foreign_key "filterings", "permissions", :name => "filterings_permissions_id_fk"
+  end
+
+  def self.down
+    remove_foreign_key "filters", :name => "filters_roles_id_fk"
+    remove_foreign_key "filterings", :name => "filterings_filters_id_fk"
+    remove_foreign_key "filterings", :name => "filterings_permissions_id_fk"
+  end
+end
diff --git a/db/migrate/20140219183343_migrate_permissions.rb b/db/migrate/20140219183343_migrate_permissions.rb
new file mode 100644
index 000000000..751fc7c58
--- /dev/null
+++ b/db/migrate/20140219183343_migrate_permissions.rb
@@ -0,0 +1,298 @@
+# we need permissions to be seeded already
+require Rails.root + 'db/seeds.d/20-permissions'
+
+# Fake models to make sure that this migration can be executed even when
+# original models changes later (e.g. add validation on columns that are not
+# present at this moment)
+class FakePermission < ActiveRecord::Base
+  set_table_name 'permissions'
+end
+
+class FakeFilter < ActiveRecord::Base
+  set_table_name 'filters'
+  # we need this for polymorphic relation to work, it has class name hardcoded in AR
+  def self.name
+    'Filter'
+  end
+  belongs_to :role, :class_name => 'FakeRole'
+  has_many :filterings, :dependent => :destroy, :foreign_key => 'filter_id'
+  has_many :permissions, :through => :filterings
+
+  def resource_type
+    @resource_type ||= permissions.first.try(:resource_type)
+  end
+
+  taxonomy_join_table = "taxable_taxonomies"
+  has_many taxonomy_join_table, :dependent => :destroy, :as => :taxable, :foreign_key => 'taxable_id'
+  has_many :locations,     :through => taxonomy_join_table, :source => :taxonomy,
+           :conditions => "taxonomies.type='Location'", :validate => false
+  has_many :organizations, :through => taxonomy_join_table, :source => :taxonomy,
+           :conditions => "taxonomies.type='Organization'", :validate => false
+
+end
+
+class FakeUserRole < ActiveRecord::Base
+  set_table_name 'user_roles'
+  belongs_to :owner, :polymorphic => true
+  belongs_to :role, :class_name => 'FakeRole'
+end
+
+class FakeRole < ActiveRecord::Base
+  set_table_name 'roles'
+  scope :builtin, lambda { |*args|
+    compare = 'not' if args.first
+    where("#{compare} builtin = 0")
+  }
+
+  has_many :filters, :dependent => :destroy, :class_name => 'FakeFilter', :foreign_key => 'role_id'
+  has_many :permissions, :through => :filters, :class_name => 'FakePermission', :foreign_key => 'permission_id'
+end
+
+class FakeFiltering < ActiveRecord::Base
+  set_table_name 'filterings'
+  belongs_to :filter, :class_name => 'FakeFilter'
+  belongs_to :permission, :class_name => 'FakePermission'
+end
+
+class FakeUser < ActiveRecord::Base
+  set_table_name 'users'
+  # we need this for polymorphic relation to work, it has class name hardcoded in AR
+  def self.name
+    'User'
+  end
+
+  has_and_belongs_to_many :compute_resources, :join_table => "user_compute_resources", :foreign_key => 'user_id'
+  has_and_belongs_to_many :domains, :join_table => "user_domains", :foreign_key => 'user_id'
+  has_many :user_hostgroups, :dependent => :destroy, :foreign_key => 'user_id'
+  has_many :hostgroups, :through => :user_hostgroups
+  has_many :user_facts, :dependent => :destroy, :foreign_key => 'user_id'
+  has_many :facts, :through => :user_facts, :source => :fact_name
+  has_many :user_roles, :dependent => :destroy, :foreign_key => 'owner_id',
+           :conditions => {:owner_type => 'User'}, :class_name => 'FakeUserRole'
+  has_many :roles, :through => :user_roles, :dependent => :destroy, :class_name => 'FakeRole'
+  taxonomy_join_table = "taxable_taxonomies"
+  has_many taxonomy_join_table, :dependent => :destroy, :as => :taxable, :foreign_key => 'taxable_id'
+  has_many :locations,     :through => taxonomy_join_table, :source => :taxonomy,
+           :conditions => "taxonomies.type='Location'", :validate => false
+  has_many :organizations, :through => taxonomy_join_table, :source => :taxonomy,
+           :conditions => "taxonomies.type='Organization'", :validate => false
+  has_many :cached_usergroup_members, :foreign_key => 'user_id'
+  has_many :cached_usergroups, :through => :cached_usergroup_members, :source => :usergroup
+end
+
+class MigratePermissions < ActiveRecord::Migration
+  def self.up
+    if old_permissions_present
+      migrate_roles
+      migrate_user_filters
+
+      flag = Setting::General.find_or_initialize_by_name('fix_db_cache',
+                                                         :description   => 'Fix DB cache on next Foreman restart',
+                                                         :settings_type => 'boolean', :default => false)
+      flag.update_attributes :value => true
+      Rake::Task['db:migrate'].enhance nil do
+        Rake::Task['fix_db_cache'].invoke
+      end
+    else
+      say 'Skipping migration of permissions, since old permissions are not present'
+    end
+  end
+
+  # STEP 1 - migrate roles
+  # for all role permissions we'll create unlimited filters
+  # we'll group permissions into filters by their resource
+  def self.migrate_roles
+    roles = FakeRole.all
+    roles.each do |role|
+
+      # role without permissions? nothing to do then
+      if role.attributes['permissions'].nil?
+        say "no old permissions found for role '#{role.name}', skipping"
+        next
+      end
+
+      # permissions assigned to role which we want to migrate
+      permission_names = YAML.load(role.attributes['permissions'])
+
+      # role without permissions but with YAML record
+      if permission_names.blank?
+        clear_old_permission(role)
+        next
+      end
+
+      # filter out unknown permissions, this could be leftovers from an old plugin.
+      role_permissions = FakePermission.where(:name => permission_names)
+
+      # we group permissions by resource the belong to
+      # then create a filter per resource
+      # and create a new relation between mapped permission and this filter
+      role_permissions.group_by(&:resource_type).each do |resource, permissions|
+        filter = FakeFilter.new
+        filter.role = role
+        filter.save!
+        say "Created an unlimited filter for role '#{role.name}'"
+
+        permissions.each do |permission|
+          filtering            = FakeFiltering.new
+          filtering.filter     = filter
+          filtering.permission = FakePermission.find_by_name(permission.name)
+          filtering.save!
+          say "... with permission '#{permission.name}'"
+        end
+      end
+
+      # finally we clear old permissions from role so
+      clear_old_permission(role)
+    end
+  end
+
+  def self.clear_old_permission(role)
+    say "Clearing old permissions for role '#{role.name}'"
+    if FakeRole.update_all("permissions = NULL", "id = #{role.id}") == 1
+      say "... OK"
+    else
+      raise "could not clear old permissions for role '#{role.name}'"
+    end
+  end
+
+  # STEP 2 - migrate user filters
+  # for every user having a filter we make copy of all his roles and add filtering scoped searches
+  # to corresponding filters
+  def self.migrate_user_filters
+    users = FakeUser.all
+    users.each do |user|
+      unless filtered?(user)
+        say "no filters found for user '#{user.login}', skipping"
+        next
+      end
+
+      say "Migrating user '#{user.login}'"
+      say "... cloning all roles"
+      clones     = user.roles.builtin(false).map { |r| clone_role(r, user) }
+      user.roles = clones + user.roles.builtin(true)
+      say "... done"
+
+      filters                     = Hash.new { |h, k| h[k] = '' }
+
+      # compute resources
+      filters[:compute_resources] = search = user.compute_resources.uniq.map { |cr| "id = #{cr.id}" }.join(' or ')
+      affected                    = clones.map(&:filters).flatten.select { |f| f.resource_type == 'ComputeResource' }
+      affected.each do |filter|
+        filter.update_attributes :search => search unless search.blank?
+      end
+      say "... compute resource filters applied"
+
+      # domains were not limited in old system, to keep it compatible, we don't convert it and use just search string
+      # later for hosts
+      filters[:domains]    = user.domains.uniq.map { |cr| "id = #{cr.id}" }.join(' or ')
+
+      # host groups
+      filters[:hostgroups] = search = user.hostgroups.uniq.map { |cr| "id = #{cr.id}" }.join(' or ')
+      affected             = clones.map(&:filters).flatten.select { |f| f.resource_type == 'Hostgroup' }
+      affected.each do |filter|
+        filter.update_attributes :search => search unless search.blank?
+      end
+      say "... hostgroups filters applied"
+
+      # fact_values for hosts scope
+      filters[:facts] = user.user_facts.uniq.map { |uf| "facts.#{uf.fact_name.name} #{uf.operator} #{uf.criteria}" }.join(' or ')
+
+      search, orgs, locs = convert_filters_to_search(filters, user)
+
+      affected = clones.map(&:filters).flatten.select { |f| f.resource_type == 'Host' }
+      affected.each do |filter|
+        filter.organizations = orgs
+        filter.locations = locs
+        filter.update_attributes :search => search unless search.blank?
+      end
+      say "... all other filters applied"
+
+      say "Removing old filter"
+      user.domains           = []
+      user.compute_resources = []
+      user.hostgroups        = []
+      user.facts             = []
+      user.filter_on_owner   = false
+      user.save!
+      say "... done"
+    end
+  end
+
+  def self.convert_filters_to_search(filters, user)
+    search = ''
+    orgs = []
+    locs = []
+
+    # owner_type
+    if user.filter_on_owner
+      user_cond = "owner_id = #{user.id} and owner_type = User"
+      group_cond = user.cached_usergroups.uniq.map { |g| "owner_id = #{g.id}" }.join(' or ')
+      search = "(#{user_cond})"
+      search += " or ((#{group_cond}) and owner_type = Usergroup)" unless group_cond.blank?
+    end
+
+    # normal filters - domains, compute resource, hostgroup, facts
+    filter = filters[:domains].gsub('id', 'domain_id')
+    search = "(#{search}) #{user.domains_andor} (#{filter})" unless filter.blank?
+    filter = filters[:compute_resources].gsub('id', 'compute_resource_id')
+    search = "(#{search}) #{user.compute_resources_andor} (#{filter})" unless filter.blank?
+    filter = filters[:hostgroups].gsub('id', 'hostgroup_id')
+    search = "(#{search}) #{user.hostgroups_andor} (#{filter})" unless filter.blank?
+    filter = filters[:facts]
+    search = "(#{search}) #{user.facts_andor} (#{filter})" unless filter.blank?
+
+    # taxonomies
+    if SETTINGS[:organizations_enabled]
+      orgs = user.organizations
+    end
+    if SETTINGS[:locations_enabled]
+      locs = user.locations
+    end
+
+    # fix first and/or that could appear
+    search = search.sub(/^\(\)\s*(and|or)\s*/, '')
+    [ search, orgs, locs ]
+  end
+
+  def self.filtered?(user)
+    user.compute_resources.present? ||
+        user.domains.present? ||
+        user.hostgroups.present? ||
+        user.facts.present? ||
+        user.filter_on_owner
+  end
+
+  def self.clone_role(role, user)
+    clone      = role.dup
+    clone.name = role.name + "_#{user.login}"
+    clone.save!
+
+    role.filters.each { |f| clone_filter(f, clone) }
+
+    clone.reload
+  end
+
+  def self.clone_filter(filter, role)
+    clone             = filter.dup
+    clone.permissions = filter.permissions
+    clone.role        = role
+    clone.save!
+  end
+
+
+  # To detect whether migration is needed we use existing models
+  # fakes would always indicate that migration is needed
+  def self.old_permissions_present
+    user = User.new
+    Role.column_names.include?('permissions') &&
+        user.respond_to?(:compute_resources) &&
+        user.respond_to?(:domains) &&
+        user.respond_to?(:hostgroups) &&
+        user.respond_to?(:facts) &&
+        user.respond_to?(:filter_on_owner)
+  end
+
+  def self.down
+    say 'Permission data migration is impossible, skipping'
+  end
+end
diff --git a/db/migrate/20140219183345_add_taxonomy_searches_to_filter.rb b/db/migrate/20140219183345_add_taxonomy_searches_to_filter.rb
new file mode 100644
index 000000000..04d4720db
--- /dev/null
+++ b/db/migrate/20140219183345_add_taxonomy_searches_to_filter.rb
@@ -0,0 +1,21 @@
+class AddTaxonomySearchesToFilter < ActiveRecord::Migration
+  def self.up
+    add_column :filters, :taxonomy_search, :text
+
+    # to precache taxonomy search on all existing filters
+    # setting may not exist yet
+    flag = Setting::General.find_or_initialize_by_name('fix_db_cache',
+                                                       :description   => 'Fix DB cache on next Foreman restart',
+                                                       :settings_type => 'boolean', :default => false)
+    flag.update_attributes :value => true
+
+    Rake::Task['db:migrate'].enhance nil do
+      Filter.reset_column_information
+      Rake::Task['fix_db_cache'].invoke
+    end
+  end
+
+  def self.down
+    remove_column :filters, :taxonomy_search
+  end
+end
diff --git a/db/seeds.d/05-architectures.rb b/db/seeds.d/05-architectures.rb
new file mode 100644
index 000000000..03de07692
--- /dev/null
+++ b/db/seeds.d/05-architectures.rb
@@ -0,0 +1,5 @@
+# Architectures
+Architecture.without_auditing do
+  Architecture.find_or_create_by_name "x86_64"
+  Architecture.find_or_create_by_name "i386"
+end
diff --git a/db/seeds.d/07-config_templates.rb b/db/seeds.d/07-config_templates.rb
new file mode 100644
index 000000000..5e74d77f3
--- /dev/null
+++ b/db/seeds.d/07-config_templates.rb
@@ -0,0 +1,59 @@
+# Find known operating systems for associations
+os_junos = Operatingsystem.find_all_by_type "Junos" || Operatingsystem.where("name LIKE ?", "junos")
+os_solaris = Operatingsystem.find_all_by_type "Solaris"
+os_suse = Operatingsystem.find_all_by_type "Suse" || Operatingsystem.where("name LIKE ?", "suse")
+os_windows = Operatingsystem.find_all_by_type "Windows"
+
+# Template kinds
+kinds = {}
+[:PXELinux, :PXEGrub, :iPXE, :provision, :finish, :script, :user_data, :ZTP].each do |type|
+  kinds[type] = TemplateKind.find_by_name(type)
+  kinds[type] ||= TemplateKind.create :name => type
+  raise "Unable to create template kind: #{format_errors kinds[type]}" if kinds[type].nil? || kinds[type].errors.any?
+end
+
+# Provisioning templates
+ConfigTemplate.without_auditing do
+  [
+      # Generic PXE files
+      { :name => 'PXELinux global default', :source => 'pxe/PXELinux_default.erb', :template_kind => kinds[:PXELinux] },
+      { :name => 'PXELinux default local boot', :source => 'pxe/PXELinux_local.erb', :template_kind => kinds[:PXELinux] },
+      { :name => 'PXEGrub default local boot', :source => 'pxe/PXEGrub_local.erb', :template_kind => kinds[:PXEGrub] },
+      # OS specific files
+      { :name => 'AutoYaST default', :source => 'autoyast/provision.erb', :template_kind => kinds[:provision], :operatingsystems => os_suse },
+      { :name => 'AutoYaST SLES default', :source => 'autoyast/provision_sles.erb', :template_kind => kinds[:provision], :operatingsystems => os_suse },
+      { :name => 'AutoYaST default PXELinux', :source => 'autoyast/PXELinux.erb', :template_kind => kinds[:PXELinux], :operatingsystems => os_suse },
+      { :name => 'Grubby default', :source => 'scripts/grubby.erb', :template_kind => kinds[:script] },
+      { :name => 'Jumpstart default', :source => 'jumpstart/provision.erb', :template_kind => kinds[:provision], :operatingsystems => os_solaris },
+      { :name => 'Jumpstart default finish', :source => 'jumpstart/finish.erb', :template_kind => kinds[:finish], :operatingsystems => os_solaris },
+      { :name => 'Jumpstart default PXEGrub', :source => 'jumpstart/PXEGrub.erb', :template_kind => kinds[:PXEGrub], :operatingsystems => os_solaris },
+      { :name => 'Kickstart default', :source => 'kickstart/provision.erb', :template_kind => kinds[:provision] },
+      { :name => 'Kickstart RHEL default', :source => 'kickstart/provision_rhel.erb', :template_kind => kinds[:provision] },
+      { :name => 'Kickstart default PXELinux', :source => 'kickstart/PXELinux.erb', :template_kind => kinds[:PXELinux] },
+      { :name => 'Kickstart default iPXE', :source => 'kickstart/iPXE.erb', :template_kind => kinds[:iPXE] },
+      { :name => 'Kickstart default user data', :source => 'kickstart/userdata.erb', :template_kind => kinds[:user_data] },
+      { :name => 'Preseed default', :source => 'preseed/provision.erb', :template_kind => kinds[:provision] },
+      { :name => 'Preseed default finish', :source => 'preseed/finish.erb', :template_kind => kinds[:finish] },
+      { :name => 'Preseed default PXELinux', :source => 'preseed/PXELinux.erb', :template_kind => kinds[:PXELinux] },
+      { :name => 'Preseed default iPXE', :source => 'preseed/iPXE.erb', :template_kind => kinds[:iPXE] },
+      { :name => 'Preseed default user data', :source => 'preseed/userdata.erb', :template_kind => kinds[:user_data] },
+      { :name => 'WAIK default PXELinux', :source => 'waik/PXELinux.erb', :template_kind => kinds[:PXELinux], :operatingsystems => os_windows },
+      { :name => "Junos default SLAX", :source => 'ztp/provision.erb', :template_kind => kinds[:provision], :operatingsystems => os_junos },
+      { :name => "Junos default ZTP config", :source => 'ztp/ZTP.erb', :template_kind => kinds[:ZTP], :operatingsystems => os_junos },
+      { :name => "Junos default finish", :source => 'ztp/finish.erb', :template_kind => kinds[:finish], :operatingsystems => os_junos },
+      # snippets
+      { :name => 'epel', :source => 'snippets/_epel.erb', :snippet => true },
+      { :name => 'fix_hosts', :source => 'snippets/_fix_hosts.erb', :snippet => true },
+      { :name => 'http_proxy', :source => 'snippets/_http_proxy.erb', :snippet => true },
+      { :name => 'puppet.conf', :source => 'snippets/_puppet.conf.erb', :snippet => true },
+      { :name => 'redhat_register', :source => 'snippets/_redhat_register.erb', :snippet => true }
+  ].each do |input|
+    next if ConfigTemplate.find_by_name(input[:name])
+    next if audit_modified? ConfigTemplate, input[:name]
+    t = ConfigTemplate.create({
+                                  :snippet  => false,
+                                  :template => File.read(File.join("#{Rails.root}/app/views/unattended", input.delete(:source)))
+                              }.merge(input))
+    raise "Unable to create template #{t.name}: #{format_errors t}" if t.nil? || t.errors.any?
+  end
+end
diff --git a/db/seeds.d/08-partition_tables.rb b/db/seeds.d/08-partition_tables.rb
new file mode 100644
index 000000000..9af750446
--- /dev/null
+++ b/db/seeds.d/08-partition_tables.rb
@@ -0,0 +1,21 @@
+# Partition tables
+Ptable.without_auditing do
+  [
+      { :name => 'AutoYaST entire SCSI disk', :os_family => 'Suse', :source => 'autoyast/disklayout_scsi.erb' },
+      { :name => 'AutoYaST entire virtual disk', :os_family => 'Suse', :source => 'autoyast/disklayout_virtual.erb' },
+      { :name => 'AutoYaST LVM', :os_family => 'Suse', :source => 'autoyast/disklayout_lvm.erb' },
+      { :name => 'Jumpstart default', :os_family => 'Solaris', :source => 'jumpstart/disklayout.erb' },
+      { :name => 'Jumpstart mirrored', :os_family => 'Solaris', :source => 'jumpstart/disklayout_mirrored.erb' },
+      { :name => 'Kickstart default', :os_family => 'Redhat', :source => 'kickstart/disklayout.erb' },
+      { :name => 'Preseed default', :os_family => 'Debian', :source => 'preseed/disklayout.erb' },
+      { :name => 'Preseed custom LVM', :os_family => 'Debian', :source => 'preseed/disklayout_lvm.erb' },
+      { :name => 'Junos default fake', :os_family => 'Junos', :source => 'ztp/disklayout.erb' }
+  ].each do |input|
+    next if Ptable.find_by_name(input[:name])
+    next if audit_modified? Ptable, input[:name]
+    p = Ptable.create({
+                          :layout => File.read(File.join("#{Rails.root}/app/views/unattended", input.delete(:source)))
+                      }.merge(input))
+    raise "Unable to create partition table: #{format_errors p}" if p.nil? || p.errors.any?
+  end
+end
diff --git a/db/seeds.d/10-installation_media.rb b/db/seeds.d/10-installation_media.rb
new file mode 100644
index 000000000..e88a40407
--- /dev/null
+++ b/db/seeds.d/10-installation_media.rb
@@ -0,0 +1,17 @@
+os_suse = Operatingsystem.find_all_by_type "Suse" || Operatingsystem.where("name LIKE ?", "suse")
+
+# Installation media: default mirrors
+Medium.without_auditing do
+  [
+      { :name => "CentOS mirror", :os_family => "Redhat", :path => "http://mirror.centos.org/centos/$major.$minor/os/$arch" },
+      { :name => "Debian mirror", :os_family => "Debian", :path => "http://ftp.debian.org/debian/" },
+      { :name => "Fedora mirror", :os_family => "Redhat", :path => "http://dl.fedoraproject.org/pub/fedora/linux/releases/$major/Fedora/$arch/os/" },
+      { :name => "OpenSUSE mirror", :os_family => "Suse", :path => "http://download.opensuse.org/distribution/$major.$minor/repo/oss", :operatingsystems => os_suse },
+      { :name => "Ubuntu mirror", :os_family => "Debian", :path => "http://archive.ubuntu.com/ubuntu/" }
+  ].each do |input|
+    next if Medium.where(['name = ? OR path = ?', input[:name], input[:path]]).any?
+    next if audit_modified? Medium, input[:name]
+    m = Medium.create input
+    raise "Unable to create medium: #{format_errors m}" if m.nil? || m.errors.any?
+  end
+end
diff --git a/db/seeds.d/11-smart_proxy_features.rb b/db/seeds.d/11-smart_proxy_features.rb
new file mode 100644
index 000000000..c80fb00ed
--- /dev/null
+++ b/db/seeds.d/11-smart_proxy_features.rb
@@ -0,0 +1,5 @@
+# Proxy features
+[ "TFTP", "DNS", "DHCP", "Puppet", "Puppet CA", "BMC", "Chef Proxy" ].each do |input|
+  f = Feature.find_or_create_by_name(input)
+  raise "Unable to create proxy feature: #{format_errors f}" if f.nil? || f.errors.any?
+end
diff --git a/db/seeds.d/12-auth_sources.rb b/db/seeds.d/12-auth_sources.rb
new file mode 100644
index 000000000..e049e1fed
--- /dev/null
+++ b/db/seeds.d/12-auth_sources.rb
@@ -0,0 +1,17 @@
+AuthSource.without_auditing do
+  # Auth sources
+  src = AuthSourceInternal.find_by_type "AuthSourceInternal"
+  src ||= AuthSourceInternal.create :name => "Internal"
+
+  # Users
+  unless User.find_by_login("admin").present?
+    User.without_auditing do
+      user = User.new(:login => "admin", :firstname => "Admin", :lastname => "User", :mail => Setting[:administrator])
+      user.admin = true
+      user.auth_source = src
+      user.password = "changeme"
+      User.current = user
+      raise "Unable to create admin user: #{format_errors user}" unless user.save
+    end
+  end
+end
diff --git a/db/seeds.d/13-compute_profiles.rb b/db/seeds.d/13-compute_profiles.rb
new file mode 100644
index 000000000..18206e361
--- /dev/null
+++ b/db/seeds.d/13-compute_profiles.rb
@@ -0,0 +1,13 @@
+# Compute Profiles - only create if there are not any
+if ComputeProfile.unconfigured?
+  ComputeProfile.without_auditing do
+    [
+        { :name => '1-Small' },
+        { :name => '2-Medium' },
+        { :name => '3-Large' },
+    ].each do |input|
+      cp = ComputeProfile.create input
+      raise "Unable to create hardware profile: #{format_errors m}" if cp.nil? || cp.errors.any?
+    end
+  end
+end
diff --git a/db/seeds.d/15-bookmarks.rb b/db/seeds.d/15-bookmarks.rb
new file mode 100644
index 000000000..5acadcadb
--- /dev/null
+++ b/db/seeds.d/15-bookmarks.rb
@@ -0,0 +1,16 @@
+# Bookmarks
+Bookmark.without_auditing do
+  [
+      { :name => "eventful", :query => "eventful = true", :controller=> "reports" },
+      { :name => "active", :query => 'last_report > "35 minutes ago" and (status.applied > 0 or status.restarted > 0)', :controller=> "hosts" },
+      { :name => "out of sync", :query => 'last_report < "30 minutes ago" andstatus.enabled = true', :controller=> "hosts" },
+      { :name => "error", :query => 'last_report > "35 minutes ago" and (status.failed > 0 or status.failed_restarts > 0 or status.skipped > 0)', :controller=> "hosts" },
+      { :name => "disabled", :query => 'status.enabled = false', :controller=> "hosts" },
+      { :name => "ok hosts", :query => 'last_report > "35 minutes ago" and status.enabled = true and status.applied = 0 and status.failed = 0 and status.pending = 0', :controller=> "hosts" }
+  ].each do |input|
+    next if Bookmark.find_by_name(input[:name])
+    next if audit_modified? Bookmark, input[:name]
+    b = Bookmark.create({ :public => true }.merge(input))
+    raise "Unable to create bookmark: #{format_errors b}" if b.nil? || b.errors.any?
+  end
+end
diff --git a/db/seeds.d/20-permissions.rb b/db/seeds.d/20-permissions.rb
new file mode 100644
index 000000000..44713490a
--- /dev/null
+++ b/db/seeds.d/20-permissions.rb
@@ -0,0 +1,152 @@
+permissions = [
+    ['Architecture', 'view_architectures'],
+    ['Architecture', 'create_architectures'],
+    ['Architecture', 'edit_architectures'],
+    ['Architecture', 'destroy_architectures'],
+    ['Audit', 'view_audit_logs'],
+    ['AuthSourceLdap', 'view_authenticators'],
+    ['AuthSourceLdap', 'create_authenticators'],
+    ['AuthSourceLdap', 'edit_authenticators'],
+    ['AuthSourceLdap', 'destroy_authenticators'],
+    ['Bookmark', 'view_bookmarks'],
+    ['Bookmark', 'create_bookmarks'],
+    ['Bookmark', 'edit_bookmarks'],
+    ['Bookmark', 'destroy_bookmarks'],
+    ['ComputeProfile', 'view_compute_profiles'],
+    ['ComputeProfile', 'create_compute_profiles'],
+    ['ComputeProfile', 'edit_compute_profiles'],
+    ['ComputeProfile', 'destroy_compute_profiles'],
+    ['ComputeResource', 'view_compute_resources'],
+    ['ComputeResource', 'create_compute_resources'],
+    ['ComputeResource', 'edit_compute_resources'],
+    ['ComputeResource', 'destroy_compute_resources'],
+    ['ComputeResource', 'view_compute_resources_vms'],
+    ['ComputeResource', 'create_compute_resources_vms'],
+    ['ComputeResource', 'edit_compute_resources_vms'],
+    ['ComputeResource', 'destroy_compute_resources_vms'],
+    ['ComputeResource', 'power_compute_resources_vms'],
+    ['ComputeResource', 'console_compute_resources_vms'],
+    ['ConfigTemplate', 'view_templates'],
+    ['ConfigTemplate', 'create_templates'],
+    ['ConfigTemplate', 'edit_templates'],
+    ['ConfigTemplate', 'destroy_templates'],
+    ['ConfigTemplate', 'deploy_templates'],
+    [nil, 'access_dashboard'],
+    ['Domain', 'view_domains'],
+    ['Domain', 'create_domains'],
+    ['Domain', 'edit_domains'],
+    ['Domain', 'destroy_domains'],
+    ['Environment', 'view_environments'],
+    ['Environment', 'create_environments'],
+    ['Environment', 'edit_environments'],
+    ['Environment', 'destroy_environments'],
+    ['Environment', 'import_environments'],
+    ['LookupKey', 'view_external_variables'],
+    ['LookupKey', 'create_external_variables'],
+    ['LookupKey', 'edit_external_variables'],
+    ['LookupKey', 'destroy_external_variables'],
+    ['FactValue', 'view_facts'],
+    ['FactValue', 'upload_facts'],
+    ['Filter', 'view_filters'],
+    ['Filter', 'create_filters'],
+    ['Filter', 'edit_filters'],
+    ['Filter', 'destroy_filters'],
+    ['CommonParameter', 'view_globals'],
+    ['CommonParameter', 'create_globals'],
+    ['CommonParameter', 'edit_globals'],
+    ['CommonParameter', 'destroy_globals'],
+    ['HostClass', 'edit_classes'],
+    ['Parameter', 'create_params'],
+    ['Parameter', 'edit_params'],
+    ['Parameter', 'destroy_params'],
+    ['Hostgroup', 'view_hostgroups'],
+    ['Hostgroup', 'create_hostgroups'],
+    ['Hostgroup', 'edit_hostgroups'],
+    ['Hostgroup', 'destroy_hostgroups'],
+    ['Host', 'view_hosts'],
+    ['Host', 'create_hosts'],
+    ['Host', 'edit_hosts'],
+    ['Host', 'destroy_hosts'],
+    ['Host', 'build_hosts'],
+    ['Host', 'power_hosts'],
+    ['Host', 'console_hosts'],
+    ['Host', 'ipmi_boot'],
+    ['Host', 'puppetrun_hosts'],
+    ['Image', 'view_images'],
+    ['Image', 'create_images'],
+    ['Image', 'edit_images'],
+    ['Image', 'destroy_images'],
+    ['Location', 'view_locations'],
+    ['Location', 'create_locations'],
+    ['Location', 'edit_locations'],
+    ['Location', 'destroy_locations'],
+    ['Location', 'assign_locations'],
+    ['Medium', 'view_media'],
+    ['Medium', 'create_media'],
+    ['Medium', 'edit_media'],
+    ['Medium', 'destroy_media'],
+    ['Model', 'view_models'],
+    ['Model', 'create_models'],
+    ['Model', 'edit_models'],
+    ['Model', 'destroy_models'],
+    ['Operatingsystem', 'view_operatingsystems'],
+    ['Operatingsystem', 'create_operatingsystems'],
+    ['Operatingsystem', 'edit_operatingsystems'],
+    ['Operatingsystem', 'destroy_operatingsystems'],
+    ['Organization', 'view_organizations'],
+    ['Organization', 'create_organizations'],
+    ['Organization', 'edit_organizations'],
+    ['Organization', 'destroy_organizations'],
+    ['Organization', 'assign_organizations'],
+    ['Ptable', 'view_ptables'],
+    ['Ptable', 'create_ptables'],
+    ['Ptable', 'edit_ptables'],
+    ['Ptable', 'destroy_ptables'],
+    [nil, 'view_plugins'],
+    ['Puppetclass', 'view_puppetclasses'],
+    ['Puppetclass', 'create_puppetclasses'],
+    ['Puppetclass', 'edit_puppetclasses'],
+    ['Puppetclass', 'destroy_puppetclasses'],
+    ['Puppetclass', 'import_puppetclasses'],
+    ['Report', 'view_reports'],
+    ['Report', 'destroy_reports'],
+    ['Report', 'upload_reports'],
+    ['Role', 'view_roles'],
+    ['Role', 'create_roles'],
+    ['Role', 'edit_roles'],
+    ['Role', 'destroy_roles'],
+    [nil, 'access_settings'],
+    ['SmartProxy', 'view_smart_proxies'],
+    ['SmartProxy', 'create_smart_proxies'],
+    ['SmartProxy', 'edit_smart_proxies'],
+    ['SmartProxy', 'destroy_smart_proxies'],
+    ['SmartProxy', 'view_smart_proxies_autosign'],
+    ['SmartProxy', 'create_smart_proxies_autosign'],
+    ['SmartProxy', 'destroy_smart_proxies_autosign'],
+    ['SmartProxy', 'view_smart_proxies_puppetca'],
+    ['SmartProxy', 'edit_smart_proxies_puppetca'],
+    ['SmartProxy', 'destroy_smart_proxies_puppetca'],
+    [nil, 'view_statistics'],
+    ['Subnet', 'view_subnets'],
+    ['Subnet', 'create_subnets'],
+    ['Subnet', 'edit_subnets'],
+    ['Subnet', 'destroy_subnets'],
+    ['Subnet', 'import_subnets'],
+    [nil, 'view_tasks'],
+    ['Trend', 'view_trends'],
+    ['Trend', 'create_trends'],
+    ['Trend', 'edit_trends'],
+    ['Trend', 'destroy_trends'],
+    ['Trend', 'update_trends'],
+    ['Usergroup', 'view_usergroups'],
+    ['Usergroup', 'create_usergroups'],
+    ['Usergroup', 'edit_usergroups'],
+    ['Usergroup', 'destroy_usergroups'],
+    ['User', 'view_users'],
+    ['User', 'create_users'],
+    ['User', 'edit_users'],
+    ['User', 'destroy_users'],
+]
+permissions.each do |resource, permission|
+  Permission.find_or_create_by_resource_type_and_name resource, permission
+end
diff --git a/db/seeds.d/25-roles.rb b/db/seeds.d/25-roles.rb
new file mode 100644
index 000000000..1852e4910
--- /dev/null
+++ b/db/seeds.d/25-roles.rb
@@ -0,0 +1,70 @@
+# Roles
+default_permissions =
+    { 'Manager'               => [:view_architectures, :create_architectures, :edit_architectures, :destroy_architectures,
+                                  :view_authenticators, :create_authenticators, :edit_authenticators, :destroy_authenticators,
+                                  :view_bookmarks, :create_bookmarks, :edit_bookmarks, :destroy_bookmarks,
+                                  :view_compute_resources, :create_compute_resources, :edit_compute_resources, :destroy_compute_resources,
+                                  :view_compute_resources_vms, :create_compute_resources_vms, :edit_compute_resources_vms, :destroy_compute_resources_vms, :power_compute_resources_vms, :console_compute_resources_vms,
+                                  :view_templates, :create_templates, :edit_templates, :destroy_templates, :deploy_templates,
+                                  :view_domains, :create_domains, :edit_domains, :destroy_domains,
+                                  :view_environments, :create_environments, :edit_environments, :destroy_environments, :import_environments,
+                                  :view_external_variables, :create_external_variables, :edit_external_variables, :destroy_external_variables,
+                                  :view_globals, :create_globals, :edit_globals, :destroy_globals,
+                                  :view_hostgroups, :create_hostgroups, :edit_hostgroups, :destroy_hostgroups,
+                                  :view_hosts, :create_hosts, :edit_hosts, :destroy_hosts, :build_hosts, :power_hosts, :console_hosts, :ipmi_boot, :puppetrun_hosts,
+                                  :edit_classes, :create_params, :edit_params, :destroy_params,
+                                  :view_images, :create_images, :edit_images, :destroy_images,
+                                  :view_locations, :create_locations, :edit_locations, :destroy_locations, :assign_locations,
+                                  :view_media, :create_media, :edit_media, :destroy_media,
+                                  :view_models, :create_models, :edit_models, :destroy_models,
+                                  :view_operatingsystems, :create_operatingsystems, :edit_operatingsystems, :destroy_operatingsystems,
+                                  :view_ptables, :create_ptables, :edit_ptables, :destroy_ptables,
+                                  :view_puppetclasses, :create_puppetclasses, :edit_puppetclasses, :destroy_puppetclasses, :import_puppetclasses,
+                                  :view_smart_proxies, :create_smart_proxies, :edit_smart_proxies, :destroy_smart_proxies,
+                                  :view_smart_proxies_autosign, :create_smart_proxies_autosign, :destroy_smart_proxies_autosign,
+                                  :view_smart_proxies_puppetca, :edit_smart_proxies_puppetca, :destroy_smart_proxies_puppetca,
+                                  :view_subnets, :create_subnets, :edit_subnets, :destroy_subnets, :import_subnets,
+                                  :view_organizations, :create_organizations, :edit_organizations, :destroy_organizations, :assign_organizations,
+                                  :view_usergroups, :create_usergroups, :edit_usergroups, :destroy_usergroups,
+                                  :view_users, :create_users, :edit_users, :destroy_users, :access_settings, :access_dashboard,
+                                  :view_reports, :destroy_reports, :upload_reports,
+                                  :view_facts, :upload_facts, :view_audit_logs,
+                                  :view_statistics, :view_trends, :create_trends, :edit_trends, :destroy_trends, :update_trends,
+                                  :view_tasks, :view_plugins],
+      'Edit partition tables' => [:view_ptables, :create_ptables, :edit_ptables, :destroy_ptables],
+      'View hosts'            => [:view_hosts],
+      'Edit hosts'            => [:view_hosts, :edit_hosts, :create_hosts, :destroy_hosts, :build_hosts],
+      'Viewer'                => [:view_hosts, :view_puppetclasses, :view_hostgroups, :view_domains, :view_operatingsystems,
+                                  :view_locations, :view_media, :view_models, :view_environments, :view_architectures,
+                                  :view_ptables, :view_globals, :view_external_variables, :view_authenticators,
+                                  :access_settings, :access_dashboard, :view_reports, :view_facts, :view_smart_proxies,
+                                  :view_subnets, :view_statistics, :view_organizations, :view_usergroups, :view_users,
+                                  :view_audit_logs],
+      'Site manager'          => [:view_architectures, :view_audit_logs, :view_authenticators, :access_dashboard,
+                                  :view_domains, :view_environments, :import_environments, :view_external_variables,
+                                  :create_external_variables, :edit_external_variables, :destroy_external_variables,
+                                  :view_facts, :view_globals, :view_hostgroups, :view_hosts, :view_smart_proxies_puppetca,
+                                  :view_smart_proxies_autosign, :create_hosts, :edit_hosts, :destroy_hosts,
+                                  :build_hosts, :view_media, :create_media, :edit_media, :destroy_media,
+                                  :view_models, :view_operatingsystems, :view_ptables, :view_puppetclasses,
+                                  :import_puppetclasses, :view_reports, :destroy_reports, :access_settings,
+                                  :view_smart_proxies, :edit_smart_proxies, :view_subnets, :edit_subnets,
+                                  :view_statistics, :view_usergroups, :create_usergroups, :edit_usergroups,
+                                  :destroy_usergroups, :view_users, :edit_users],
+    }
+
+default_user_permissions = [:view_hosts, :view_puppetclasses, :view_hostgroups, :view_domains,
+                            :view_operatingsystems, :view_media, :view_models, :view_environments,
+                            :view_architectures, :view_ptables, :view_globals, :view_external_variables,
+                            :view_authenticators, :access_settings, :access_dashboard,
+                            :view_reports, :view_subnets, :view_facts, :view_locations,
+                            :view_organizations, :view_statistics]
+anonymous_permissions    = [:view_hosts, :view_bookmarks, :view_tasks]
+
+Role.without_auditing do
+  default_permissions.each do |role_name, permission_names|
+    create_role(role_name, permission_names, 0)
+  end
+  create_role('Default user', default_user_permissions, Role::BUILTIN_DEFAULT_USER)
+  create_role('Anonymous', anonymous_permissions, Role::BUILTIN_ANONYMOUS)
+end
diff --git a/db/seeds.rb b/db/seeds.rb
index 1a9a051d0..8c9301103 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -5,6 +5,8 @@
 #
 # Please ensure that all templates are submitted to community-templates, then they will be synced in.
 
+# define all helpers here
+
 def format_errors(model = nil)
   return '(nil found)' if model.nil?
   model.errors.full_messages.join(';')
@@ -20,161 +22,34 @@ def audit_modified?(type, name)
   false
 end
 
-# Architectures
-Architecture.without_auditing do
-  Architecture.find_or_create_by_name "x86_64"
-  Architecture.find_or_create_by_name "i386"
-end
-
-# Template kinds
-kinds = {}
-[:PXELinux, :PXEGrub, :iPXE, :provision, :finish, :script, :user_data, :ZTP].each do |type|
-  kinds[type] = TemplateKind.find_by_name(type)
-  kinds[type] ||= TemplateKind.create :name => type
-  raise "Unable to create template kind: #{format_errors kinds[type]}" if kinds[type].nil? || kinds[type].errors.any?
-end
-
-# Find known operating systems for associations
-os_junos = Operatingsystem.find_all_by_type "Junos" || Operatingsystem.where("name LIKE ?", "junos")
-os_solaris = Operatingsystem.find_all_by_type "Solaris"
-os_suse = Operatingsystem.find_all_by_type "Suse" || Operatingsystem.where("name LIKE ?", "suse")
-os_windows = Operatingsystem.find_all_by_type "Windows"
-
-# Partition tables
-Ptable.without_auditing do
-  [
-    { :name => 'AutoYaST entire SCSI disk', :os_family => 'Suse', :source => 'autoyast/disklayout_scsi.erb' },
-    { :name => 'AutoYaST entire virtual disk', :os_family => 'Suse', :source => 'autoyast/disklayout_virtual.erb' },
-    { :name => 'AutoYaST LVM', :os_family => 'Suse', :source => 'autoyast/disklayout_lvm.erb' },
-    { :name => 'Jumpstart default', :os_family => 'Solaris', :source => 'jumpstart/disklayout.erb' },
-    { :name => 'Jumpstart mirrored', :os_family => 'Solaris', :source => 'jumpstart/disklayout_mirrored.erb' },
-    { :name => 'Kickstart default', :os_family => 'Redhat', :source => 'kickstart/disklayout.erb' },
-    { :name => 'Preseed default', :os_family => 'Debian', :source => 'preseed/disklayout.erb' },
-    { :name => 'Preseed custom LVM', :os_family => 'Debian', :source => 'preseed/disklayout_lvm.erb' },
-    { :name => 'Junos default fake', :os_family => 'Junos', :source => 'ztp/disklayout.erb' }
-  ].each do |input|
-    next if Ptable.find_by_name(input[:name])
-    next if audit_modified? Ptable, input[:name]
-    p = Ptable.create({
-      :layout => File.read(File.join("#{Rails.root}/app/views/unattended", input.delete(:source)))
-    }.merge(input))
-    raise "Unable to create partition table: #{format_errors p}" if p.nil? || p.errors.any?
-  end
-end
-
-# Provisioning templates
-ConfigTemplate.without_auditing do
-  [
-    # Generic PXE files
-    { :name => 'PXELinux global default', :source => 'pxe/PXELinux_default.erb', :template_kind => kinds[:PXELinux] },
-    { :name => 'PXELinux default local boot', :source => 'pxe/PXELinux_local.erb', :template_kind => kinds[:PXELinux] },
-    { :name => 'PXEGrub default local boot', :source => 'pxe/PXEGrub_local.erb', :template_kind => kinds[:PXEGrub] },
-    # OS specific files
-    { :name => 'AutoYaST default', :source => 'autoyast/provision.erb', :template_kind => kinds[:provision], :operatingsystems => os_suse },
-    { :name => 'AutoYaST SLES default', :source => 'autoyast/provision_sles.erb', :template_kind => kinds[:provision], :operatingsystems => os_suse },
-    { :name => 'AutoYaST default PXELinux', :source => 'autoyast/PXELinux.erb', :template_kind => kinds[:PXELinux], :operatingsystems => os_suse },
-    { :name => 'Grubby default', :source => 'scripts/grubby.erb', :template_kind => kinds[:script] },
-    { :name => 'Jumpstart default', :source => 'jumpstart/provision.erb', :template_kind => kinds[:provision], :operatingsystems => os_solaris },
-    { :name => 'Jumpstart default finish', :source => 'jumpstart/finish.erb', :template_kind => kinds[:finish], :operatingsystems => os_solaris },
-    { :name => 'Jumpstart default PXEGrub', :source => 'jumpstart/PXEGrub.erb', :template_kind => kinds[:PXEGrub], :operatingsystems => os_solaris },
-    { :name => 'Kickstart default', :source => 'kickstart/provision.erb', :template_kind => kinds[:provision] },
-    { :name => 'Kickstart RHEL default', :source => 'kickstart/provision_rhel.erb', :template_kind => kinds[:provision] },
-    { :name => 'Kickstart default PXELinux', :source => 'kickstart/PXELinux.erb', :template_kind => kinds[:PXELinux] },
-    { :name => 'Kickstart default iPXE', :source => 'kickstart/iPXE.erb', :template_kind => kinds[:iPXE] },
-    { :name => 'Kickstart default user data', :source => 'kickstart/userdata.erb', :template_kind => kinds[:user_data] },
-    { :name => 'Preseed default', :source => 'preseed/provision.erb', :template_kind => kinds[:provision] },
-    { :name => 'Preseed default finish', :source => 'preseed/finish.erb', :template_kind => kinds[:finish] },
-    { :name => 'Preseed default PXELinux', :source => 'preseed/PXELinux.erb', :template_kind => kinds[:PXELinux] },
-    { :name => 'Preseed default iPXE', :source => 'preseed/iPXE.erb', :template_kind => kinds[:iPXE] },
-    { :name => 'Preseed default user data', :source => 'preseed/userdata.erb', :template_kind => kinds[:user_data] },
-    { :name => 'WAIK default PXELinux', :source => 'waik/PXELinux.erb', :template_kind => kinds[:PXELinux], :operatingsystems => os_windows },
-    { :name => "Junos default SLAX", :source => 'ztp/provision.erb', :template_kind => kinds[:provision], :operatingsystems => os_junos },
-    { :name => "Junos default ZTP config", :source => 'ztp/ZTP.erb', :template_kind => kinds[:ZTP], :operatingsystems => os_junos },
-    { :name => "Junos default finish", :source => 'ztp/finish.erb', :template_kind => kinds[:finish], :operatingsystems => os_junos },
-    # snippets
-    { :name => 'epel', :source => 'snippets/_epel.erb', :snippet => true },
-    { :name => 'fix_hosts', :source => 'snippets/_fix_hosts.erb', :snippet => true },
-    { :name => 'http_proxy', :source => 'snippets/_http_proxy.erb', :snippet => true },
-    { :name => 'puppet.conf', :source => 'snippets/_puppet.conf.erb', :snippet => true },
-    { :name => 'redhat_register', :source => 'snippets/_redhat_register.erb', :snippet => true }
-  ].each do |input|
-    next if ConfigTemplate.find_by_name(input[:name])
-    next if audit_modified? ConfigTemplate, input[:name]
-    t = ConfigTemplate.create({
-      :snippet  => false,
-      :template => File.read(File.join("#{Rails.root}/app/views/unattended", input.delete(:source)))
-    }.merge(input))
-    raise "Unable to create template #{t.name}: #{format_errors t}" if t.nil? || t.errors.any?
-  end
-end
+def create_filters(role, collection)
+  collection.group_by(&:resource_type).each do |resource, permissions|
+    filter      = Filter.new
+    filter.role = role
 
-# Installation media: default mirrors
-Medium.without_auditing do
-  [
-    { :name => "CentOS mirror", :os_family => "Redhat", :path => "http://mirror.centos.org/centos/$major.$minor/os/$arch" },
-    { :name => "Debian mirror", :os_family => "Debian", :path => "http://ftp.debian.org/debian/" },
-    { :name => "Fedora mirror", :os_family => "Redhat", :path => "http://dl.fedoraproject.org/pub/fedora/linux/releases/$major/Fedora/$arch/os/" },
-    { :name => "OpenSUSE mirror", :os_family => "Suse", :path => "http://download.opensuse.org/distribution/$major.$minor/repo/oss", :operatingsystems => os_suse },
-    { :name => "Ubuntu mirror", :os_family => "Debian", :path => "http://archive.ubuntu.com/ubuntu/" }
-  ].each do |input|
-    next if Medium.where(['name = ? OR path = ?', input[:name], input[:path]]).any?
-    next if audit_modified? Medium, input[:name]
-    m = Medium.create input
-    raise "Unable to create medium: #{format_errors m}" if m.nil? || m.errors.any?
-  end
-end
+    permissions.each do |permission|
+      filtering            = filter.filterings.build
+      filtering.permission = permission
+    end
 
-# Bookmarks
-Bookmark.without_auditing do
-  [
-    { :name => "eventful", :query => "eventful = true", :controller=> "reports" },
-    { :name => "active", :query => 'last_report > "35 minutes ago" and (status.applied > 0 or status.restarted > 0)', :controller=> "hosts" },
-    { :name => "out of sync", :query => 'last_report < "30 minutes ago" andstatus.enabled = true', :controller=> "hosts" },
-    { :name => "error", :query => 'last_report > "35 minutes ago" and (status.failed > 0 or status.failed_restarts > 0 or status.skipped > 0)', :controller=> "hosts" },
-    { :name => "disabled", :query => 'status.enabled = false', :controller=> "hosts" },
-    { :name => "ok hosts", :query => 'last_report > "35 minutes ago" and status.enabled = true and status.applied = 0 and status.failed = 0 and status.pending = 0', :controller=> "hosts" }
-  ].each do |input|
-    next if Bookmark.find_by_name(input[:name])
-    next if audit_modified? Bookmark, input[:name]
-    b = Bookmark.create({ :public => true }.merge(input))
-    raise "Unable to create bookmark: #{format_errors b}" if b.nil? || b.errors.any?
+    filter.save!
   end
 end
 
-# Proxy features
-[ "TFTP", "DNS", "DHCP", "Puppet", "Puppet CA", "BMC", "Chef Proxy" ].each do |input|
-  f = Feature.find_or_create_by_name(input)
-  raise "Unable to create proxy feature: #{format_errors f}" if f.nil? || f.errors.any?
-end
-
-AuthSource.without_auditing do
-  # Auth sources
-  src = AuthSourceInternal.find_by_type "AuthSourceInternal"
-  src ||= AuthSourceInternal.create :name => "Internal"
+def create_role(role_name, permission_names, builtin)
+  return if Role.find_by_name(role_name)
+  return if audit_modified? Role, role_name && builtin == 0
 
-  # Users
-  unless User.find_by_login("admin").present?
-    User.without_auditing do
-      user = User.new(:login => "admin", :firstname => "Admin", :lastname => "User", :mail => Setting[:administrator])
-      user.admin = true
-      user.auth_source = src
-      user.password = "changeme"
-      User.current = user
-      raise "Unable to create admin user: #{format_errors user}" unless user.save
-    end
-  end
+  role         = Role.new(:name => role_name)
+  role.builtin = builtin
+  role.save!
+  permissions = Permission.find_all_by_name permission_names
+  create_filters(role, permissions)
 end
 
-# Compute Profiles - only create if there are not any
-if ComputeProfile.unconfigured?
-  ComputeProfile.without_auditing do
-    [
-      { :name => '1-Small' },
-      { :name => '2-Medium' },
-      { :name => '3-Large' },
-    ].each do |input|
-      cp = ComputeProfile.create input
-      raise "Unable to create hardware profile: #{format_errors m}" if cp.nil? || cp.errors.any?
-    end
-  end
+# now we load all seed files
+Dir.glob(Rails.root + 'db/seeds.d/*.rb').sort.each do |seed|
+  puts "Seeding #{seed}"
+  load seed
 end
+puts "All seed files executed"
diff --git a/foreman.spec b/foreman.spec
index 94d0d295a..a392cf840 100644
--- a/foreman.spec
+++ b/foreman.spec
@@ -77,6 +77,7 @@ Requires: %{?scl_prefix}rubygem(jquery-ui-rails)
 Requires: %{?scl_prefix}rubygem(bootstrap-sass) >= 3.0.3.0
 Requires: %{?scl_prefix}rubygem(bootstrap-sass) < 3.0.4
 Requires: %{?scl_prefix}rubygem(foreigner) >= 1.4.2
+Requires: %{?scl_prefix}rubygem(deep_cloneable)
 BuildRequires: %{?scl_prefix}rubygem(ancestry) >= 2.0.0
 BuildRequires: %{?scl_prefix}rubygem(ancestry) < 3.0.0
 BuildRequires: %{?scl_prefix}rubygem(apipie-rails) >= 0.0.23
@@ -114,6 +115,7 @@ BuildRequires: %{?scl_prefix}rubygem(flot-rails) = 0.0.3
 BuildRequires: %{?scl_prefix}rubygem(foreigner) >= 1.4.2
 BuildRequires: %{?scl_prefix}rubygem(multi-select-rails) >= 0.9.10
 BuildRequires: %{?scl_prefix}rubygem(multi-select-rails) < 0.10.0
+BuildRequires: %{?scl_prefix}rubygem(deep_cloneable)
 BuildRequires: %{?scl_prefix}facter
 BuildRequires: gettext
 BuildRequires: asciidoc
@@ -452,12 +454,13 @@ ln -sv %{_sysconfdir}/%{name}/$i %{buildroot}%{_datadir}/%{name}/config/$i
 done
 
 # Put db in %{_localstatedir}/lib/%{name}/db
-cp -pr db/migrate db/seeds.rb %{buildroot}%{_datadir}/%{name}
+cp -pr db/migrate db/seeds.rb db/seeds.d %{buildroot}%{_datadir}/%{name}
 mkdir %{buildroot}%{_localstatedir}/lib/%{name}/db
 
 ln -sv %{_localstatedir}/lib/%{name}/db %{buildroot}%{_datadir}/%{name}/db
 ln -sv %{_datadir}/%{name}/migrate %{buildroot}%{_localstatedir}/lib/%{name}/db/migrate
 ln -sv %{_datadir}/%{name}/seeds.rb %{buildroot}%{_localstatedir}/lib/%{name}/db/seeds.rb
+ln -sv %{_datadir}/%{name}/seeds.d %{buildroot}%{_localstatedir}/lib/%{name}/db/seeds.d
 
 # Put HTML %{_localstatedir}/lib/%{name}/public
 cp -pr public %{buildroot}%{_localstatedir}/lib/%{name}/
diff --git a/lib/core_extensions.rb b/lib/core_extensions.rb
index 7e9934c54..02ecc2f8a 100644
--- a/lib/core_extensions.rb
+++ b/lib/core_extensions.rb
@@ -1,3 +1,4 @@
+require 'tsort'
 # Add an empty method to nil. Now no need for if x and x.empty?. Just x.empty?
 class NilClass
   def empty?
@@ -51,6 +52,48 @@ class ActiveRecord::Base
     end
   end
 
+  class EnsureNoCycle
+    include TSort
+
+    def initialize(base, source, target)
+      @source = source; @target = target
+      @base  = base.map { |record| [record.send(@source), record.send(@target)] }
+      @nodes = @base.flatten.uniq
+      @graph = Hash.new { |h, k| h[k] = [] }
+      @base.each { |s, t| @graph[s]<< t }
+    end
+
+    def tsort_each_node(&block)
+      @nodes.each(&block)
+    end
+
+    def tsort_each_child(node, &block)
+      @graph[node].each(&block)
+    end
+
+    def ensure(record)
+      @record = record
+      add_new_edges
+      detect_cycle
+    end
+
+    private
+
+    def add_new_edges
+      edges = @graph[@record.send(@source) || 0]
+      edges<< @record.send(@target) unless edges.include?(@record.send(@target))
+    end
+
+    def detect_cycle
+      if strongly_connected_components.any? { |component| component.size > 1 }
+        @record.errors.add :base, _("Adding would cause a cycle!")
+        raise ::Foreman::CyclicGraphException, @record
+      else
+        true
+      end
+    end
+  end
+
   def id_and_type
     "#{id}-#{self.class.table_name.humanize}"
   end
diff --git a/lib/foreman/default_data/loader.rb b/lib/foreman/default_data/loader.rb
deleted file mode 100644
index 8d142964e..000000000
--- a/lib/foreman/default_data/loader.rb
+++ /dev/null
@@ -1,166 +0,0 @@
-# redMine - project management software
-# Copyright (C) 2006-2007  Jean-Philippe Lang
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE  See the
-# GNU General Public License for more details
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
-
-module Foreman
-  module DefaultData
-    class DataAlreadyLoaded < Exception; end
-
-    module Loader
-      class << self
-        # Returns true if no data is already loaded in the database
-        # otherwise false
-        def no_data?
-          !Role.first(:conditions => {:builtin => 0})
-        end
-
-        # Loads the default data
-        # Raises a RecordNotSaved exception if something goes wrong
-        def load(reset=false)
-
-          # We may be executing something like rake db:migrate:reset, which destroys this table; only continue if the table exists
-          Role.count rescue return
-          Role.transaction do
-            # Roles
-            manager = Role.where(:name => "Manager").empty? ? Role.create(:name => "Manager") : Role.where(:name => "Manager")[0]
-            if reset or manager.permissions.empty?
-              manager.update_attribute :permissions, manager.setable_permissions.collect {|p| p.name}
-            end
-
-            ptable_editor =  Role.where(:name => "Edit partition tables").empty? ? Role.create(:name => "Edit partition tables") : Role.where(:name => "Edit partition tables")[0]
-            if reset or ptable_editor.permissions.empty?
-              ptable_editor.update_attribute :permissions, [:view_ptables, :create_ptables, :edit_ptables, :destroy_ptables]
-            end
-
-            hosts_reader =  Role.where(:name => "View hosts").empty? ? Role.create(:name => "View hosts") : Role.where(:name => "View hosts")[0]
-            if reset or hosts_reader.permissions.empty?
-              hosts_reader.update_attribute :permissions, [:view_hosts]
-            end
-
-            hosts_editor =  Role.where(:name => "Edit hosts").empty? ? Role.create(:name => "Edit hosts") : Role.where(:name => "Edit hosts")[0]
-            if reset or hosts_editor.permissions.empty?
-              hosts_editor.update_attribute :permissions, [:view_hosts,    :edit_hosts,    :create_hosts,    :destroy_hosts, :build_hosts]
-            end
-
-            viewer =  Role.where(:name => "Viewer").empty? ? Role.create(:name => "Viewer") : Role.where(:name => "Viewer")[0]
-            if reset or viewer.permissions.empty?
-              viewer.update_attribute :permissions, [:view_hosts,
-                :view_puppetclasses,
-                :view_hostgroups,
-                :view_domains,
-                :view_operatingsystems,
-                :view_locations,
-                :view_media,
-                :view_models,
-                :view_environments,
-                :view_architectures,
-                :view_ptables,
-                :view_globals,
-                :view_external_variables,
-                :view_authenticators,
-                :access_settings,
-                :access_dashboard,
-                :view_reports,
-                :view_facts,
-                :view_smart_proxies,
-                :view_subnets,
-                :view_statistics,
-                :view_organizations,
-                :view_usergroups,
-                :view_users,
-                :view_audit_logs]
-            end
-
-            siteman = Role.where(:name => "Site manager").empty? ? Role.create(:name => "Site manager") : Role.where(:name => "Site manager")[0]
-            if reset or siteman.permissions.empty?
-              siteman.update_attribute :permissions, [ :view_architectures,
-                :view_audit_logs,
-                :view_authenticators,
-                :access_dashboard,
-                :view_domains,
-                :view_environments,
-                :import_environments,
-                :view_external_variables,
-                :create_external_variables,
-                :edit_external_variables,
-                :destroy_external_variables,
-                :view_facts,
-                :view_globals,
-                :view_hostgroups,
-                :view_hosts,
-                :view_smart_proxies_puppetca,
-                :view_smart_proxies_autosign,
-                :create_hosts,
-                :edit_hosts,
-                :destroy_hosts,
-                :build_hosts,
-                :view_media,
-                :create_media,
-                :edit_media,
-                :destroy_media,
-                :view_models,
-                :view_operatingsystems,
-                :view_ptables,
-                :view_puppetclasses,
-                :import_puppetclasses,
-                :view_reports,
-                :destroy_reports,
-                :access_settings,
-                :view_smart_proxies,
-                :edit_smart_proxies,
-                :view_subnets,
-                :edit_subnets,
-                :view_statistics,
-                :view_usergroups,
-                :create_usergroups,
-                :edit_usergroups,
-                :destroy_usergroups,
-                :view_users,
-                :edit_users]
-            end
-            if reset or Role.default_user.permissions.empty?
-              Role.default_user.update_attribute :permissions, [:view_hosts,
-                :view_puppetclasses,
-                :view_hostgroups,
-                :view_domains,
-                :view_operatingsystems,
-                :view_media,
-                :view_models,
-                :view_environments,
-                :view_architectures,
-                :view_ptables,
-                :view_globals,
-                :view_external_variables,
-                :view_authenticators,
-                :access_settings,
-                :access_dashboard,
-                :view_reports,
-                :view_subnets,
-                :view_facts,
-                :view_locations,
-                :view_organizations,
-                :view_statistics]
-            end
-            if reset or Role.anonymous.permissions.empty?
-              Role.anonymous.update_attribute :permissions, [:view_hosts, :view_bookmarks, :view_tasks]
-            end
-          end
-          true
-        end
-      end
-    end
-  end
-end
diff --git a/lib/foreman/exception.rb b/lib/foreman/exception.rb
index 35ef4da7d..6f0e8f5a6 100644
--- a/lib/foreman/exception.rb
+++ b/lib/foreman/exception.rb
@@ -42,4 +42,7 @@ module Foreman
     end
   end
 
+  class CyclicGraphException < ::ActiveRecord::RecordInvalid
+  end
+
 end
diff --git a/lib/tasks/backup.rake b/lib/tasks/backup.rake
new file mode 100644
index 000000000..7dbb37c04
--- /dev/null
+++ b/lib/tasks/backup.rake
@@ -0,0 +1,75 @@
+namespace :db do
+  desc "Make a dump of your database"
+  task :dump => :environment do
+    config      = Rails.configuration.database_configuration[Rails.env]
+    backup_name = "foreman.#{Time.now.to_i}.sql"
+    case config['adapter']
+    when 'mysql', 'mysql2'
+      mysql_dump(backup_name, config)
+    when 'postgresql'
+      postgres_dump(backup_name, config)
+    when 'sqlite'
+      backup_name = "foreman.#{Time.now.to_i}.sqlite_copy"
+      sqlite_dump(backup_name, config)
+    else
+      puts 'Your database is not supported by Foreman.' and exit(1)
+    end
+
+    puts "Backup in file #{backup_name}"
+  end
+
+  def mysql_dump(name, config)
+    cmd = "mysqldump --opt #{config['database']} -u #{config['username']} "
+    cmd += " -p#{config['password']} " if config['password'].present?
+    cmd += " -h #{config['host']} "    if config['host'].present?
+    cmd += " -P #{config['port']} "    if config['port'].present?
+    cmd += " > #{name}"
+    system(cmd)
+  end
+
+  def postgres_dump(name, config)
+    cmd = "pg_dump -Fc #{config['database']} -U #{config['username']} "
+    cmd += " -h #{config['host']} "    if config['host'].present?
+    cmd += " -p #{config['port']} "    if config['port'].present?
+    cmd += " > #{name}"
+    system({'PGPASSWORD' => config['password']}, cmd)
+  end
+
+  def sqlite_dump(name, config)
+    FileUtils.cp config['database'], name
+  end
+
+  desc 'Import your database dump'
+  task :import_dump => :environment do
+    puts "Run this task with a file argument with the location of your db dump,
+          'rake db:import_dump file=DBDUMPLOCATION" and return unless ENV['file']
+    config = Rails.configuration.database_configuration[Rails.env]
+
+    case config['adapter']
+    when 'mysql', 'mysql2'
+      mysql_import(ENV['file'], config)
+    when 'postgresql'
+      postgres_import(ENV['file'], config)
+    else
+      puts 'Your database dump cannot be imported by Foreman.' and exit(1)
+    end
+  end
+
+  def mysql_import(file, config)
+    cmd = "mysql #{config['database']} -u #{config['username']} "
+    cmd += " -p#{config['password']} " if config['password'].present?
+    cmd += " -h #{config['host']} "    if config['host'].present?
+    cmd += " -P #{config['port']} "    if config['port'].present?
+    cmd += " < #{file}"
+    system(cmd)
+  end
+
+  def postgres_import(file, config)
+    cmd = "pg_restore -d #{config['database']} -U #{config['username']} "
+    cmd += " -h #{config['host']} " if config['host'].present?
+    cmd += " -p #{config['port']} " if config['port'].present?
+    cmd += " #{file}"
+    system({'PGPASSWORD' => config['password']}, cmd)
+    system(cmd)
+  end
+end
diff --git a/lib/tasks/fix_cache.rake b/lib/tasks/fix_cache.rake
new file mode 100644
index 000000000..8244f80e7
--- /dev/null
+++ b/lib/tasks/fix_cache.rake
@@ -0,0 +1,22 @@
+desc 'Fix user groups and authorization cache by removing all cached records and recreating them'
+task :fix_db_cache => :environment do
+  puts 'Recreating cache'
+  CacheManager.recache!
+end
+
+namespace :fix_db_cache do
+  task :delete_old_cache do
+    CacheManager.delete_old_permission_cache
+    puts "Old cached records were deleted"
+  end
+
+  task :create_new_cache do
+    CacheManager.create_new_permission_cache
+    puts "New cached records were saved"
+  end
+
+  task :cache_filter_searches do
+    CacheManager.create_new_filter_cache
+    puts "Filters cache saved"
+  end
+end
diff --git a/lib/tasks/load_default_data.rake b/lib/tasks/load_default_data.rake
deleted file mode 100644
index 76570d607..000000000
--- a/lib/tasks/load_default_data.rake
+++ /dev/null
@@ -1,16 +0,0 @@
-desc 'Load Foreman default configuration data.'
-
-namespace :foreman do
-  task :load_default_data => :environment do
-
-    begin
-      Foreman::DefaultData::Loader.load(true)
-      puts "Default configuration data reloaded."
-    rescue Foreman::DefaultData::DataAlreadyLoaded => error
-      puts error
-    rescue => error
-      puts "Error: " + error
-      puts "Default configuration data was not loaded."
-    end
-  end
-end
diff --git a/test/factories/architecture.rb b/test/factories/architecture.rb
new file mode 100644
index 000000000..e49367500
--- /dev/null
+++ b/test/factories/architecture.rb
@@ -0,0 +1,5 @@
+FactoryGirl.define do
+  factory :architecture do
+    sequence(:name) {|n| "arm#{n}" }
+  end
+end
diff --git a/test/factories/domain.rb b/test/factories/domain.rb
new file mode 100644
index 000000000..0f9b0d515
--- /dev/null
+++ b/test/factories/domain.rb
@@ -0,0 +1,6 @@
+FactoryGirl.define do
+  factory :domain do
+    sequence(:name) {|n| "example#{n}.com" }
+    fullname { |n| n.name }
+  end
+end
diff --git a/test/factories/taxonomies.rb b/test/factories/taxonomies.rb
new file mode 100644
index 000000000..afa65da8d
--- /dev/null
+++ b/test/factories/taxonomies.rb
@@ -0,0 +1,9 @@
+FactoryGirl.define do
+  factory :organization do
+    sequence(:name) { |n| "org#{n}" }
+  end
+
+  factory :location do
+    sequence(:name) { |n| "loc#{n}" }
+  end
+end
diff --git a/test/factories/user_related.rb b/test/factories/user_related.rb
new file mode 100644
index 000000000..783a6e073
--- /dev/null
+++ b/test/factories/user_related.rb
@@ -0,0 +1,80 @@
+FactoryGirl.define do
+  factory :usergroup do
+    sequence(:name) {|n| "usergroup#{n}" }
+  end
+
+  factory :user do
+    auth_source { AuthSourceInternal.first }
+    password 'password'
+    sequence(:login) {|n| "user#{n}" }
+
+    trait :with_mail do
+      sequence(:mail) {|n| "email#{n}@example.com" }
+    end
+  end
+
+  factory :permission do
+    sequence(:name) {|n| "view_#{n}" }
+    resource_type nil
+
+    trait :host do
+      resource_type 'Host'
+    end
+
+    trait :domain do
+      resource_type 'Domain'
+    end
+
+    trait :architecture do
+      resource_type 'Architecture'
+    end
+  end
+
+  factory :role do
+    sequence(:name) {|n| "role #{n}" }
+    builtin 0
+  end
+
+  factory :user_role do
+    role { FactoryGirl.create :role }
+
+    factory :user_user_role do
+      owner { FactoryGirl.create :user }
+    end
+
+    factory :user_group_user_role do
+      owner { FactoryGirl.create :usergroup }
+    end
+  end
+
+  factory :usergroup_member do
+    usergroup { FactoryGirl.create :usergroup }
+
+    factory :user_usergroup_member do
+      member { FactoryGirl.create :user }
+    end
+
+    factory :usergroup_usergroup_member do
+      member { FactoryGirl.create :usergroup }
+    end
+  end
+
+  factory :filter do
+    search nil
+    role { FactoryGirl.create :role }
+    permissions { [ FactoryGirl.create(:permission, :host) ] }
+
+    trait :on_name_all do
+      search 'name ~ *'
+    end
+
+    trait :on_name_starting_with_a do
+      search 'name ~ a*'
+    end
+
+    trait :on_name_starting_with_b do
+      search 'name ~ b*'
+    end
+  end
+
+end
diff --git a/test/factories/usergroups.rb b/test/factories/usergroups.rb
deleted file mode 100644
index f5e3ac18e..000000000
--- a/test/factories/usergroups.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-FactoryGirl.define do
-  factory :usergroup do
-    sequence(:name) {|n| "usergroup#{n}" }
-  end
-end
diff --git a/test/fixtures/filterings.yml b/test/fixtures/filterings.yml
new file mode 100644
index 000000000..c362ac5a8
--- /dev/null
+++ b/test/fixtures/filterings.yml
@@ -0,0 +1,408 @@
+manager_1_0:
+  filter: manager_1
+  permission: view_architectures
+manager_1_1:
+  filter: manager_1
+  permission: create_architectures
+manager_1_2:
+  filter: manager_1
+  permission: edit_architectures
+manager_1_3:
+  filter: manager_1
+  permission: destroy_architectures
+manager_2_0:
+  filter: manager_2
+  permission: view_authenticators
+manager_2_1:
+  filter: manager_2
+  permission: create_authenticators
+manager_2_2:
+  filter: manager_2
+  permission: edit_authenticators
+manager_2_3:
+  filter: manager_2
+  permission: destroy_authenticators
+manager_3_0:
+  filter: manager_3
+  permission: view_environments
+manager_3_1:
+  filter: manager_3
+  permission: create_environments
+manager_3_2:
+  filter: manager_3
+  permission: edit_environments
+manager_3_3:
+  filter: manager_3
+  permission: destroy_environments
+manager_3_4:
+  filter: manager_3
+  permission: import_environments
+manager_4_0:
+  filter: manager_4
+  permission: view_external_variables
+manager_4_1:
+  filter: manager_4
+  permission: create_external_variables
+manager_4_2:
+  filter: manager_4
+  permission: edit_external_variables
+manager_4_3:
+  filter: manager_4
+  permission: destroy_external_variables
+manager_5_0:
+  filter: manager_5
+  permission: view_domains
+manager_5_1:
+  filter: manager_5
+  permission: create_domains
+manager_5_2:
+  filter: manager_5
+  permission: edit_domains
+manager_5_3:
+  filter: manager_5
+  permission: destroy_domains
+manager_6_0:
+  filter: manager_6
+  permission: view_globals
+manager_6_1:
+  filter: manager_6
+  permission: create_globals
+manager_6_2:
+  filter: manager_6
+  permission: edit_globals
+manager_6_3:
+  filter: manager_6
+  permission: destroy_globals
+manager_7_0:
+  filter: manager_7
+  permission: view_hostgroups
+manager_7_1:
+  filter: manager_7
+  permission: create_hostgroups
+manager_7_2:
+  filter: manager_7
+  permission: edit_hostgroups
+manager_7_3:
+  filter: manager_7
+  permission: destroy_hostgroups
+manager_8_0:
+  filter: manager_8
+  permission: view_hosts
+manager_8_1:
+  filter: manager_8
+  permission: create_hosts
+manager_8_2:
+  filter: manager_8
+  permission: edit_hosts
+manager_8_3:
+  filter: manager_8
+  permission: destroy_hosts
+manager_9_0:
+  filter: manager_9
+  permission: view_media
+manager_9_1:
+  filter: manager_9
+  permission: create_media
+manager_9_2:
+  filter: manager_9
+  permission: edit_media
+manager_9_3:
+  filter: manager_9
+  permission: destroy_media
+manager_10_0:
+  filter: manager_10
+  permission: view_models
+manager_10_1:
+  filter: manager_10
+  permission: create_models
+manager_10_2:
+  filter: manager_10
+  permission: edit_models
+manager_10_3:
+  filter: manager_10
+  permission: destroy_models
+manager_11_0:
+  filter: manager_11
+  permission: view_operatingsystems
+manager_11_1:
+  filter: manager_11
+  permission: create_operatingsystems
+manager_11_2:
+  filter: manager_11
+  permission: edit_operatingsystems
+manager_11_3:
+  filter: manager_11
+  permission: destroy_operatingsystems
+manager_12_0:
+  filter: manager_12
+  permission: view_ptables
+manager_12_1:
+  filter: manager_12
+  permission: create_ptables
+manager_12_2:
+  filter: manager_12
+  permission: edit_ptables
+manager_12_3:
+  filter: manager_12
+  permission: destroy_ptables
+manager_13_0:
+  filter: manager_13
+  permission: view_puppetclasses
+manager_13_1:
+  filter: manager_13
+  permission: create_puppetclasses
+manager_13_2:
+  filter: manager_13
+  permission: edit_puppetclasses
+manager_13_3:
+  filter: manager_13
+  permission: destroy_puppetclasses
+manager_13_4:
+  filter: manager_13
+  permission: import_puppetclasses
+manager_14_0:
+  filter: manager_14
+  permission: view_usergroups
+manager_14_1:
+  filter: manager_14
+  permission: create_usergroups
+manager_14_2:
+  filter: manager_14
+  permission: edit_usergroups
+manager_14_3:
+  filter: manager_14
+  permission: destroy_usergroups
+manager_15_0:
+  filter: manager_15
+  permission: view_users
+manager_15_1:
+  filter: manager_15
+  permission: create_users
+manager_15_2:
+  filter: manager_15
+  permission: edit_users
+manager_15_3:
+  filter: manager_15
+  permission: destroy_users
+manager_16_0:
+  filter: manager_16
+  permission: access_settings
+manager_16_1:
+  filter: manager_16
+  permission: access_dashboard
+manager_16_2:
+  filter: manager_16
+  permission: view_statistics
+manager_17_0:
+  filter: manager_17
+  permission: view_reports
+manager_17_1:
+  filter: manager_17
+  permission: destroy_reports
+manager_18_0:
+  filter: manager_18
+  permission: view_facts
+manager_19_0:
+  filter: manager_19
+  permission: view_audit_logs
+manager_20_0:
+  filter: manager_20
+  permission: view_locations
+manager_20_1:
+  filter: manager_20
+  permission: edit_locations
+manager_20_2:
+  filter: manager_20
+  permission: create_locations
+manager_20_3:
+  filter: manager_20
+  permission: destroy_locations
+manager_21_0:
+  filter: manager_21
+  permission: view_organizations
+manager_21_1:
+  filter: manager_21
+  permission: edit_organizations
+manager_21_2:
+  filter: manager_21
+  permission: create_organizations
+manager_21_3:
+  filter: manager_21
+  permission: destroy_organizations
+manager_22_0:
+  filter: manager_22
+  permission: edit_classes
+edit_partition_tables_1_0:
+  filter: edit_partition_tables_1
+  permission: view_ptables
+edit_partition_tables_1_1:
+  filter: edit_partition_tables_1
+  permission: create_ptables
+edit_partition_tables_1_2:
+  filter: edit_partition_tables_1
+  permission: edit_ptables
+edit_partition_tables_1_3:
+  filter: edit_partition_tables_1
+  permission: destroy_ptables
+view_hosts_1_0:
+  filter: view_hosts_1
+  permission: view_hosts
+edit_hosts_1_0:
+  filter: edit_hosts_1
+  permission: edit_hosts
+viewer_1_0:
+  filter: viewer_1
+  permission: view_architectures
+viewer_2_0:
+  filter: viewer_2
+  permission: view_audit_logs
+viewer_3_0:
+  filter: viewer_3
+  permission: view_authenticators
+viewer_4_0:
+  filter: viewer_4
+  permission: access_dashboard
+viewer_4_1:
+  filter: viewer_4
+  permission: access_settings
+viewer_4_2:
+  filter: viewer_4
+  permission: view_statistics
+viewer_5_0:
+  filter: viewer_5
+  permission: view_domains
+viewer_6_0:
+  filter: viewer_6
+  permission: view_environments
+viewer_7_0:
+  filter: viewer_7
+  permission: view_external_variables
+viewer_8_0:
+  filter: viewer_8
+  permission: view_facts
+viewer_9_0:
+  filter: viewer_9
+  permission: view_globals
+viewer_10_0:
+  filter: viewer_10
+  permission: view_hostgroups
+viewer_11_0:
+  filter: viewer_11
+  permission: view_hosts
+viewer_12_0:
+  filter: viewer_12
+  permission: view_locations
+viewer_13_0:
+  filter: viewer_13
+  permission: view_media
+viewer_14_0:
+  filter: viewer_14
+  permission: view_models
+viewer_15_0:
+  filter: viewer_15
+  permission: view_operatingsystems
+viewer_16_0:
+  filter: viewer_16
+  permission: view_ptables
+viewer_17_0:
+  filter: viewer_17
+  permission: view_puppetclasses
+viewer_18_0:
+  filter: viewer_18
+  permission: view_reports
+viewer_19_0:
+  filter: viewer_19
+  permission: view_organizations
+viewer_20_0:
+  filter: viewer_20
+  permission: view_usergroups
+viewer_21_0:
+  filter: viewer_21
+  permission: view_users
+default_user_1_0:
+  filter: default_user_1
+  permission: view_hosts
+default_user_2_0:
+  filter: default_user_2
+  permission: view_puppetclasses
+default_user_3_0:
+  filter: default_user_3
+  permission: view_hostgroups
+default_user_4_0:
+  filter: default_user_4
+  permission: view_domains
+default_user_5_0:
+  filter: default_user_5
+  permission: view_operatingsystems
+default_user_6_0:
+  filter: default_user_6
+  permission: view_media
+default_user_7_0:
+  filter: default_user_7
+  permission: view_models
+default_user_8_0:
+  filter: default_user_8
+  permission: view_environments
+default_user_9_0:
+  filter: default_user_9
+  permission: view_architectures
+default_user_10_0:
+  filter: default_user_10
+  permission: view_ptables
+default_user_11_0:
+  filter: default_user_11
+  permission: view_globals
+default_user_12_0:
+  filter: default_user_12
+  permission: view_external_variables
+default_user_13_0:
+  filter: default_user_13
+  permission: view_authenticators
+default_user_14_0:
+  filter: default_user_14
+  permission: access_settings
+default_user_14_1:
+  filter: default_user_14
+  permission: access_dashboard
+default_user_14_2:
+  filter: default_user_14
+  permission: view_statistics
+default_user_15_0:
+  filter: default_user_15
+  permission: view_reports
+default_user_16_0:
+  filter: default_user_16
+  permission: view_facts
+anonymous_1_0:
+  filter: anonymous_1
+  permission: view_hosts
+destroy_hosts_1_0:
+  filter: destroy_hosts_1
+  permission: destroy_hosts
+create_hosts_1_0:
+  filter: create_hosts_1
+  permission: create_hosts
+view_compute_resources_1_0:
+  filter: view_compute_resources_1
+  permission: view_compute_resources
+view_compute_resources_1_1:
+  filter: view_compute_resources_1
+  permission: create_compute_resources
+view_compute_resources_1_2:
+  filter: view_compute_resources_1
+  permission: edit_compute_resources
+view_compute_resources_1_3:
+  filter: view_compute_resources_1
+  permission: destroy_compute_resources
+crud_hosts_1_0:
+  filter: crud_hosts_1
+  permission: create_hosts
+crud_hosts_1_1:
+  filter: crud_hosts_1
+  permission: edit_hosts
+crud_hosts_1_2:
+  filter: crud_hosts_1
+  permission: destroy_hosts
+crud_hosts_1_3:
+  filter: crud_hosts_1
+  permission: view_hosts
diff --git a/test/fixtures/filters.yml b/test/fixtures/filters.yml
new file mode 100644
index 000000000..33e6810ed
--- /dev/null
+++ b/test/fixtures/filters.yml
@@ -0,0 +1,134 @@
+manager_1:
+  role_id: 1
+manager_2:
+  role_id: 1
+manager_3:
+  role_id: 1
+manager_4:
+  role_id: 1
+manager_5:
+  role_id: 1
+manager_6:
+  role_id: 1
+manager_7:
+  role_id: 1
+manager_8:
+  role_id: 1
+manager_9:
+  role_id: 1
+manager_10:
+  role_id: 1
+manager_11:
+  role_id: 1
+manager_12:
+  role_id: 1
+manager_13:
+  role_id: 1
+manager_14:
+  role_id: 1
+manager_15:
+  role_id: 1
+manager_16:
+  role_id: 1
+manager_17:
+  role_id: 1
+manager_18:
+  role_id: 1
+manager_19:
+  role_id: 1
+manager_20:
+  role_id: 1
+manager_21:
+  role_id: 1
+manager_22:
+  role_id: 1
+edit_partition_tables_1:
+  role_id: 2
+view_hosts_1:
+  role_id: 3
+edit_hosts_1:
+  role_id: 4
+viewer_1:
+  role_id: 5
+viewer_2:
+  role_id: 5
+viewer_3:
+  role_id: 5
+viewer_4:
+  role_id: 5
+viewer_5:
+  role_id: 5
+viewer_6:
+  role_id: 5
+viewer_7:
+  role_id: 5
+viewer_8:
+  role_id: 5
+viewer_9:
+  role_id: 5
+viewer_10:
+  role_id: 5
+viewer_11:
+  role_id: 5
+viewer_12:
+  role_id: 5
+viewer_13:
+  role_id: 5
+viewer_14:
+  role_id: 5
+viewer_15:
+  role_id: 5
+viewer_16:
+  role_id: 5
+viewer_17:
+  role_id: 5
+viewer_18:
+  role_id: 5
+viewer_19:
+  role_id: 5
+viewer_20:
+  role_id: 5
+viewer_21:
+  role_id: 5
+default_user_1:
+  role_id: 6
+default_user_2:
+  role_id: 6
+default_user_3:
+  role_id: 6
+default_user_4:
+  role_id: 6
+default_user_5:
+  role_id: 6
+default_user_6:
+  role_id: 6
+default_user_7:
+  role_id: 6
+default_user_8:
+  role_id: 6
+default_user_9:
+  role_id: 6
+default_user_10:
+  role_id: 6
+default_user_11:
+  role_id: 6
+default_user_12:
+  role_id: 6
+default_user_13:
+  role_id: 6
+default_user_14:
+  role_id: 6
+default_user_15:
+  role_id: 6
+default_user_16:
+  role_id: 6
+anonymous_1:
+  role_id: 7
+destroy_hosts_1:
+  role_id: 8
+create_hosts_1:
+  role_id: 9
+view_compute_resources_1:
+  role_id: 11
+crud_hosts_1:
+  role_id: 12
diff --git a/test/fixtures/permissions.yml b/test/fixtures/permissions.yml
new file mode 100644
index 000000000..546762cd9
--- /dev/null
+++ b/test/fixtures/permissions.yml
@@ -0,0 +1,676 @@
+---
+  view_architectures:
+    name: view_architectures
+    resource_type: Architecture
+    created_at: "2013-12-04 08:41:04.321867"
+    updated_at: "2013-12-04 08:41:04.321867"
+  create_architectures:
+    name: create_architectures
+    resource_type: Architecture
+    created_at: "2013-12-04 08:41:04.357689"
+    updated_at: "2013-12-04 08:41:04.357689"
+  edit_architectures:
+    name: edit_architectures
+    resource_type: Architecture
+    created_at: "2013-12-04 08:41:04.365371"
+    updated_at: "2013-12-04 08:41:04.365371"
+  destroy_architectures:
+    name: destroy_architectures
+    resource_type: Architecture
+    created_at: "2013-12-04 08:41:04.373182"
+    updated_at: "2013-12-04 08:41:04.373182"
+  view_authenticators:
+    name: view_authenticators
+    resource_type: AuthSourceLdap
+    created_at: "2013-12-04 08:41:04.389686"
+    updated_at: "2013-12-04 08:41:04.389686"
+  create_authenticators:
+    name: create_authenticators
+    resource_type: AuthSourceLdap
+    created_at: "2013-12-04 08:41:04.398376"
+    updated_at: "2013-12-04 08:41:04.398376"
+  edit_authenticators:
+    name: edit_authenticators
+    resource_type: AuthSourceLdap
+    created_at: "2013-12-04 08:41:04.40647"
+    updated_at: "2013-12-04 08:41:04.40647"
+  destroy_authenticators:
+    name: destroy_authenticators
+    resource_type: AuthSourceLdap
+    created_at: "2013-12-04 08:41:04.414794"
+    updated_at: "2013-12-04 08:41:04.414794"
+  view_bookmarks:
+    name: view_bookmarks
+    resource_type: Bookmark
+    created_at: "2013-12-04 08:41:04.423092"
+    updated_at: "2013-12-04 08:41:04.423092"
+  create_bookmarks:
+    name: create_bookmarks
+    resource_type: Bookmark
+    created_at: "2013-12-04 08:41:04.431655"
+    updated_at: "2013-12-04 08:41:04.431655"
+  edit_bookmarks:
+    name: edit_bookmarks
+    resource_type: Bookmark
+    created_at: "2013-12-04 08:41:04.439632"
+    updated_at: "2013-12-04 08:41:04.439632"
+  destroy_bookmarks:
+    name: destroy_bookmarks
+    resource_type: Bookmark
+    created_at: "2013-12-04 08:41:04.448264"
+    updated_at: "2013-12-04 08:41:04.448264"
+  view_compute_resources:
+    name: view_compute_resources
+    resource_type: ComputeResource
+    created_at: "2013-12-04 08:41:04.456196"
+    updated_at: "2013-12-04 08:41:04.456196"
+  create_compute_resources:
+    name: create_compute_resources
+    resource_type: ComputeResource
+    created_at: "2013-12-04 08:41:04.464913"
+    updated_at: "2013-12-04 08:41:04.464913"
+  edit_compute_resources:
+    name: edit_compute_resources
+    resource_type: ComputeResource
+    created_at: "2013-12-04 08:41:04.473288"
+    updated_at: "2013-12-04 08:41:04.473288"
+  destroy_compute_resources:
+    name: destroy_compute_resources
+    resource_type: ComputeResource
+    created_at: "2013-12-04 08:41:04.481549"
+    updated_at: "2013-12-04 08:41:04.481549"
+  view_templates:
+    name: view_templates
+    resource_type: ConfigTemplate
+    created_at: "2013-12-04 08:41:04.540648"
+    updated_at: "2013-12-04 08:41:04.540648"
+  create_templates:
+    name: create_templates
+    resource_type: ConfigTemplate
+    created_at: "2013-12-04 08:41:04.548631"
+    updated_at: "2013-12-04 08:41:04.548631"
+  edit_templates:
+    name: edit_templates
+    resource_type: ConfigTemplate
+    created_at: "2013-12-04 08:41:04.556639"
+    updated_at: "2013-12-04 08:41:04.556639"
+  destroy_templates:
+    name: destroy_templates
+    resource_type: ConfigTemplate
+    created_at: "2013-12-04 08:41:04.564948"
+    updated_at: "2013-12-04 08:41:04.564948"
+  deploy_templates:
+    name: deploy_templates
+    resource_type: ConfigTemplate
+    created_at: "2013-12-04 08:41:04.573359"
+    updated_at: "2013-12-04 08:41:04.573359"
+  access_dashboard:
+    name: access_dashboard
+    resource_type:
+    created_at: "2013-12-04 08:41:04.581753"
+    updated_at: "2013-12-04 08:41:04.581753"
+  view_domains:
+    name: view_domains
+    resource_type: Domain
+    created_at: "2013-12-04 08:41:04.590726"
+    updated_at: "2013-12-04 08:41:04.590726"
+  create_domains:
+    name: create_domains
+    resource_type: Domain
+    created_at: "2013-12-04 08:41:04.598517"
+    updated_at: "2013-12-04 08:41:04.598517"
+  edit_domains:
+    name: edit_domains
+    resource_type: Domain
+    created_at: "2013-12-04 08:41:04.606731"
+    updated_at: "2013-12-04 08:41:04.606731"
+  destroy_domains:
+    name: destroy_domains
+    resource_type: Domain
+    created_at: "2013-12-04 08:41:04.615227"
+    updated_at: "2013-12-04 08:41:04.615227"
+  view_environments:
+    name: view_environments
+    resource_type: Environment
+    created_at: "2013-12-04 08:41:04.623403"
+    updated_at: "2013-12-04 08:41:04.623403"
+  create_environments:
+    name: create_environments
+    resource_type: Environment
+    created_at: "2013-12-04 08:41:04.631791"
+    updated_at: "2013-12-04 08:41:04.631791"
+  edit_environments:
+    name: edit_environments
+    resource_type: Environment
+    created_at: "2013-12-04 08:41:04.640671"
+    updated_at: "2013-12-04 08:41:04.640671"
+  destroy_environments:
+    name: destroy_environments
+    resource_type: Environment
+    created_at: "2013-12-04 08:41:04.648794"
+    updated_at: "2013-12-04 08:41:04.648794"
+  import_environments:
+    name: import_environments
+    resource_type: Environment
+    created_at: "2013-12-04 08:41:04.656654"
+    updated_at: "2013-12-04 08:41:04.656654"
+  view_external_variables:
+    name: view_external_variables
+    resource_type: LookupKey
+    created_at: "2013-12-04 08:41:04.664806"
+    updated_at: "2013-12-04 08:41:04.664806"
+  create_external_variables:
+    name: create_external_variables
+    resource_type: LookupKey
+    created_at: "2013-12-04 08:41:04.673323"
+    updated_at: "2013-12-04 08:41:04.673323"
+  edit_external_variables:
+    name: edit_external_variables
+    resource_type: LookupKey
+    created_at: "2013-12-04 08:41:04.681526"
+    updated_at: "2013-12-04 08:41:04.681526"
+  destroy_external_variables:
+    name: destroy_external_variables
+    resource_type: LookupKey
+    created_at: "2013-12-04 08:41:04.690155"
+    updated_at: "2013-12-04 08:41:04.690155"
+  view_globals:
+    name: view_globals
+    resource_type: CommonParameter
+    created_at: "2013-12-04 08:41:04.782155"
+    updated_at: "2013-12-04 08:41:04.782155"
+  create_globals:
+    name: create_globals
+    resource_type: CommonParameter
+    created_at: "2013-12-04 08:41:04.790219"
+    updated_at: "2013-12-04 08:41:04.790219"
+  edit_globals:
+    name: edit_globals
+    resource_type: CommonParameter
+    created_at: "2013-12-04 08:41:04.799011"
+    updated_at: "2013-12-04 08:41:04.799011"
+  destroy_globals:
+    name: destroy_globals
+    resource_type: CommonParameter
+    created_at: "2013-12-04 08:41:04.806569"
+    updated_at: "2013-12-04 08:41:04.806569"
+  create_params:
+    name: create_params
+    resource_type: Parameter
+    created_at: "2013-12-04 08:41:04.823716"
+    updated_at: "2013-12-04 08:41:04.823716"
+  edit_params:
+    name: edit_params
+    resource_type: Parameter
+    created_at: "2013-12-04 08:41:04.831888"
+    updated_at: "2013-12-04 08:41:04.831888"
+  destroy_params:
+    name: destroy_params
+    resource_type: Parameter
+    created_at: "2013-12-04 08:41:04.84007"
+    updated_at: "2013-12-04 08:41:04.84007"
+  view_hostgroups:
+    name: view_hostgroups
+    resource_type: Hostgroup
+    created_at: "2013-12-04 08:41:04.848067"
+    updated_at: "2013-12-04 08:41:04.848067"
+  create_hostgroups:
+    name: create_hostgroups
+    resource_type: Hostgroup
+    created_at: "2013-12-04 08:41:04.857019"
+    updated_at: "2013-12-04 08:41:04.857019"
+  edit_hostgroups:
+    name: edit_hostgroups
+    resource_type: Hostgroup
+    created_at: "2013-12-04 08:41:04.865372"
+    updated_at: "2013-12-04 08:41:04.865372"
+  destroy_hostgroups:
+    name: destroy_hostgroups
+    resource_type: Hostgroup
+    created_at: "2013-12-04 08:41:04.873653"
+    updated_at: "2013-12-04 08:41:04.873653"
+  view_hosts:
+    name: view_hosts
+    resource_type: Host
+    created_at: "2013-12-04 08:41:04.882031"
+    updated_at: "2013-12-04 08:41:04.882031"
+  create_hosts:
+    name: create_hosts
+    resource_type: Host
+    created_at: "2013-12-04 08:41:04.88986"
+    updated_at: "2013-12-04 08:41:04.88986"
+  edit_hosts:
+    name: edit_hosts
+    resource_type: Host
+    created_at: "2013-12-04 08:41:04.898373"
+    updated_at: "2013-12-04 08:41:04.898373"
+  destroy_hosts:
+    name: destroy_hosts
+    resource_type: Host
+    created_at: "2013-12-04 08:41:04.907008"
+    updated_at: "2013-12-04 08:41:04.907008"
+  build_hosts:
+    name: build_hosts
+    resource_type: Host
+    created_at: "2013-12-04 08:41:04.915297"
+    updated_at: "2013-12-04 08:41:04.915297"
+  power_hosts:
+    name: power_hosts
+    resource_type: Host
+    created_at: "2013-12-04 08:41:04.923204"
+    updated_at: "2013-12-04 08:41:04.923204"
+  console_hosts:
+    name: console_hosts
+    resource_type: Host
+    created_at: "2013-12-04 08:41:04.931564"
+    updated_at: "2013-12-04 08:41:04.931564"
+  ipmi_boot:
+    name: ipmi_boot
+    resource_type: Host
+    created_at: "2013-12-04 08:41:04.940695"
+    updated_at: "2013-12-04 08:41:04.940695"
+  puppetrun_hosts:
+    name: puppetrun_hosts
+    resource_type: Host
+    created_at: "2013-12-04 08:41:04.962776"
+    updated_at: "2013-12-04 08:41:04.962776"
+  view_images:
+    name: view_images
+    resource_type: Image
+    created_at: "2013-12-04 08:41:04.982309"
+    updated_at: "2013-12-04 08:41:04.982309"
+  create_images:
+    name: create_images
+    resource_type: Image
+    created_at: "2013-12-04 08:41:04.990762"
+    updated_at: "2013-12-04 08:41:04.990762"
+  edit_images:
+    name: edit_images
+    resource_type: Image
+    created_at: "2013-12-04 08:41:04.998531"
+    updated_at: "2013-12-04 08:41:04.998531"
+  destroy_images:
+    name: destroy_images
+    resource_type: Image
+    created_at: "2013-12-04 08:41:05.00707"
+    updated_at: "2013-12-04 08:41:05.00707"
+  view_locations:
+    name: view_locations
+    resource_type: Location
+    created_at: "2013-12-04 08:41:05.015645"
+    updated_at: "2013-12-04 08:41:05.015645"
+  create_locations:
+    name: create_locations
+    resource_type: Location
+    created_at: "2013-12-04 08:41:05.024063"
+    updated_at: "2013-12-04 08:41:05.024063"
+  edit_locations:
+    name: edit_locations
+    resource_type: Location
+    created_at: "2013-12-04 08:41:05.032154"
+    updated_at: "2013-12-04 08:41:05.032154"
+  destroy_locations:
+    name: destroy_locations
+    resource_type: Location
+    created_at: "2013-12-04 08:41:05.040216"
+    updated_at: "2013-12-04 08:41:05.040216"
+  assign_locations:
+    name: assign_locations
+    resource_type: Location
+    created_at: "2013-12-04 08:41:05.048684"
+    updated_at: "2013-12-04 08:41:05.048684"
+  view_media:
+    name: view_media
+    resource_type: Medium
+    created_at: "2013-12-04 08:41:05.057086"
+    updated_at: "2013-12-04 08:41:05.057086"
+  create_media:
+    name: create_media
+    resource_type: Medium
+    created_at: "2013-12-04 08:41:05.065732"
+    updated_at: "2013-12-04 08:41:05.065732"
+  edit_media:
+    name: edit_media
+    resource_type: Medium
+    created_at: "2013-12-04 08:41:05.073582"
+    updated_at: "2013-12-04 08:41:05.073582"
+  destroy_media:
+    name: destroy_media
+    resource_type: Medium
+    created_at: "2013-12-04 08:41:05.0823"
+    updated_at: "2013-12-04 08:41:05.0823"
+  view_models:
+    name: view_models
+    resource_type: Model
+    created_at: "2013-12-04 08:41:05.090697"
+    updated_at: "2013-12-04 08:41:05.090697"
+  create_models:
+    name: create_models
+    resource_type: Model
+    created_at: "2013-12-04 08:41:05.098969"
+    updated_at: "2013-12-04 08:41:05.098969"
+  edit_models:
+    name: edit_models
+    resource_type: Model
+    created_at: "2013-12-04 08:41:05.107244"
+    updated_at: "2013-12-04 08:41:05.107244"
+  destroy_models:
+    name: destroy_models
+    resource_type: Model
+    created_at: "2013-12-04 08:41:05.11531"
+    updated_at: "2013-12-04 08:41:05.11531"
+  view_operatingsystems:
+    name: view_operatingsystems
+    resource_type: Operatingsystem
+    created_at: "2013-12-04 08:41:05.123697"
+    updated_at: "2013-12-04 08:41:05.123697"
+  create_operatingsystems:
+    name: create_operatingsystems
+    resource_type: Operatingsystem
+    created_at: "2013-12-04 08:41:05.132086"
+    updated_at: "2013-12-04 08:41:05.132086"
+  edit_operatingsystems:
+    name: edit_operatingsystems
+    resource_type: Operatingsystem
+    created_at: "2013-12-04 08:41:05.140537"
+    updated_at: "2013-12-04 08:41:05.140537"
+  destroy_operatingsystems:
+    name: destroy_operatingsystems
+    resource_type: Operatingsystem
+    created_at: "2013-12-04 08:41:05.148799"
+    updated_at: "2013-12-04 08:41:05.148799"
+  view_organizations:
+    name: view_organizations
+    resource_type: Organization
+    created_at: "2013-12-04 08:41:05.235498"
+    updated_at: "2013-12-04 08:41:05.235498"
+  create_organizations:
+    name: create_organizations
+    resource_type: Organization
+    created_at: "2013-12-04 08:41:05.248483"
+    updated_at: "2013-12-04 08:41:05.248483"
+  edit_organizations:
+    name: edit_organizations
+    resource_type: Organization
+    created_at: "2013-12-04 08:41:05.25688"
+    updated_at: "2013-12-04 08:41:05.25688"
+  destroy_organizations:
+    name: destroy_organizations
+    resource_type: Organization
+    created_at: "2013-12-04 08:41:05.26538"
+    updated_at: "2013-12-04 08:41:05.26538"
+  assign_organizations:
+    name: assign_organizations
+    resource_type: Organization
+    created_at: "2013-12-04 08:41:05.273221"
+    updated_at: "2013-12-04 08:41:05.273221"
+  view_ptables:
+    name: view_ptables
+    resource_type: Ptable
+    created_at: "2013-12-04 08:41:05.281766"
+    updated_at: "2013-12-04 08:41:05.281766"
+  create_ptables:
+    name: create_ptables
+    resource_type: Ptable
+    created_at: "2013-12-04 08:41:05.482882"
+    updated_at: "2013-12-04 08:41:05.482882"
+  edit_ptables:
+    name: edit_ptables
+    resource_type: Ptable
+    created_at: "2013-12-04 08:41:05.683037"
+    updated_at: "2013-12-04 08:41:05.683037"
+  destroy_ptables:
+    name: destroy_ptables
+    resource_type: Ptable
+    created_at: "2013-12-04 08:41:05.762135"
+    updated_at: "2013-12-04 08:41:05.762135"
+  view_puppetclasses:
+    name: view_puppetclasses
+    resource_type: Puppetclass
+    created_at: "2013-12-04 08:41:06.01639"
+    updated_at: "2013-12-04 08:41:06.01639"
+  create_puppetclasses:
+    name: create_puppetclasses
+    resource_type: Puppetclass
+    created_at: "2013-12-04 08:41:06.074428"
+    updated_at: "2013-12-04 08:41:06.074428"
+  edit_puppetclasses:
+    name: edit_puppetclasses
+    resource_type: Puppetclass
+    created_at: "2013-12-04 08:41:06.082891"
+    updated_at: "2013-12-04 08:41:06.082891"
+  destroy_puppetclasses:
+    name: destroy_puppetclasses
+    resource_type: Puppetclass
+    created_at: "2013-12-04 08:41:06.091247"
+    updated_at: "2013-12-04 08:41:06.091247"
+  import_puppetclasses:
+    name: import_puppetclasses
+    resource_type: Puppetclass
+    created_at: "2013-12-04 08:41:06.099309"
+    updated_at: "2013-12-04 08:41:06.099309"
+  view_reports:
+    name: view_reports
+    resource_type: Report
+    created_at: "2013-12-04 08:41:06.107515"
+    updated_at: "2013-12-04 08:41:06.107515"
+  destroy_reports:
+    name: destroy_reports
+    resource_type: Report
+    created_at: "2013-12-04 08:41:06.116056"
+    updated_at: "2013-12-04 08:41:06.116056"
+  upload_reports:
+    name: upload_reports
+    resource_type: Report
+    created_at: "2013-12-04 08:41:06.123783"
+    updated_at: "2013-12-04 08:41:06.123783"
+  access_settings:
+    name: access_settings
+    resource_type:
+    created_at: "2013-12-04 08:41:06.132528"
+    updated_at: "2013-12-04 08:41:06.132528"
+  view_smart_proxies:
+    name: view_smart_proxies
+    resource_type: SmartProxy
+    created_at: "2013-12-04 08:41:06.140564"
+    updated_at: "2013-12-04 08:41:06.140564"
+  create_smart_proxies:
+    name: create_smart_proxies
+    resource_type: SmartProxy
+    created_at: "2013-12-04 08:41:06.149029"
+    updated_at: "2013-12-04 08:41:06.149029"
+  edit_smart_proxies:
+    name: edit_smart_proxies
+    resource_type: SmartProxy
+    created_at: "2013-12-04 08:41:06.157364"
+    updated_at: "2013-12-04 08:41:06.157364"
+  destroy_smart_proxies:
+    name: destroy_smart_proxies
+    resource_type: SmartProxy
+    created_at: "2013-12-04 08:41:06.1657"
+    updated_at: "2013-12-04 08:41:06.1657"
+  view_smart_proxies_autosign:
+    name: view_smart_proxies_autosign
+    resource_type: SmartProxy
+    created_at: "2013-12-04 08:41:06.173638"
+    updated_at: "2013-12-04 08:41:06.173638"
+  create_smart_proxies_autosign:
+    name: create_smart_proxies_autosign
+    resource_type: SmartProxy
+    created_at: "2013-12-04 08:41:06.182425"
+    updated_at: "2013-12-04 08:41:06.182425"
+  destroy_smart_proxies_autosign:
+    name: destroy_smart_proxies_autosign
+    resource_type: SmartProxy
+    created_at: "2013-12-04 08:41:06.190274"
+    updated_at: "2013-12-04 08:41:06.190274"
+  view_smart_proxies_puppetca:
+    name: view_smart_proxies_puppetca
+    resource_type: SmartProxy
+    created_at: "2013-12-04 08:41:06.198169"
+    updated_at: "2013-12-04 08:41:06.198169"
+  edit_smart_proxies_puppetca:
+    name: edit_smart_proxies_puppetca
+    resource_type: SmartProxy
+    created_at: "2013-12-04 08:41:06.206812"
+    updated_at: "2013-12-04 08:41:06.206812"
+  destroy_smart_proxies_puppetca:
+    name: destroy_smart_proxies_puppetca
+    resource_type: SmartProxy
+    created_at: "2013-12-04 08:41:06.216154"
+    updated_at: "2013-12-04 08:41:06.216154"
+  view_statistics:
+    name: view_statistics
+    resource_type:
+    created_at: "2013-12-04 08:41:06.223528"
+    updated_at: "2013-12-04 08:41:06.223528"
+  view_subnets:
+    name: view_subnets
+    resource_type: Subnet
+    created_at: "2013-12-04 08:41:06.231467"
+    updated_at: "2013-12-04 08:41:06.231467"
+  create_subnets:
+    name: create_subnets
+    resource_type: Subnet
+    created_at: "2013-12-04 08:41:06.24055"
+    updated_at: "2013-12-04 08:41:06.24055"
+  edit_subnets:
+    name: edit_subnets
+    resource_type: Subnet
+    created_at: "2013-12-04 08:41:06.248888"
+    updated_at: "2013-12-04 08:41:06.248888"
+  destroy_subnets:
+    name: destroy_subnets
+    resource_type: Subnet
+    created_at: "2013-12-04 08:41:06.257199"
+    updated_at: "2013-12-04 08:41:06.257199"
+  import_subnets:
+    name: import_subnets
+    resource_type: Subnet
+    created_at: "2013-12-04 08:41:06.265758"
+    updated_at: "2013-12-04 08:41:06.265758"
+  view_tasks:
+    name: view_tasks
+    resource_type:
+    created_at: "2013-12-04 08:41:06.274449"
+    updated_at: "2013-12-04 08:41:06.274449"
+  view_trends:
+    name: view_trends
+    resource_type: Trend
+    created_at: "2013-12-04 08:41:06.282474"
+    updated_at: "2013-12-04 08:41:06.282474"
+  create_trends:
+    name: create_trends
+    resource_type: Trend
+    created_at: "2013-12-04 08:41:06.290854"
+    updated_at: "2013-12-04 08:41:06.290854"
+  edit_trends:
+    name: edit_trends
+    resource_type: Trend
+    created_at: "2013-12-04 08:41:06.299794"
+    updated_at: "2013-12-04 08:41:06.299794"
+  destroy_trends:
+    name: destroy_trends
+    resource_type: Trend
+    created_at: "2013-12-04 08:41:06.307322"
+    updated_at: "2013-12-04 08:41:06.307322"
+  update_trends:
+    name: update_trends
+    resource_type: Trend
+    created_at: "2013-12-04 08:41:06.315986"
+    updated_at: "2013-12-04 08:41:06.315986"
+  view_usergroups:
+    name: view_usergroups
+    resource_type: Usergroup
+    created_at: "2013-12-04 08:41:06.323534"
+    updated_at: "2013-12-04 08:41:06.323534"
+  create_usergroups:
+    name: create_usergroups
+    resource_type: Usergroup
+    created_at: "2013-12-04 08:41:06.332584"
+    updated_at: "2013-12-04 08:41:06.332584"
+  edit_usergroups:
+    name: edit_usergroups
+    resource_type: Usergroup
+    created_at: "2013-12-04 08:41:06.341266"
+    updated_at: "2013-12-04 08:41:06.341266"
+  destroy_usergroups:
+    name: destroy_usergroups
+    resource_type: Usergroup
+    created_at: "2013-12-04 08:41:06.34905"
+    updated_at: "2013-12-04 08:41:06.34905"
+  view_users:
+    name: view_users
+    resource_type: User
+    created_at: "2013-12-04 08:41:06.358291"
+    updated_at: "2013-12-04 08:41:06.358291"
+  create_users:
+    name: create_users
+    resource_type: User
+    created_at: "2013-12-04 08:41:06.366741"
+    updated_at: "2013-12-04 08:41:06.366741"
+  edit_users:
+    name: edit_users
+    resource_type: User
+    created_at: "2013-12-04 08:41:06.375061"
+    updated_at: "2013-12-04 08:41:06.375061"
+  destroy_users:
+    name: destroy_users
+    resource_type: User
+    created_at: "2013-12-04 08:41:06.38334"
+    updated_at: "2013-12-04 08:41:06.38334"
+  edit_classes:
+    name: edit_classes
+    resource_type: HostClass
+    created_at: "2013-12-04 08:41:04.815942"
+    updated_at: "2013-12-11 09:48:09.672833"
+  view_compute_resources_vms:
+    name: view_compute_resources_vms
+    resource_type: ComputeResource
+    created_at: "2013-12-11 11:23:54.965532"
+    updated_at: "2013-12-11 11:23:54.965532"
+  create_compute_resources_vms:
+    name: create_compute_resources_vms
+    resource_type: ComputeResource
+    created_at: "2013-12-11 11:23:54.972197"
+    updated_at: "2013-12-11 11:23:54.972197"
+  edit_compute_resources_vms:
+    name: edit_compute_resources_vms
+    resource_type: ComputeResource
+    created_at: "2013-12-11 11:23:54.979606"
+    updated_at: "2013-12-11 11:23:54.979606"
+  destroy_compute_resources_vms:
+    name: destroy_compute_resources_vms
+    resource_type: ComputeResource
+    created_at: "2013-12-11 11:23:54.988796"
+    updated_at: "2013-12-11 11:23:54.988796"
+  power_compute_resources_vms:
+    name: power_compute_resources_vms
+    resource_type: ComputeResource
+    created_at: "2013-12-11 11:23:54.996808"
+    updated_at: "2013-12-11 11:23:54.996808"
+  console_compute_resources_vms:
+    name: console_compute_resources_vms
+    resource_type: ComputeResource
+    created_at: "2013-12-11 11:23:55.004963"
+    updated_at: "2013-12-11 11:23:55.004963"
+  view_facts:
+    name: view_facts
+    resource_type: FactValue
+    created_at: "2013-12-11 11:23:55.027868"
+    updated_at: "2013-12-11 11:23:55.027868"
+  upload_facts:
+    name: upload_facts
+    resource_type: FactValue
+    created_at: "2013-12-11 11:23:55.038552"
+    updated_at: "2013-12-11 11:23:55.038552"
+  view_audit_logs:
+    name: view_audit_logs
+    resource_type: Audit
+    created_at: "2013-12-11 11:23:54.920553"
+    updated_at: "2014-01-03 15:52:15.267695"
+  view_plugins:
+    name: view_plugins
+    resource_type:
+    created_at: "2014-01-07 15:09:32.783442"
+    updated_at: "2014-01-07 15:09:32.783442"
diff --git a/test/fixtures/roles.yml b/test/fixtures/roles.yml
index 35278a406..d7090251b 100644
--- a/test/fixtures/roles.yml
+++ b/test/fixtures/roles.yml
@@ -3,218 +3,58 @@ manager:
   name: Manager
   id: "1"
   builtin: "0"
-  permissions: |
-    ---
-    - :view_architectures
-    - :create_architectures
-    - :edit_architectures
-    - :destroy_architectures
-    - :view_authenticators
-    - :create_authenticators
-    - :edit_authenticators
-    - :destroy_authenticators
-    - :view_environments
-    - :create_environments
-    - :edit_environments
-    - :destroy_environments
-    - :import_environments
-    - :view_external_variables
-    - :create_external_variables
-    - :edit_external_variables
-    - :destroy_external_variables
-    - :view_domains
-    - :create_domain
-    - :edit_domains
-    - :destroy_domains
-    - :view_globals
-    - :create_globals
-    - :edit_globals
-    - :destroy_globals
-    - :view_hostgroups
-    - :create_hostgroups
-    - :edit_hostgroups
-    - :destroy_hostgroups
-    - :view_hosts
-    - :create_hosts
-    - :edit_hosts
-    - :destroy_hosts
-    - :view_media
-    - :create_media
-    - :edit_media
-    - :destroy_media
-    - :view_models
-    - :create_models
-    - :edit_models
-    - :destroy_models
-    - :view_operatingsystems
-    - :create_operatingsystems
-    - :edit_operatingsystems
-    - :destroy_operatingsystems
-    - :view_ptables
-    - :create_ptables
-    - :edit_ptables
-    - :destroy_ptables
-    - :view_puppetclasses
-    - :create_puppetclasses
-    - :edit_puppetclasses
-    - :destroy_puppetclasses
-    - :import_puppetclasses
-    - :view_usergroups
-    - :create_usergroups
-    - :edit_usergroups
-    - :destroy_usergroups
-    - :view_users
-    - :create_users
-    - :edit_users
-    - :destroy_users
-    - :access_settings
-    - :access_dashboard
-    - :view_reports
-    - :destroy_reports
-    - :view_facts
-    - :view_audit_logs
-    - :view_statistics
-    - :view_locations
-    - :edit_locations
-    - :create_locations
-    - :destroy_locations
-    - :view_organizations
-    - :edit_organizations
-    - :create_organizations
-    - :destroy_organizations
-    - :edit_classes
 
 edit_partition_tables:
   name: Edit partition tables
   id: "2"
   builtin: "0"
-  permissions: |
-    ---
-    - :view_ptables
-    - :create_ptables
-    - :edit_ptables
-    - :destroy_ptables
 
 view_hosts:
   name: View hosts
   id: "3"
   builtin: "0"
-  permissions: |
-    ---
-    - :view_hosts
 
 edit_hosts:
   name: Edit hosts
   id: "4"
   builtin: "0"
-  permissions: |
-    ---
-    - :edit_hosts
 
 viewer:
   name: Viewer
   id: "5"
   builtin: "0"
-  permissions: |
-    ---
-    - :view_architectures
-    - :view_audit_logs
-    - :view_authenticators
-    - :access_dashboard
-    - :view_domains
-    - :view_environments
-    - :view_external_variables
-    - :view_facts
-    - :view_globals
-    - :view_hostgroups
-    - :view_hosts
-    - :view_locations
-    - :view_media
-    - :view_models
-    - :view_operatingsystems
-    - :view_ptables
-    - :view_puppetclasses
-    - :view_reports
-    - :access_settings
-    - :view_statistics
-    - :view_organizations
-    - :view_usergroups
-    - :view_users
 
 default_user:
   name: Default user
   id: "6"
   builtin: "1"
-  permissions: |
-    ---
-    - :view_hosts
-    - :view_puppetclasses
-    - :view_hostgroups
-    - :view_domains
-    - :view_oses
-    - :view_media
-    - :view_models
-    - :view_environments
-    - :view_architectures
-    - :view_ptables
-    - :view_globals
-    - :view_external_variables
-    - :view_authenticators
-    - :access_settings
-    - :access_dashboard
-    - :view_reports
-    - :view_facts
-    - :view_statistics
 
 anonymous:
   name: Anonymous
   id: "7"
   builtin: "2"
-  permissions: |
-    ---
-    - :view_hosts
 
 destroy_hosts:
   name: Destroy hosts
   id: "8"
   builtin: "0"
-  permissions: |
-    ---
-    - :destroy_hosts
 
 create_hosts:
   name: Create hosts
   id: "9"
   builtin: "0"
-  permissions: |
-    ---
-    - :create_hosts
 
 none_compute_resources:
   name: No rights
   id: "10"
   builtin: "0"
-  permissions: |
-    ---
 
 manage_compute_resources:
   name: View compute resources
   id: "11"
   builtin: "0"
-  permissions: |
-    ---
-    - :view_compute_resources
-    - :create_compute_resources
-    - :edit_compute_resources
-    - :destroy_compute_resources
 
 manage_hosts:
   name: CRUD hosts
   id: "12"
   builtin: "0"
-  permissions: |
-    ---
-    - :create_hosts
-    - :edit_hosts
-    - :destroy_hosts
-    - :view_hosts
diff --git a/test/fixtures/user_facts.yml b/test/fixtures/user_facts.yml
index dea8c48fe..e69de29bb 100644
--- a/test/fixtures/user_facts.yml
+++ b/test/fixtures/user_facts.yml
@@ -1,8 +0,0 @@
-# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
-
-one:
-  user: one
-  fact_name: ipaddress
-  criteria: '10.0.19.33'
-  operator: '='
-  andor: 'and'
diff --git a/test/fixtures/user_roles.yml b/test/fixtures/user_roles.yml
index d4a9f7587..f254e879f 100644
--- a/test/fixtures/user_roles.yml
+++ b/test/fixtures/user_roles.yml
@@ -1,15 +1,15 @@
 user_restricted_viewer_role:
-  user: restricted
+  owner: restricted
   role_id: 5
 
 user_restricted_anonymous_role:
-  user: restricted
+  owner: restricted
   role_id: 7
 
 user_restricted_manage_hosts_role:
-  user: restricted
+  owner: restricted
   role_id: 12
 
 user_restricted_manage_compute_resources:
-  user: restricted
+  owner: restricted
   role_id: 11
diff --git a/test/functional/api/v1/compute_resources_controller_test.rb b/test/functional/api/v1/compute_resources_controller_test.rb
index 68755ed6e..230a01fe5 100644
--- a/test/functional/api/v1/compute_resources_controller_test.rb
+++ b/test/functional/api/v1/compute_resources_controller_test.rb
@@ -48,59 +48,52 @@ class Api::V1::ComputeResourcesControllerTest < ActionController::TestCase
   end
 
   test "should get index of owned" do
-    as_user(:restricted) do
-      get :index, {}
-    end
+    setup_user 'view', 'compute_resources', "id = #{compute_resources(:mycompute).id}"
+    get :index, {}
     assert_response :success
     assert_not_nil assigns(:compute_resources)
     compute_resources = ActiveSupport::JSON.decode(@response.body)
     ids               = compute_resources.map { |hash| hash['compute_resource']['id'] }
-    assert !ids.include?(compute_resources(:mycompute).id)
-    assert ids.include?(compute_resources(:yourcompute).id)
+    assert_includes ids, compute_resources(:mycompute).id
+    refute_includes ids, compute_resources(:yourcompute).id
   end
 
   test "should allow access to a compute resource for owner" do
-    as_user(:restricted) do
-      get :show, { :id => compute_resources(:yourcompute).to_param }
-    end
+    setup_user 'view', 'compute_resources', "id = #{compute_resources(:mycompute).id}"
+    get :show, { :id => compute_resources(:mycompute).to_param }
     assert_response :success
   end
 
   test "should update compute resource for owner" do
-    as_user(:restricted) do
-      put :update, { :id => compute_resources(:yourcompute).to_param, :compute_resource => { :description => "new_description" } }
-    end
-    assert_equal "new_description", ComputeResource.find_by_name('yourcompute').description
+    setup_user 'edit', 'compute_resources', "id = #{compute_resources(:mycompute).id}"
+    put :update, { :id => compute_resources(:mycompute).to_param, :compute_resource => { :description => "new_description" } }
+    assert_equal "new_description", ComputeResource.find_by_name('mycompute').description
     assert_response :success
   end
 
   test "should destroy compute resource for owner" do
     assert_difference('ComputeResource.count', -1) do
-      as_user(:restricted) do
-        delete :destroy, { :id => compute_resources(:yourcompute).id }
-      end
+      setup_user 'destroy', 'compute_resources', "id = #{compute_resources(:mycompute).id}"
+      delete :destroy, { :id => compute_resources(:mycompute).id }
     end
     assert_response :success
   end
 
   test "should not allow access to a compute resource out of users compute resources scope" do
-    as_user(:restricted) do
-      get :show, { :id => compute_resources(:one).to_param }
-    end
+    setup_user 'view', 'compute_resources', "id = #{compute_resources(:mycompute).id}"
+    get :show, { :id => compute_resources(:one).to_param }
     assert_response :not_found
   end
 
   test "should not update compute resource for restricted" do
-    as_user(:restricted) do
-      put :update, { :id => compute_resources(:mycompute).to_param, :compute_resource => { :description => "new_description" } }
-    end
+    setup_user 'edit', 'compute_resources', "id = #{compute_resources(:mycompute).id}"
+    put :update, { :id => compute_resources(:yourcompute).to_param, :compute_resource => { :description => "new_description" } }
     assert_response :not_found
   end
 
   test "should not destroy compute resource for restricted" do
-    as_user(:restricted) do
-      delete :destroy, { :id => compute_resources(:mycompute).id }
-    end
+    setup_user 'destroy', 'compute_resources', "id = #{compute_resources(:mycompute).id}"
+    delete :destroy, { :id => compute_resources(:yourcompute).id }
     assert_response :not_found
   end
 
diff --git a/test/functional/api/v1/config_templates_controller_test.rb b/test/functional/api/v1/config_templates_controller_test.rb
index e4f1d9f46..041609d28 100644
--- a/test/functional/api/v1/config_templates_controller_test.rb
+++ b/test/functional/api/v1/config_templates_controller_test.rb
@@ -5,7 +5,7 @@ class Api::V1::ConfigTemplatesControllerTest < ActionController::TestCase
   test "should get index" do
     get :index
     templates = ActiveSupport::JSON.decode(@response.body)
-    assert !templates.empty?, "Should response with template"
+    assert_not_empty templates
     assert_response :success
   end
 
@@ -13,13 +13,13 @@ class Api::V1::ConfigTemplatesControllerTest < ActionController::TestCase
     get :show, { :id => config_templates(:pxekickstart).to_param }
     assert_response :success
     template = ActiveSupport::JSON.decode(@response.body)
-    assert !template.empty?
+    assert_not_empty template
     assert_equal template["config_template"]["name"], config_templates(:pxekickstart).name
   end
 
   test "should not create invalid" do
     post :create
-    assert_response 422
+    assert_response :unprocessable_entity
   end
 
   test "should create valid" do
@@ -28,13 +28,13 @@ class Api::V1::ConfigTemplatesControllerTest < ActionController::TestCase
     post :create, { :config_template => valid_attrs }
     template = ActiveSupport::JSON.decode(@response.body)
     assert template["config_template"]["name"] == "RandomName"
-    assert_response 200
+    assert_response :success
   end
 
   test "should not update invalid" do
     put :update, { :id              => config_templates(:pxekickstart).to_param,
                    :config_template => { :name => "" } }
-    assert_response 422
+    assert_response :unprocessable_entity
   end
 
   test "should update valid" do
@@ -42,13 +42,13 @@ class Api::V1::ConfigTemplatesControllerTest < ActionController::TestCase
     put :update, { :id              => config_templates(:pxekickstart).to_param,
                    :config_template => { :template => "blah" } }
     template = ActiveSupport::JSON.decode(@response.body)
-    assert_response :ok
+    assert_response :success
   end
 
   test "should not destroy template with associated hosts" do
     config_template = config_templates(:pxekickstart)
     delete :destroy, { :id => config_template.to_param }
-    assert_response 422
+    assert_response :unprocessable_entity
     assert ConfigTemplate.exists?(config_template.id)
   end
 
@@ -57,7 +57,7 @@ class Api::V1::ConfigTemplatesControllerTest < ActionController::TestCase
     config_template.os_default_templates.clear
     delete :destroy, { :id => config_template.to_param }
     template = ActiveSupport::JSON.decode(@response.body)
-    assert_response :ok
+    assert_response :success
     assert !ConfigTemplate.exists?(config_template.id)
   end
 
@@ -65,7 +65,7 @@ class Api::V1::ConfigTemplatesControllerTest < ActionController::TestCase
     ProxyAPI::TFTP.any_instance.stubs(:create_default).returns(true)
     ProxyAPI::TFTP.any_instance.stubs(:fetch_boot_file).returns(true)
     get :build_pxe_default
-    assert_response 200
+    assert_response :success
   end
 
   test "should add audit comment" do
diff --git a/test/functional/api/v1/hosts_controller_test.rb b/test/functional/api/v1/hosts_controller_test.rb
index dcdf964b2..46d0b9676 100644
--- a/test/functional/api/v1/hosts_controller_test.rb
+++ b/test/functional/api/v1/hosts_controller_test.rb
@@ -21,14 +21,14 @@ class Api::V1::HostsControllerTest < ActionController::TestCase
     assert_response :success
     assert_not_nil assigns(:hosts)
     hosts = ActiveSupport::JSON.decode(@response.body)
-    assert !hosts.empty?
+    assert_not_empty hosts
   end
 
   test "should show individual record" do
     get :show, { :id => hosts(:one).to_param }
     assert_response :success
     show_response = ActiveSupport::JSON.decode(@response.body)
-    assert !show_response.empty?
+    assert_not_empty show_response
   end
 
   test "should create host" do
@@ -74,72 +74,64 @@ class Api::V1::HostsControllerTest < ActionController::TestCase
   end
 
   test "should allow access to restricted user who owns the host" do
-    as_user :restricted do
-      get :show, { :id => hosts(:owned_by_restricted).to_param }
-    end
+    setup_user 'view', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    $debug = true
+    get :show, { :id => hosts(:owned_by_restricted).to_param }
     assert_response :success
   end
 
   test "should allow to update for restricted user who owns the host" do
     disable_orchestration
-    as_user :restricted do
-      put :update, { :id => hosts(:owned_by_restricted).to_param, :host => {} }
-    end
+    setup_user 'edit', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    put :update, { :id => hosts(:owned_by_restricted).to_param, :host => {} }
     assert_response :success
   end
 
   test "should allow destroy for restricted user who owns the hosts" do
     assert_difference('Host.count', -1) do
-      as_user :restricted do
-        delete :destroy, { :id => hosts(:owned_by_restricted).to_param }
-      end
+      setup_user 'destroy', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+      delete :destroy, { :id => hosts(:owned_by_restricted).to_param }
     end
     assert_response :success
   end
 
   test "should allow show status for restricted user who owns the hosts" do
-    as_user :restricted do
-      get :status, { :id => hosts(:owned_by_restricted).to_param }
-    end
+    setup_user 'view', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    get :status, { :id => hosts(:owned_by_restricted).to_param }
     assert_response :success
   end
 
   test "should not allow access to a host out of users hosts scope" do
-    as_user :restricted do
-      get :show, { :id => hosts(:one).to_param }
-    end
+    setup_user 'view', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    get :show, { :id => hosts(:one).to_param }
     assert_response :not_found
   end
 
   test "should not list a host out of users hosts scope" do
-    as_user :restricted do
-      get :index, {}
-    end
+    setup_user 'view', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    get :index, {}
     assert_response :success
     hosts = ActiveSupport::JSON.decode(@response.body)
     ids = hosts.map { |hash| hash['host']['id'] }
-    assert !ids.include?(hosts(:one).id)
-    assert ids.include?(hosts(:owned_by_restricted).id)
+    refute_includes ids, hosts(:one).id
+    assert_includes ids, hosts(:owned_by_restricted).id
   end
 
   test "should not update host out of users hosts scope" do
-    as_user :restricted do
-      put :update, { :id => hosts(:one).to_param }
-    end
+    setup_user 'edit', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    put :update, { :id => hosts(:one).to_param }
     assert_response :not_found
   end
 
   test "should not delete hosts out of users hosts scope" do
-    as_user :restricted do
-      delete :destroy, { :id => hosts(:one).to_param }
-    end
+    setup_user 'destroy', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    delete :destroy, { :id => hosts(:one).to_param }
     assert_response :not_found
   end
 
   test "should not show status of hosts out of users hosts scope" do
-    as_user :restricted do
-      get :status, { :id => hosts(:one).to_param }
-    end
+    setup_user 'view', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    get :status, { :id => hosts(:one).to_param }
     assert_response :not_found
   end
 
diff --git a/test/functional/api/v1/smart_proxies_controller_test.rb b/test/functional/api/v1/smart_proxies_controller_test.rb
index eaef8ff1c..8401b2fda 100644
--- a/test/functional/api/v1/smart_proxies_controller_test.rb
+++ b/test/functional/api/v1/smart_proxies_controller_test.rb
@@ -9,7 +9,7 @@ class Api::V1::SmartProxiesControllerTest < ActionController::TestCase
     assert_response :success
     assert_not_nil assigns(:smart_proxies)
     smart_proxies = ActiveSupport::JSON.decode(@response.body)
-    assert !smart_proxies.empty?
+    assert_not smart_proxies.empty?
   end
 
   test "should get index filtered by type" do
@@ -19,7 +19,7 @@ class Api::V1::SmartProxiesControllerTest < ActionController::TestCase
     assert_response :success
     assert_not_nil assigns(:smart_proxies)
     smart_proxies = ActiveSupport::JSON.decode(@response.body)
-    assert !smart_proxies.empty?
+    assert_not smart_proxies.empty?
 
     returned_proxy_ids = smart_proxies.map { |p| p["smart_proxy"]["id"] }
     expected_proxy_ids = SmartProxy.tftp_proxies.map { |p| p.id }
@@ -37,7 +37,7 @@ class Api::V1::SmartProxiesControllerTest < ActionController::TestCase
     get :show, { :id => smart_proxies(:one).to_param }
     assert_response :success
     show_response = ActiveSupport::JSON.decode(@response.body)
-    assert !show_response.empty?
+    assert_not show_response.empty?
   end
 
   test "should create smart_proxy" do
diff --git a/test/functional/api/v1/statistics_controller_test.rb b/test/functional/api/v1/statistics_controller_test.rb
index 6fef2a4b5..8a20d5703 100644
--- a/test/functional/api/v1/statistics_controller_test.rb
+++ b/test/functional/api/v1/statistics_controller_test.rb
@@ -7,8 +7,12 @@ class Api::V1::StatisticsControllerTest < ActionController::TestCase
     get :index, { }
     assert_response :success
     response = ActiveSupport::JSON.decode(@response.body)
-    assert !response.empty?
-    assert_equal 'statistics', response.keys.first
+    assert_not response.empty?
+    expected_keys = ["arch_count", "cpu_count", "env_count", "klass_count",
+                     "mem_free", "mem_size", "mem_totfree", "mem_totsize",
+                     "model_count", "os_count", "swap_free", "swap_size"]
+
+    assert_equal expected_keys, response.keys.sort
   end
 
 end
diff --git a/test/functional/api/v1/users_controller_test.rb b/test/functional/api/v1/users_controller_test.rb
index c4bd53f6a..fafe8989f 100644
--- a/test/functional/api/v1/users_controller_test.rb
+++ b/test/functional/api/v1/users_controller_test.rb
@@ -17,14 +17,14 @@ class Api::V1::UsersControllerTest < ActionController::TestCase
     get :show, { :id => users(:one).id }
     assert_response :success
     show_response = ActiveSupport::JSON.decode(@response.body)
-    assert !show_response.empty?
+    assert_not show_response.empty?
   end
 
   test "should show individual record by login name" do
     get :show, { :id => users(:one).login }
     assert_response :success
     show_response = ActiveSupport::JSON.decode(@response.body)
-    assert !show_response.empty?
+    assert_not show_response.empty?
   end
 
   test "should update user" do
diff --git a/test/functional/api/v2/compute_resources_controller_test.rb b/test/functional/api/v2/compute_resources_controller_test.rb
index c56148f8e..b60d0b3c6 100644
--- a/test/functional/api/v2/compute_resources_controller_test.rb
+++ b/test/functional/api/v2/compute_resources_controller_test.rb
@@ -48,59 +48,52 @@ class Api::V2::ComputeResourcesControllerTest < ActionController::TestCase
   end
 
   test "should get index of owned" do
-    as_user(:restricted) do
-      get :index, {}
-    end
+    setup_user 'view', 'compute_resources', "id = #{compute_resources(:mycompute).id}"
+    get :index, {}
     assert_response :success
     assert_not_nil assigns(:compute_resources)
     compute_resources = ActiveSupport::JSON.decode(@response.body)
     ids               = compute_resources['results'].map { |hash| hash['id'] }
-    assert !ids.include?(compute_resources(:mycompute).id)
-    assert ids.include?(compute_resources(:yourcompute).id)
+    assert_includes ids, compute_resources(:mycompute).id
+    refute_includes ids, compute_resources(:yourcompute).id
   end
 
   test "should allow access to a compute resource for owner" do
-    as_user(:restricted) do
-      get :show, { :id => compute_resources(:yourcompute).to_param }
-    end
+    setup_user 'view', 'compute_resources', "id = #{compute_resources(:mycompute).id}"
+    get :show, { :id => compute_resources(:mycompute).to_param }
     assert_response :success
   end
 
   test "should update compute resource for owner" do
-    as_user(:restricted) do
-      put :update, { :id => compute_resources(:yourcompute).to_param, :compute_resource => { :description => "new_description" } }
-    end
-    assert_equal "new_description", ComputeResource.find_by_name('yourcompute').description
+    setup_user 'edit', 'compute_resources', "id = #{compute_resources(:mycompute).id}"
+    put :update, { :id => compute_resources(:mycompute).to_param, :compute_resource => { :description => "new_description" } }
+    assert_equal "new_description", ComputeResource.find_by_name('mycompute').description
     assert_response :success
   end
 
   test "should destroy compute resource for owner" do
     assert_difference('ComputeResource.count', -1) do
-      as_user(:restricted) do
-        delete :destroy, { :id => compute_resources(:yourcompute).id }
-      end
+      setup_user 'destroy', 'compute_resources', "id = #{compute_resources(:mycompute).id}"
+      delete :destroy, { :id => compute_resources(:mycompute).id }
     end
     assert_response :success
   end
 
   test "should not allow access to a compute resource out of users compute resources scope" do
-    as_user(:restricted) do
-      get :show, { :id => compute_resources(:one).to_param }
-    end
+    setup_user 'view', 'compute_resources', "id = #{compute_resources(:mycompute).id}"
+    get :show, { :id => compute_resources(:one).to_param }
     assert_response :not_found
   end
 
   test "should not update compute resource for restricted" do
-    as_user(:restricted) do
-      put :update, { :id => compute_resources(:mycompute).to_param, :compute_resource => { :description => "new_description" } }
-    end
+    setup_user 'edit', 'compute_resources', "id = #{compute_resources(:mycompute).id}"
+    put :update, { :id => compute_resources(:yourcompute).to_param, :compute_resource => { :description => "new_description" } }
     assert_response :not_found
   end
 
   test "should not destroy compute resource for restricted" do
-    as_user(:restricted) do
-      delete :destroy, { :id => compute_resources(:mycompute).id }
-    end
+    setup_user 'destroy', 'compute_resources', "id = #{compute_resources(:mycompute).id}"
+    delete :destroy, { :id => compute_resources(:yourcompute).id }
     assert_response :not_found
   end
 
@@ -115,7 +108,7 @@ class Api::V2::ComputeResourcesControllerTest < ActionController::TestCase
     get :available_images, { :id => compute_resources(:ec2).to_param }
     assert_response :success
     available_images = ActiveSupport::JSON.decode(@response.body)
-    assert !available_images.empty?
+    assert_not_empty available_images
   end
 
   test "should get available networks" do
diff --git a/test/functional/api/v2/filters_controller_test.rb b/test/functional/api/v2/filters_controller_test.rb
new file mode 100644
index 000000000..448871495
--- /dev/null
+++ b/test/functional/api/v2/filters_controller_test.rb
@@ -0,0 +1,40 @@
+require 'test_helper'
+
+class Api::V2::FiltersControllerTest < ActionController::TestCase
+
+  test "should get index" do
+    get :index
+    assert_response :success
+    assert_not_nil assigns(:filters)
+    filters = ActiveSupport::JSON.decode(@response.body)
+    assert !filters.empty?
+  end
+
+  test "should show individual record" do
+    get :show, { :id => filters(:manager_1).to_param }
+    assert_response :success
+    show_response = ActiveSupport::JSON.decode(@response.body)
+    assert !show_response.empty?
+  end
+
+  test "should create filter" do
+    valid_attrs = { :role_id => roles(:manager).id, :permission_ids => [permissions(:view_architectures).id]  }
+    assert_difference('Filter.count') do
+      post :create, { :filter => valid_attrs }
+    end
+    assert_response :success
+  end
+
+  test "should update filter" do
+    put :update, { :id => filters(:manager_1).to_param, :filter => { } }
+    assert_response :success
+  end
+
+  test "should destroy filters" do
+    assert_difference('Filter.count', -1) do
+      delete :destroy, { :id => filters(:crud_hosts_1).to_param }
+    end
+    assert_response :success
+  end
+
+end
diff --git a/test/functional/api/v2/hosts_controller_test.rb b/test/functional/api/v2/hosts_controller_test.rb
index 17d368d0c..663e076b0 100644
--- a/test/functional/api/v2/hosts_controller_test.rb
+++ b/test/functional/api/v2/hosts_controller_test.rb
@@ -74,72 +74,63 @@ class Api::V2::HostsControllerTest < ActionController::TestCase
   end
 
   test "should allow access to restricted user who owns the host" do
-    as_user :restricted do
-      get :show, { :id => hosts(:owned_by_restricted).to_param }
-    end
+    setup_user 'view', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    get :show, { :id => hosts(:owned_by_restricted).to_param }
     assert_response :success
   end
 
   test "should allow to update for restricted user who owns the host" do
     disable_orchestration
-    as_user :restricted do
-      put :update, { :id => hosts(:owned_by_restricted).to_param, :host => {} }
-    end
+    setup_user 'edit', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    put :update, { :id => hosts(:owned_by_restricted).to_param, :host => {} }
     assert_response :success
   end
 
   test "should allow destroy for restricted user who owns the hosts" do
     assert_difference('Host.count', -1) do
-      as_user :restricted do
-        delete :destroy, { :id => hosts(:owned_by_restricted).to_param }
-      end
+      setup_user 'destroy', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+      delete :destroy, { :id => hosts(:owned_by_restricted).to_param }
     end
     assert_response :success
   end
 
   test "should allow show status for restricted user who owns the hosts" do
-    as_user :restricted do
-      get :status, { :id => hosts(:owned_by_restricted).to_param }
-    end
+    setup_user 'view', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    get :status, { :id => hosts(:owned_by_restricted).to_param }
     assert_response :success
   end
 
   test "should not allow access to a host out of users hosts scope" do
-    as_user :restricted do
-      get :show, { :id => hosts(:one).to_param }
-    end
+    setup_user 'view', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    get :show, { :id => hosts(:one).to_param }
     assert_response :not_found
   end
 
   test "should not list a host out of users hosts scope" do
-    as_user :restricted do
-      get :index, {}
-    end
+    setup_user 'view', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    get :index, {}
     assert_response :success
     hosts = ActiveSupport::JSON.decode(@response.body)
     ids = hosts['results'].map { |hash| hash['id'] }
-    assert !ids.include?(hosts(:one).id)
-    assert ids.include?(hosts(:owned_by_restricted).id)
+    refute_includes ids, hosts(:one).id
+    assert_includes ids, hosts(:owned_by_restricted).id
   end
 
   test "should not update host out of users hosts scope" do
-    as_user :restricted do
-      put :update, { :id => hosts(:one).to_param }
-    end
+    setup_user 'edit', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    put :update, { :id => hosts(:one).to_param }
     assert_response :not_found
   end
 
   test "should not delete hosts out of users hosts scope" do
-    as_user :restricted do
-      delete :destroy, { :id => hosts(:one).to_param }
-    end
+    setup_user 'destroy', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    delete :destroy, { :id => hosts(:one).to_param }
     assert_response :not_found
   end
 
   test "should not show status of hosts out of users hosts scope" do
-    as_user :restricted do
-      get :status, { :id => hosts(:one).to_param }
-    end
+    setup_user 'view', 'hosts', "owner_type = User and owner_id = #{users(:restricted).id}", :restricted
+    get :status, { :id => hosts(:one).to_param }
     assert_response :not_found
   end
 
diff --git a/test/functional/api/v2/operatingsystems_controller_test.rb b/test/functional/api/v2/operatingsystems_controller_test.rb
index 599df67ad..7d284403c 100644
--- a/test/functional/api/v2/operatingsystems_controller_test.rb
+++ b/test/functional/api/v2/operatingsystems_controller_test.rb
@@ -54,7 +54,6 @@ class Api::V2::OperatingsystemsControllerTest < ActionController::TestCase
 
   test "should update associated architectures by ids" do
     os = operatingsystems(:redhat)
-    puts
     assert_difference('os.architectures.count') do
       put :update, { :id => operatingsystems(:redhat).to_param, :operatingsystem => { },
                      :architectures => [{ :id => architectures(:x86_64).id }, { :id => architectures(:sparc).id } ]
@@ -65,7 +64,6 @@ class Api::V2::OperatingsystemsControllerTest < ActionController::TestCase
 
   test "should update associated architectures by name" do
     os = operatingsystems(:redhat)
-    puts
     assert_difference('os.architectures.count') do
       put :update, { :id => operatingsystems(:redhat).to_param,  :operatingsystem => { },
                      :architectures => [{ :name => architectures(:x86_64).name }, { :name => architectures(:sparc).name } ]
diff --git a/test/functional/api/v2/override_values_controller_test.rb b/test/functional/api/v2/override_values_controller_test.rb
index da3976aa0..041d5ff3e 100644
--- a/test/functional/api/v2/override_values_controller_test.rb
+++ b/test/functional/api/v2/override_values_controller_test.rb
@@ -9,14 +9,14 @@ class Api::V2::OverrideValuesControllerTest < ActionController::TestCase
     get :index, {:smart_variable_id => lookup_keys(:two).to_param }
     assert_response :success
     override_values = ActiveSupport::JSON.decode(@response.body)
-    assert !override_values.empty?
+    assert_not_empty override_values
     assert_equal 1, override_values["results"].length
   end
   test "should get override values for specific smart class parameter" do
     get :index, {:smart_class_parameter_id => lookup_keys(:complex).to_param }
     assert_response :success
     override_values = ActiveSupport::JSON.decode(@response.body)
-    assert !override_values.empty?
+    assert_not_empty override_values
     assert_equal 2, override_values["results"].length
   end
 
@@ -37,13 +37,13 @@ class Api::V2::OverrideValuesControllerTest < ActionController::TestCase
     get :show,  {:smart_variable_id => lookup_keys(:two).to_param, :id => lookup_values(:four).to_param }
     assert_response :success
     results = ActiveSupport::JSON.decode(@response.body)
-    assert !results.empty?
+    assert_not_empty results
     assert_equal "hostgroup=Common", results['match']
   end
   test "should show specific override values for specific smart class parameter" do
     get :show,  {:smart_class_parameter_id => lookup_keys(:complex).to_param, :id => lookup_values(:hostgroupcommon).to_param }
     results = ActiveSupport::JSON.decode(@response.body)
-    assert !results.empty?
+    assert_not_empty results
     assert_equal "hostgroup=Common", results['match']
     assert_response :success
   end
@@ -60,4 +60,4 @@ class Api::V2::OverrideValuesControllerTest < ActionController::TestCase
     assert_response :success
   end
 
-end
\ No newline at end of file
+end
diff --git a/test/functional/api/v2/parameters_controller_test.rb b/test/functional/api/v2/parameters_controller_test.rb
index 9a1f74db7..7d8268f2b 100644
--- a/test/functional/api/v2/parameters_controller_test.rb
+++ b/test/functional/api/v2/parameters_controller_test.rb
@@ -9,7 +9,7 @@ class Api::V2::ParametersControllerTest < ActionController::TestCase
     assert_response :success
     assert_not_nil assigns(:parameters)
     parameters = ActiveSupport::JSON.decode(@response.body)
-    assert !parameters.empty?
+    assert_not_empty parameters
   end
 
   test "should get index for specific domain" do
@@ -17,7 +17,7 @@ class Api::V2::ParametersControllerTest < ActionController::TestCase
     assert_response :success
     assert_not_nil assigns(:parameters)
     parameters = ActiveSupport::JSON.decode(@response.body)
-    assert !parameters.empty?
+    assert_not_empty parameters
   end
 
   test "should get index for specific hostgroup" do
@@ -25,7 +25,7 @@ class Api::V2::ParametersControllerTest < ActionController::TestCase
     assert_response :success
     assert_not_nil assigns(:parameters)
     parameters = ActiveSupport::JSON.decode(@response.body)
-    assert !parameters.empty?
+    assert_not_empty parameters
   end
 
 
@@ -34,35 +34,35 @@ class Api::V2::ParametersControllerTest < ActionController::TestCase
     assert_response :success
     assert_not_nil assigns(:parameters)
     parameters = ActiveSupport::JSON.decode(@response.body)
-    assert !parameters.empty?
+    assert_not_empty parameters
   end
 
   test "should show a host parameter" do
     get :show, { :host_id => hosts(:one).to_param, :id => parameters(:host).to_param }
     assert_response :success
     show_response = ActiveSupport::JSON.decode(@response.body)
-    assert !show_response.empty?
+    assert_not_empty show_response
   end
 
   test "should show a domain parameter" do
     get :show, {:domain_id => domains(:mydomain).to_param, :id => parameters(:domain).to_param }
     assert_response :success
     show_response = ActiveSupport::JSON.decode(@response.body)
-    assert !show_response.empty?
+    assert_not_empty show_response
   end
 
   test "should show a hostgroup parameter" do
     get :show, {:hostgroup_id => hostgroups(:common).to_param,:id => parameters(:group).to_param }
     assert_response :success
     show_response = ActiveSupport::JSON.decode(@response.body)
-    assert !show_response.empty?
+    assert_not_empty show_response
   end
 
   test "should show an os parameter" do
     get :show, {:operatingsystem_id => operatingsystems(:redhat).to_param,:id => parameters(:os).to_param }
     assert_response :success
     show_response = ActiveSupport::JSON.decode(@response.body)
-    assert !show_response.empty?
+    assert_not_empty show_response
   end
 
   test "should show correct parameter if id is name even if name is not unique" do
diff --git a/test/functional/api/v2/statistics_controller_test.rb b/test/functional/api/v2/statistics_controller_test.rb
index e07b40a40..fff25a27d 100644
--- a/test/functional/api/v2/statistics_controller_test.rb
+++ b/test/functional/api/v2/statistics_controller_test.rb
@@ -6,19 +6,12 @@ class Api::V2::StatisticsControllerTest < ActionController::TestCase
     get :index, { }
     assert_response :success
     response = ActiveSupport::JSON.decode(@response.body)
-    assert !response.empty?
-    assert response.keys.include?('os_count')
-    assert response.keys.include?('arch_count')
-    assert response.keys.include?('env_count')
-    assert response.keys.include?('klass_count')
-    assert response.keys.include?('cpu_count')
-    assert response.keys.include?('model_count')
-    assert response.keys.include?('mem_size')
-    assert response.keys.include?('mem_free')
-    assert response.keys.include?('swap_size')
-    assert response.keys.include?('swap_free')
-    assert response.keys.include?('mem_totsize')
-    assert response.keys.include?('mem_totfree')
+    assert_not response.empty?
+    expected_keys = ["arch_count", "cpu_count", "env_count", "klass_count",
+                     "mem_free", "mem_size", "mem_totfree", "mem_totsize",
+                     "model_count", "os_count", "swap_free", "swap_size"]
+
+    assert_equal expected_keys, response.keys.sort
   end
 
 end
diff --git a/test/functional/api/v2/users_controller_test.rb b/test/functional/api/v2/users_controller_test.rb
index cf58ee57b..f7b8e5f33 100644
--- a/test/functional/api/v2/users_controller_test.rb
+++ b/test/functional/api/v2/users_controller_test.rb
@@ -17,14 +17,14 @@ class Api::V2::UsersControllerTest < ActionController::TestCase
     get :show, { :id => users(:one).id }
     assert_response :success
     show_response = ActiveSupport::JSON.decode(@response.body)
-    assert !show_response.empty?
+    assert_not show_response.empty?
   end
 
   test "should show individual record by login name" do
     get :show, { :id => users(:one).login }
     assert_response :success
     show_response = ActiveSupport::JSON.decode(@response.body)
-    assert !show_response.empty?
+    assert_not show_response.empty?
   end
 
   test "should update user" do
diff --git a/test/functional/compute_resources_controller_test.rb b/test/functional/compute_resources_controller_test.rb
index 13c90830a..2ff9fa093 100644
--- a/test/functional/compute_resources_controller_test.rb
+++ b/test/functional/compute_resources_controller_test.rb
@@ -59,7 +59,7 @@ class ComputeResourcesControllerTest < ActionController::TestCase
   test "should not show compute resource when restricted" do
     setup_user "view"
     get :show, {:id => @your_compute_resource.to_param}, set_session_user
-    assert_response 403
+    assert_response 404
   end
 
   test "should show compute resource" do
@@ -77,7 +77,7 @@ class ComputeResourcesControllerTest < ActionController::TestCase
   test "should not get edit when restricted" do
     setup_user "edit"
     get :edit, {:id => @your_compute_resource.to_param}, set_session_user
-    assert_response 403
+    assert_response 404
   end
 
   test "should get edit" do
@@ -95,7 +95,7 @@ class ComputeResourcesControllerTest < ActionController::TestCase
   test "should not update compute resource when restricted" do
     setup_user "edit"
     put :update, {:id => @your_compute_resource.to_param, :compute_resource => {:name => "editing_self", :provider => "EC2"}}, set_session_user
-    assert_response 403
+    assert_response 404
   end
 
   test "should update compute resource" do
@@ -119,7 +119,7 @@ class ComputeResourcesControllerTest < ActionController::TestCase
       delete :destroy, {:id => @your_compute_resource.to_param}, set_session_user
     end
 
-    assert_response 403
+    assert_response 404
   end
 
   test "should destroy compute resource" do
@@ -154,18 +154,8 @@ class ComputeResourcesControllerTest < ActionController::TestCase
     SETTINGS[:login] ? {:user => User.current.id, :expires_at => 5.minutes.from_now} : {}
   end
 
-  def setup_user operation
-    @one = users(:one)
-    @request.session[:user] = @one.id
-    as_admin do
-      @one.roles = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
-      role = Role.find_or_create_by_name :name => "#{operation}_compute_resources"
-      role.permissions = ["#{operation}_compute_resources".to_sym]
-      role.save!
-      @one.roles << [role]
-      @one.compute_resources = [@compute_resource]
-      @one.save!
-    end
-    User.current = @one
+  def setup_user operation, type = 'compute_resources'
+    super(operation, type, "id = #{@compute_resource.id}")
   end
+
 end
diff --git a/test/functional/compute_resources_vms_controller_test.rb b/test/functional/compute_resources_vms_controller_test.rb
index 3ffd5b0b1..04d32bc9e 100644
--- a/test/functional/compute_resources_vms_controller_test.rb
+++ b/test/functional/compute_resources_vms_controller_test.rb
@@ -12,6 +12,10 @@ class ComputeResourcesVmsControllerTest < ActionController::TestCase
 #    Fog.unmock!
   end
 
+  def setup_user operation, type = 'compute_resources_vms'
+    super(operation, type, "id = #{@compute_resource.id}")
+  end
+
   test "should not get index when not permitted" do
     setup_user "none"
     get :index, {:compute_resource_id => @compute_resource.to_param}, set_session_user
@@ -34,7 +38,7 @@ class ComputeResourcesVmsControllerTest < ActionController::TestCase
   test "should not show vm JSON when restricted" do
     setup_user "view"
     get :show, {:id => @test_vm.uuid, :format => "json", :compute_resource_id => @your_compute_resource.to_param}, set_session_user
-    assert_response 403
+    assert_response 404
   end
 
   test "should show vm JSON" do
@@ -52,7 +56,7 @@ class ComputeResourcesVmsControllerTest < ActionController::TestCase
   test "should not show vm when restricted" do
     setup_user "view"
     get :show, {:id => @test_vm.uuid, :compute_resource_id => @your_compute_resource.to_param}, set_session_user
-    assert_response 403
+    assert_response 404
   end
 
   test "should show vm" do
@@ -73,7 +77,9 @@ class ComputeResourcesVmsControllerTest < ActionController::TestCase
   #Broken with Fog.mock! because lib/fog/libvirt/models/compute/volume.rb:41 calls create_volume with the wrong number of arguments
   #Broken before Fog 8d95d5bff223a199d33e297ea21884d8598f6921 because default pool name being "default-pool" and not "default" (with test:///default) triggers an internal bug
   def test_should_create_vm(name = "new_test")
-    setup_user "create"
+    setup_user "create" do |user|
+      user.roles.last.add_permissions! :view_compute_resources
+    end
     assert_difference('@compute_resource.vms.count', +1) do
       attrs = {:name => name, :memory => 128*1024*1024, :domain_type => "test", :arch => "i686"}
       post :create, {:vm => attrs, :compute_resource_id => @compute_resource.to_param}, set_session_user
@@ -96,7 +102,7 @@ class ComputeResourcesVmsControllerTest < ActionController::TestCase
       delete :destroy, {:format => "json", :id => @test_vm.uuid, :compute_resource_id => @your_compute_resource.to_param}, set_session_user
     end
 
-    assert_response 403
+    assert_response 404
   end
 
   test "should destroy vm" do
@@ -127,7 +133,7 @@ class ComputeResourcesVmsControllerTest < ActionController::TestCase
     setup_user "power"
     get :power, {:format => "json", :id => @test_vm.uuid, :compute_resource_id => @your_compute_resource.to_param}, set_session_user
 
-    assert_response 403
+    assert_response 404
   end
 
   test "should pause openstack vm" do
@@ -173,18 +179,4 @@ class ComputeResourcesVmsControllerTest < ActionController::TestCase
     SETTINGS[:login] ? {:user => User.current.id, :expires_at => 5.minutes.from_now} : {}
   end
 
-  def setup_user operation
-    @one = users(:one)
-    @request.session[:user] = @one.id
-    as_admin do
-      @one.roles = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
-      role = Role.find_or_create_by_name :name => "#{operation}_compute_resources_vms"
-      role.permissions = ["#{operation}_compute_resources_vms".to_sym]
-      role.save!
-      @one.roles << [role]
-      @one.compute_resources = [@compute_resource]
-      @one.save!
-    end
-    User.current = @one
-  end
 end
diff --git a/test/functional/hostgroups_controller_test.rb b/test/functional/hostgroups_controller_test.rb
index 059297ef7..824b5c0f7 100644
--- a/test/functional/hostgroups_controller_test.rb
+++ b/test/functional/hostgroups_controller_test.rb
@@ -58,17 +58,8 @@ class HostgroupsControllerTest < ActionController::TestCase
     assert !Hostgroup.exists?(hostgroup.id)
   end
 
-  def setup_user operation
-    @request.session[:user] = users(:one).id
-    @one = users(:one)
-    as_admin do
-      @one.roles = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
-      role = Role.find_or_create_by_name :name => "hostgroups"
-      role.permissions = ["#{operation}_hostgroups".to_sym]
-      role.save!
-      @one.roles << [role]
-      @one.save!
-    end
+  def setup_user operation, type = 'hostgroups'
+    super
   end
 
   test 'user with viewer rights should fail to edit a hostgroup ' do
diff --git a/test/functional/hosts_controller_test.rb b/test/functional/hosts_controller_test.rb
index 028304497..2e93d5a29 100644
--- a/test/functional/hosts_controller_test.rb
+++ b/test/functional/hosts_controller_test.rb
@@ -138,144 +138,85 @@ class HostsControllerTest < ActionController::TestCase
     refute assigns(:host).mac
   end
 
-  def setup_user_and_host operation
+  def setup_user operation, type = 'hosts', filter = nil
+    super
+  end
+
+  def setup_user_and_host(operation, filter = nil, &block)
+    setup_user operation, 'hosts', filter, &block
+
     as_admin do
-      @one             = users(:one)
-      @one.domains.destroy_all
-      @one.hostgroups.destroy_all
-      @one.user_facts.destroy_all
       @host1           = hosts(:one)
       @host1.owner     = users(:admin)
       @host1.save!
       @host2           = hosts(:two)
       @host2.owner     = users(:admin)
       @host2.save!
-      @one.roles       = [Role.find_by_name('Anonymous'), Role.find_by_name("#{operation.capitalize} hosts")]
     end
     Host.per_page == 1000
     @request.session[:user] = @one.id
   end
 
-  test 'user with edit host rights and domain is set should succeed in viewing host1' do
-    setup_user_and_host "Edit"
+  test 'user with view host rights and domain is set should succeed in viewing host1 but fail for host2' do
+    setup_user_and_host "view", "domain_id = #{domains(:mydomain).id}"
+
     as_admin do
-      @one.domains  = [domains(:mydomain)]
       @host1.update_attribute(:domain, domains(:mydomain))
       @host2.update_attribute(:domain, domains(:yourdomain))
     end
     get :index, {}, set_session_user.merge(:user => @one.id)
     assert_response :success
     assert_match /#{@host1.shortname}/, @response.body
+    refute_match /#{@host2.name}/, @response.body
   end
 
-  test 'user with edit host rights and domain is set should fail to view host2' do
-    setup_user_and_host "Edit"
-    as_admin do
-      @one.domains  = [domains(:mydomain)]
-      @host1.domain = domains(:mydomain)
-      @host2.domain = domains(:yourdomain)
-    end
-    get :index, {}, set_session_user.merge(:user => @one.id)
-    assert_response :success
-    assert @response.body !~ /#{@host2.name}/
-  end
-
-  test 'user with edit host rights and ownership is set should succeed in viewing host1' do
-    setup_user_and_host "Edit"
-    as_admin do
-      @host1.owner = @one
-      @host2.owner = users(:two)
-      @one.filter_on_owner = true
-      @one.save!
-      @host1.save!
-      @host2.save!
-    end
-    get :index, {}, set_session_user.merge(:user => @one.id)
-    assert_response :success
-    assert @response.body =~ /#{@host1.name}/
-  end
-
-  test 'user with edit host rights and ownership is set should fail to view host2' do
-    setup_user_and_host "Edit"
+  test 'user with view host rights and ownership is set should succeed in viewing host1 but fail for host2' do
+    setup_user_and_host "view", "owner_id = #{users(:one).id} and owner_type = User"
     as_admin do
       @host1.owner = @one
       @host2.owner = users(:two)
-      @one.filter_on_owner = true
-      @one.save!
       @host1.save!
       @host2.save!
     end
     get :index, {}, set_session_user.merge(:user => @one.id)
     assert_response :success
-    assert @response.body !~ /#{@host2.name}/
+    assert_match /#{@host1.name}/, @response.body
+    refute_match /#{@host2.name}/, @response.body
   end
 
-  test 'user with edit host rights and hostgroup is set should succeed in viewing host1' do
-    setup_user_and_host "Edit"
+  test 'user with view host rights and hostgroup is set should succeed in viewing host1 but fail for host2' do
+    setup_user_and_host "view", "hostgroup_id = #{hostgroups(:common).id}"
     as_admin do
       @host1.hostgroup = hostgroups(:common)
       @host2.hostgroup = hostgroups(:unusual)
-      @one.hostgroups  = [hostgroups(:common)]
       @host1.save!
       @host2.save!
     end
     get :index, {}, set_session_user.merge(:user => @one.id)
     assert_response :success
-    assert @response.body =~ /#{@host1.name}/
+    assert_match /#{@host1.name}/, @response.body
+    refute_match /#{@host2.name}/, @response.body
   end
 
-  test 'user with edit host rights and hostgroup is set should fail to view host2' do
-    setup_user_and_host "Edit"
-    as_admin do
-      @host1.hostgroup = hostgroups(:common)
-      @host2.hostgroup = hostgroups(:unusual)
-      @one.hostgroups  = [hostgroups(:common)]
-      @host1.save!
-      @host2.save!
-    end
-    get :index, {}, set_session_user.merge(:user => @one.id)
-    assert_response :success
-    assert @response.body !~ /#{@host2.name}/
-  end
-
-  test 'user with edit host rights and facts are set should succeed in viewing host1' do
-    setup_user_and_host "Edit"
+  test 'user with edit host rights and facts are set should succeed in viewing host1 but fail for host2' do
+    setup_user_and_host "view", "facts.architecture = \"x86_64\""
     as_admin do
       fn_id = FactName.find_or_create_by_name("architecture").id
       FactValue.create! :host => @host1, :fact_name_id => fn_id, :value    => "x86_64"
       FactValue.create! :host => @host2, :fact_name_id => fn_id, :value    => "i386"
-      UserFact.create!  :user => @one,   :fact_name_id => fn_id, :criteria => "x86_64", :operator => "=", :andor => "or"
     end
     get :index, {}, set_session_user.merge(:user => @one.id)
     assert_response :success
-    assert @response.body =~ /#{@host1.name}/
-  end
-
-  test 'user with edit host rights and facts are set should fail to view host2' do
-    setup_user_and_host "Edit"
-    as_admin do
-      fn_id = FactName.find_or_create_by_name("architecture").id
-      FactValue.create! :host => @host1, :fact_name_id => fn_id, :value    => "x86_64"
-      FactValue.create! :host => @host2, :fact_name_id => fn_id, :value    => "i386"
-      UserFact.create!  :user => @one,   :fact_name_id => fn_id, :criteria => "x86_64", :operator => "=", :andor => "or"
-    end
-    get :index, {}, set_session_user.merge(:user => @one.id)
-    assert_response :success
-    assert @response.body !~ /#{@host2.name}/
+    assert_match /#{@host1.name}/, @response.body
+    refute_match /#{@host2.name}/, @response.body
   end
 
   test 'user with view host rights should fail to edit host' do
-    setup_user_and_host "View"
+    setup_user_and_host "view"
     get :edit, {:id => @host1.id}, set_session_user.merge(:user => @one.id)
     assert_equal @response.status, 403
   end
 
-  test 'user with view host rights should should succeed in viewing hosts' do
-    setup_user_and_host "View"
-    get :index, {}, set_session_user.merge(:user => @one.id)
-    assert_response :success
-  end
-
   test 'multiple without hosts' do
     post :update_multiple_hostgroup, {}, set_session_user
     assert_redirected_to hosts_url
@@ -296,6 +237,7 @@ class HostsControllerTest < ActionController::TestCase
 
     hostgroup = hostgroups(:unusual)
     post :update_multiple_hostgroup, { :host_ids => hosts.map(&:id), :hostgroup => { :id => hostgroup.id } }, set_session_user
+    assert_response :redirect
 
     # reloads hosts
     hosts.map! {|h| Host.find(h.id)}
@@ -315,6 +257,7 @@ class HostsControllerTest < ActionController::TestCase
 
     hostgroup = hostgroups(:common)
     post :update_multiple_hostgroup, { :host_names => host_names, :hostgroup  => { :id => hostgroup.id} }, set_session_user
+    assert_response :redirect
 
     host_names.each do |name|
       host = Host.find_by_name name
@@ -466,7 +409,7 @@ class HostsControllerTest < ActionController::TestCase
   def test_set_manage
     @request.env['HTTP_REFERER'] = edit_host_path @host
     assert @host.update_attribute :managed, false
-    assert @host.errors.empty?
+    assert_empty @host.errors
     put :toggle_manage, {:id => @host.name}, set_session_user
     assert_redirected_to :controller => :hosts, :action => :edit
     assert flash[:notice] == "Foreman now manages the build cycle for #{@host.name}"
@@ -475,7 +418,7 @@ class HostsControllerTest < ActionController::TestCase
   def test_unset_manage
     @request.env['HTTP_REFERER'] = edit_host_path @host
     assert @host.update_attribute :managed, true
-    assert @host.errors.empty?
+    assert_empty @host.errors
     put :toggle_manage, {:id => @host.name}, set_session_user
     assert_redirected_to :controller => :hosts, :action => :edit
     assert flash[:notice] == "Foreman now no longer manages the build cycle for #{@host.name}"
@@ -781,7 +724,7 @@ class HostsControllerTest < ActionController::TestCase
   test "index returns YAML output for rundeck" do
     get :index, {:format => 'yaml', :rundeck => true}, set_session_user
     hosts = YAML.load(@response.body)
-    assert !hosts.empty?
+    assert_not_empty hosts
     host = Host.first
     assert_equal host.os.name, hosts[host.name]["osName"]  # rundeck-specific field
   end
diff --git a/test/functional/roles_controller_test.rb b/test/functional/roles_controller_test.rb
index 0353e516e..311f80519 100644
--- a/test/functional/roles_controller_test.rb
+++ b/test/functional/roles_controller_test.rb
@@ -19,7 +19,7 @@ require 'test_helper'
 
 class RolesControllerTest < ActionController::TestCase
 
-  def test_get_index
+  test 'get index' do
     get :index, {}, set_session_user
     assert_response :success
     assert_template 'index'
@@ -31,49 +31,52 @@ class RolesControllerTest < ActionController::TestCase
       :content => 'Manager'
   end
 
-  def test_get_new
+  test 'get new' do
     get :new, {}, set_session_user
     assert_response :success
     assert_template 'new'
   end
 
-  def test_post_new_with_validaton_failure
-    post :create, { :role => {:name => '',
-      :permissions => ['add_hosts', 'edit_hosts', 'edit_ptables', '']
-    }}, set_session_user
+  test 'empty name validation' do
+    post :create, { :role => {:name => ''}}, set_session_user
 
     assert_response :success
     assert_template 'new'
   end
 
-  def test_get_edit
+  test 'creates role' do
+    post :create, { :role => {:name => 'test role'}}, set_session_user
+
+    assert_redirected_to roles_path
+    assert Role.find_by_name('test role')
+  end
+
+  test 'get edit goes to right template' do
     get :edit, {:id => 1}, set_session_user
     assert_response :success
     assert_template 'edit'
     assert_equal Role.find(1), assigns(:role)
   end
 
-  def test_post_edit
-    put :update, {:id => 1,
-      :role => {:name => 'Manager',
-        :permissions => ['edit_hosts']
-    }}, set_session_user
+  test 'put edit updates role' do
+    role = FactoryGirl.create(:role)
+    put :update, {:id => role.id, :role => {:name => 'masterManager'}}, set_session_user
 
     assert_redirected_to roles_path
-    role = Role.find(1)
-    assert_equal [:edit_hosts], role.permissions
+    role.reload
+    assert_equal 'masterManager', role.name
   end
 
-  def test_destroy
-    r = Role.new(:name => 'ToBeDestroyed', :permissions => [:view_ptables])
-    assert r.save
+  test 'delete destroy removes role' do
+    role = FactoryGirl.build(:role, :name => 'ToBeDestroyed')
+    role.add_permissions! :view_ptables
 
-    delete :destroy, {:id => r}, set_session_user
+    delete :destroy, {:id => role}, set_session_user
     assert_redirected_to roles_path
-    assert_nil Role.find_by_id(r.id)
+    assert_nil Role.find_by_id(role.id)
   end
 
-  def test_destroy_role_in_use
+  test 'roles in use cannot be destroyed' do
     users(:one).roles = [roles(:manager)] # make user one a manager
     delete :destroy, {:id => roles(:manager)}, set_session_user
     assert_redirected_to roles_path
@@ -81,29 +84,28 @@ class RolesControllerTest < ActionController::TestCase
     assert_not_nil Role.find_by_id(roles(:manager).id)
   end
 
-  def test_get_report
-    get :report, {}, set_session_user
-    assert_response :success
-    assert_template 'report'
-
-    assert_not_nil assigns(:roles)
-    assert_equal Role.all.sort, assigns(:roles).sort
-
-  end
-
-  def test_post_report
-    post :report, { :permissions => { '0' => '', '1' => ['edit_issues'], '3' => ['add_issues', 'delete_issues']} }, set_session_user
-    assert_redirected_to roles_path
-
-    assert_equal [:edit_issues], Role.find(1).permissions
-    assert_equal [:add_issues, :delete_issues], Role.find(3).permissions
-    assert Role.find(2).permissions.empty?
-  end
-
-  def test_clear_all_permissions
-    post :report, { :permissions => { '0' => '' } }, set_session_user
-    assert_redirected_to roles_path
-    assert Role.find(1).permissions.empty?
+  context 'clone' do
+    setup do
+      @role = FactoryGirl.build(:role, :name => 'ToBeDestroyed')
+      @role.add_permissions! :view_ptables
+    end
+
+    test 'renders new page with hidden field original_role_id' do
+      get :clone, { :id => @role.id } , set_session_user
+      assert_template 'new'
+    end
+
+    test 'original_role_id is used to create cloned role if set' do
+      params = { :role => {:name => 'clonedrole'},
+                 :original_role_id => @role.id,
+                 :cloned_role => true }
+      post :create, params, set_session_user
+      assert_redirected_to roles_url
+
+      cloned_role = Role.find_by_name('clonedrole')
+      assert_not_nil cloned_role
+      assert_equal @role.permissions, cloned_role.permissions
+    end
   end
 
 end
diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb
index 19b52820b..06e702bcd 100644
--- a/test/functional/users_controller_test.rb
+++ b/test/functional/users_controller_test.rb
@@ -136,7 +136,7 @@ class UsersControllerTest < ActionController::TestCase
 
   test 'user with viewer rights should fail to edit a user' do
     get :edit, {:id => User.first.id}
-    assert_response 403
+    assert_response 404
   end
 
   test 'user with viewer rights should succeed in viewing users' do
@@ -233,16 +233,29 @@ class UsersControllerTest < ActionController::TestCase
     assert_response :redirect
   end
 
-  test 'non admin user should not be able to edit another user' do
+  test 'user without edit permission should not be able to edit another user' do
     User.current = users(:one)
     get :edit, { :id => users(:two) }
-    assert_response 403
+    assert_response 404
+  end
+
+  test 'user with edit permission should be able to edit another user' do
+    setup_user 'edit', 'users'
+    get :edit, { :id => users(:two) }
+    assert_response :success
   end
 
-  test 'non admin user should not be able to update another user' do
+  test 'user without edit permission should not be able to update another user' do
     User.current = users(:one)
     put :update, { :id => users(:two).id, :user => { :firstname => 'test' } }
     assert_response 403
   end
 
+  test 'user with update permission should be able to update another user' do
+    setup_user 'edit', 'users'
+    put :update, { :id => users(:two).id, :user => { :firstname => 'test' } }
+
+    assert_response :redirect
+  end
+
 end
diff --git a/test/integration/host_test.rb b/test/integration/host_test.rb
index d78e2de8b..8a8ac0332 100644
--- a/test/integration/host_test.rb
+++ b/test/integration/host_test.rb
@@ -29,10 +29,10 @@ class HostTest < ActionDispatch::IntegrationTest
   end
 
   test "edit page" do
-    disable_orchestration  # Avoid DNS errors  
+    disable_orchestration  # Avoid DNS errors
     visit hosts_path
     click_link "my5name.mydomain.net"
-    first(:link, "Edit").click 
+    first(:link, "Edit").click
     assert page.has_link?("Cancel", :href => "/hosts/my5name.mydomain.net")
     fill_in "host_name", :with => "my5rename.mydomain.net"
     assert_submit_button("/hosts/my5rename.mydomain.net")
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 59b689285..a359c5b14 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -42,6 +42,27 @@ Spork.prefork do
     set_fixture_class({ :hosts => Host::Base })
     set_fixture_class :nics => Nic::BMC
 
+    setup :begin_gc_deferment
+    teardown :reconsider_gc_deferment
+
+    DEFERRED_GC_THRESHOLD = (ENV['DEFER_GC'] || 1.0).to_f
+
+    @@last_gc_run = Time.now
+
+    def begin_gc_deferment
+      GC.disable if DEFERRED_GC_THRESHOLD > 0
+    end
+
+    def reconsider_gc_deferment
+      if DEFERRED_GC_THRESHOLD > 0 && Time.now - @@last_gc_run >= DEFERRED_GC_THRESHOLD
+        GC.enable
+        GC.start
+        GC.disable
+
+        @@last_gc_run = Time.now
+      end
+    end
+
     # for backwards compatibility to between Minitest syntax
     alias_method :assert_not,       :refute
     alias_method :assert_no_match,  :refute_match
@@ -82,12 +103,20 @@ Spork.prefork do
       user.save!
     end
 
-    def setup_user operation, type=""
-      @one = users(:one)
+    # if a method receieves a block it will be yielded just before user save
+    def setup_user operation, type="", search = nil, user = :one
+      @one = users(user)
       as_admin do
+        permission = Permission.find_by_name("#{operation}_#{type}") || FactoryGirl.create(:permission, :name => "#{operation}_#{type}")
+        filter = FactoryGirl.build(:filter, :search => search)
+        filter.permissions = [ permission ]
         role = Role.find_or_create_by_name :name => "#{operation}_#{type}"
-        role.permissions = ["#{operation}_#{type}".to_sym]
-        @one.roles = [role]
+        role.filters = [ filter ]
+        role.save!
+        filter.role = role
+        filter.save!
+        @one.roles = [ role ]
+        yield(@one) if block_given?
         @one.save!
       end
       User.current = @one
diff --git a/test/unit/architecture_test.rb b/test/unit/architecture_test.rb
index 26c0f03fc..ff61a1d62 100644
--- a/test/unit/architecture_test.rb
+++ b/test/unit/architecture_test.rb
@@ -6,19 +6,19 @@ class ArchitectureTest < ActiveSupport::TestCase
   end
   test "should not save without a name" do
     architecture = Architecture.new
-    assert !architecture.save
+    assert_not architecture.save
   end
 
   test "name should not be blank" do
     architecture = Architecture.new :name => "   "
-    assert architecture.name.strip.empty?
-    assert !architecture.save
+    assert_empty architecture.name.strip
+    assert_not architecture.save
   end
 
   test "name should not contain white spaces" do
     architecture = Architecture.new :name => " i38  6 "
-    assert !architecture.name.squeeze(" ").tr(' ', '').empty?
-    assert !architecture.save
+    assert_not_empty architecture.name.squeeze(" ").tr(' ', '')
+    assert_not architecture.save
 
     architecture.name.squeeze!(" ").tr!(' ', '')
     assert architecture.save
@@ -29,7 +29,7 @@ class ArchitectureTest < ActiveSupport::TestCase
     assert architecture.save
 
     other_architecture = Architecture.new :name => "i386"
-    assert !other_architecture.save
+    assert_not other_architecture.save
   end
 
   test "to_s retrives name" do
@@ -45,63 +45,7 @@ class ArchitectureTest < ActiveSupport::TestCase
     host.architecture = architecture
     host.save(:validate => false)
 
-    assert !architecture.destroy
+    assert_not architecture.destroy
   end
 
-  def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_architectures"
-      role.permissions = ["#{operation}_architectures".to_sym]
-      @one.roles = [role]
-      @one.save!
-    end
-    User.current = @one
-  end
-
-  test "user with create permissions should be able to create" do
-    setup_user "create"
-    record =  Architecture.create :name => "dummy"
-    assert record.valid?
-    assert !record.new_record?
-  end
-
-  test "user with view permissions should not be able to create" do
-    setup_user "view"
-    record =  Architecture.create :name => "dummy"
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy"
-    record =  Architecture.first
-    as_admin do
-      record.hosts.delete_all
-      record.hostgroups.delete_all
-    end
-    assert record.destroy
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record =  Architecture.first
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit"
-    record      =  Architecture.first
-    record.name = "renamed"
-    assert record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record      =  Architecture.first
-    record.name = "renamed"
-    assert !record.save
-  end
 end
diff --git a/test/unit/auth_source_ldap_test.rb b/test/unit/auth_source_ldap_test.rb
index 3d7e587ac..d7a0c58c8 100644
--- a/test/unit/auth_source_ldap_test.rb
+++ b/test/unit/auth_source_ldap_test.rb
@@ -175,59 +175,4 @@ class AuthSourceLdapTest < ActiveSupport::TestCase
     @auth_source_ldap.send method, "this is010this is020this is030this is040this is050this is060this is070this is080this is090this is100this is110this is120this is130this is140this is150this is160this is170this is180this is190this is200this is210this is220this is230this is240this is250 and something else"
   end
 
-  def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_authenticators"
-      role.permissions = ["#{operation}_authenticators".to_sym]
-      @one.roles = [role]
-      @one.save!
-    end
-    User.current = @one
-  end
-
-  test "user with create permissions should be able to create" do
-    setup_user "create"
-    record =  AuthSourceLdap.create :name => "dummy", :host => hosts(:one).name, :port => "1", :attr_login => "login"
-    assert record.valid?
-    assert !record.new_record?
-  end
-
-  test "user with view permissions should not be able to create" do
-    setup_user "view"
-    record =  AuthSourceLdap.create :name => "dummy", :host => hosts(:one).name, :port => "1", :attr_login => "login"
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy"
-    record =  AuthSourceLdap.first
-    as_admin do
-      record.users.delete_all
-    end
-    assert record.destroy
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record =  AuthSourceLdap.first
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit"
-    record      =  AuthSourceLdap.first
-    record.name = "renamed"
-    assert record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record      =  AuthSourceLdap.first
-    record.name = "renamed"
-    assert !record.save
-  end
 end
diff --git a/test/unit/authorizer_test.rb b/test/unit/authorizer_test.rb
new file mode 100644
index 000000000..24635d7ee
--- /dev/null
+++ b/test/unit/authorizer_test.rb
@@ -0,0 +1,248 @@
+require 'test_helper'
+
+class AuthorizerTest < ActiveSupport::TestCase
+  def setup
+    User.current = User.admin
+
+    @user_role = FactoryGirl.create(:user_user_role)
+    @user      = @user_role.owner
+    @role      = @user_role.role
+  end
+
+  # limited, unlimited, permission with resource, without resource...
+  test "#can?(:view_hosts) with unlimited filter" do
+    permission = Permission.find_by_name('view_hosts')
+    filter     = FactoryGirl.create(:filter, :role => @role, :permissions => [permission])
+    auth       = Authorizer.new(@user)
+
+    assert auth.can?(:view_hosts)
+    refute auth.can?(:view_domains)
+  end
+
+  test "#can?(:view_hosts) with unlimited filter" do
+    permission = Permission.find_by_name('view_hosts')
+    filter     = FactoryGirl.create(:filter, :on_name_all, :role => @role, :permissions => [permission])
+    auth       = Authorizer.new(@user)
+
+    assert auth.can?(:view_hosts)
+    refute auth.can?(:view_domains)
+  end
+
+  test "#can?(:view_hosts) on permission without resource" do
+    permission = Permission.find_by_name('view_hosts')
+    filter     = FactoryGirl.create(:filter, :on_name_all, :role => @role, :permissions => [permission])
+    auth       = Authorizer.new(@user)
+
+    assert auth.can?(:view_hosts)
+    refute auth.can?(:view_domains)
+  end
+
+  test "#can?(:view_hosts) is limited by particular user" do
+    permission = Permission.find_by_name('view_hosts')
+    filter     = FactoryGirl.create(:filter, :on_name_all, :role => @role, :permissions => [permission])
+    auth       = Authorizer.new(FactoryGirl.create(:user))
+
+    refute auth.can?(:view_hosts)
+  end
+
+  test "#can?(:view_domains, @host) for unlimited filter" do
+    permission = Permission.find_by_name('view_domains')
+    filter     = FactoryGirl.create(:filter, :role => @role, :permissions => [permission])
+    domain     = FactoryGirl.create(:domain)
+    auth       = Authorizer.new(@user)
+
+    assert_include auth.find_collection(Domain, :permission => :view_domains), domain
+    assert auth.can?(:view_domains, domain)
+  end
+
+  test "#can?(:view_domains, @host) for matching limited filter" do
+    permission = Permission.find_by_name('view_domains')
+    filter     = FactoryGirl.create(:filter, :role => @role, :permissions => [permission],
+                                    :search        => 'name ~ example*')
+    domain     = FactoryGirl.create(:domain)
+    auth       = Authorizer.new(@user)
+
+    assert_include auth.find_collection(Domain, :permission => :view_domains), domain
+    assert auth.can?(:view_domains, domain)
+  end
+
+  test "#can?(:view_domains, @host) for matching and not matching limited filter" do
+    permission = Permission.find_by_name('view_domains')
+    not_matching_filter = FactoryGirl.create(:filter, :role => @role, :permissions => [permission],
+                                             :search        => 'name ~ noexample*')
+    matching_filter     = FactoryGirl.create(:filter, :role => @role, :permissions => [permission],
+                                             :search        => 'name ~ example*')
+    domain              = FactoryGirl.create(:domain)
+    auth                = Authorizer.new(@user)
+
+    assert_include auth.find_collection(Domain, :permission => :view_domains), domain
+    assert auth.can?(:view_domains, domain)
+  end
+
+  test "#can?(:view_domains, @host) for not matching limited filter" do
+    permission = Permission.find_by_name('view_domains')
+    filter     = FactoryGirl.create(:filter, :role => @role, :permissions => [permission],
+                                    :search        => 'name ~ noexample*')
+    domain     = FactoryGirl.create(:domain)
+    auth       = Authorizer.new(@user)
+
+    assert_not_include auth.find_collection(Domain, :permission => :view_domains), domain
+    refute auth.can?(:view_domains, domain)
+  end
+
+  test "#can?(:view_domains, @host) filters records by matching limited filter" do
+    permission = Permission.find_by_name('view_domains')
+    filter     = FactoryGirl.create(:filter, :on_name_starting_with_a,
+                                    :role => @role, :permissions => [permission])
+    domain1    = FactoryGirl.create(:domain)
+    domain2    = FactoryGirl.create(:domain, :name => 'a-domain.to-be-found.com')
+    auth       = Authorizer.new(@user)
+
+    collection = auth.find_collection(Domain, :permission => :view_domains)
+    assert_not_include collection, domain1
+    assert_include collection, domain2
+    refute auth.can?(:view_domains, domain1)
+    assert auth.can?(:view_domains, domain2)
+  end
+
+  test "#can?(:view_domains, @host) filters records by matching limited filter and permission" do
+    permission1 = Permission.find_by_name('view_domains')
+    permission2 = Permission.find_by_name('edit_domains')
+    filter1     = FactoryGirl.create(:filter, :on_name_starting_with_a,
+                                     :role => @role, :permissions => [permission1])
+    filter2     = FactoryGirl.create(:filter, :on_name_starting_with_b,
+                                     :role => @role, :permissions => [permission2])
+    domain1     = FactoryGirl.create(:domain)
+    domain2     = FactoryGirl.create(:domain, :name => 'a-domain.to-be-found.com')
+    domain3     = FactoryGirl.create(:domain, :name => 'another-domain.to-be-found.com')
+    domain4     = FactoryGirl.create(:domain, :name => 'be_editable.to-be-found.com')
+    auth        = Authorizer.new(@user)
+
+    collection = auth.find_collection(Domain, :permission => :view_domains)
+    assert_equal [domain2, domain3], collection
+    collection = auth.find_collection(Domain, :permission => :edit_domains)
+    assert_equal [domain4], collection
+    collection = auth.find_collection(Domain, :permission => :delete_domains)
+    assert_equal [], collection
+    collection = auth.find_collection(Domain)
+    assert_equal [domain2, domain3, domain4], collection
+
+    refute auth.can?(:view_domains, domain1)
+    assert auth.can?(:view_domains, domain2)
+    assert auth.can?(:view_domains, domain3)
+    refute auth.can?(:view_domains, domain4)
+    refute auth.can?(:edit_domains, domain1)
+    refute auth.can?(:edit_domains, domain2)
+    refute auth.can?(:edit_domains, domain3)
+    assert auth.can?(:edit_domains, domain4)
+
+    # unlimited filter on Domain permission does add the domain
+    filter4 = FactoryGirl.create(:filter, :role => @role, :permissions => [permission1])
+    collection = auth.find_collection(Domain)
+    assert_include collection, domain1
+    assert_include collection, domain2
+    assert_include collection, domain3
+    assert_include collection, domain4
+  end
+
+  test "#can?(:view_domains, @host) for user without filter" do
+    permission = Permission.find_by_name('view_domains')
+    filter     = FactoryGirl.create(:filter, :role => @role, :permissions => [permission])
+    domain     = FactoryGirl.create(:domain)
+    auth       = Authorizer.new(FactoryGirl.create(:user))
+
+    result = auth.find_collection(Domain, :permission => :view_domains)
+    assert_not_include result, domain
+    assert_kind_of ActiveRecord::Relation, result
+    refute auth.can?(:view_domains, domain)
+  end
+
+  test "#can? caches results per permission and class" do
+    permission1 = Permission.find_by_name('view_domains')
+    filter1     = FactoryGirl.create(:filter, :on_name_starting_with_a,
+                                     :role => @role, :permissions => [permission1])
+    domain1     = FactoryGirl.create(:domain, :name => 'a-domain.to-be-found.com')
+    domain2     = FactoryGirl.create(:domain, :name => 'x-domain.not-to-be-found.com')
+    permission2 = Permission.find_by_name('view_architectures')
+    architecture = FactoryGirl.create(:architecture)
+    filter2      = FactoryGirl.create(:filter, :role => @role, :permissions => [permission2])
+
+    auth        = Authorizer.new(@user)
+
+    auth.stubs(:find_collection).returns([domain1]).times(3)
+    assert auth.can?(:view_domain, domain1)
+    refute auth.can?('view_domain', domain2)
+    assert auth.can?(:edit_domain, domain1)
+    refute auth.can?('edit_domain', domain2)
+    refute auth.can?(:view_architectures, architecture) # since it's stubbed and returns domain1 only
+    refute auth.can?('view_architectures', architecture)
+  end
+
+  test "#build_scoped_search_condition(filters) for empty set" do
+    auth = Authorizer.new(FactoryGirl.create(:user))
+    assert_raises ArgumentError do
+      auth.build_scoped_search_condition([])
+    end
+  end
+
+  test "#build_scoped_search_condition(filters) for one filter" do
+    auth    = Authorizer.new(FactoryGirl.create(:user))
+    filters = [FactoryGirl.build(:filter, :on_name_all)]
+    result  = auth.build_scoped_search_condition(filters)
+
+    assert_equal '(name ~ *)', result
+  end
+
+  test "#build_scoped_search_condition(filters) for more filters" do
+    auth    = Authorizer.new(FactoryGirl.create(:user))
+    filters = [FactoryGirl.build(:filter, :on_name_all), FactoryGirl.build(:filter, :on_name_starting_with_a)]
+    result  = auth.build_scoped_search_condition(filters)
+
+    assert_equal '(name ~ *) OR (name ~ a*)', result
+  end
+
+  test "#build_scoped_search_condition(filters) for unlimited filter" do
+    auth    = Authorizer.new(FactoryGirl.create(:user))
+    filters = [FactoryGirl.build(:filter)]
+    result  = auth.build_scoped_search_condition(filters)
+
+    assert_equal '(1=1)', result
+  end
+
+  test "#build_scoped_search_condition(filters) for limited and unlimited filter" do
+    auth    = Authorizer.new(FactoryGirl.create(:user))
+    filters = [FactoryGirl.build(:filter, :on_name_all), FactoryGirl.build(:filter)]
+    result  = auth.build_scoped_search_condition(filters)
+
+    assert_equal '(name ~ *) OR (1=1)', result
+  end
+
+  test "#build_scoped_search_condition(filters) for empty filter" do
+    auth    = Authorizer.new(FactoryGirl.create(:user))
+    filters = [FactoryGirl.build(:filter, :search => '')]
+    result  = auth.build_scoped_search_condition(filters)
+
+    assert_equal '(1=1)', result
+  end
+
+  test "#can? with empty base collection set" do
+    domain     = FactoryGirl.create(:domain)
+    permission = Permission.find_by_name('view_domains')
+    filter     = FactoryGirl.create(:filter, :role => @role, :permissions => [permission])
+    auth       = Authorizer.new(@user, :collection => [])
+
+    refute auth.can?(:view_domains, domain)
+  end
+
+  test "#can? with excluding base collection set" do
+    permission = Permission.find_by_name('view_domains')
+    filter1    = FactoryGirl.create(:filter, :on_name_starting_with_a,
+                                     :role => @role, :permissions => [permission])
+    domain1    = FactoryGirl.create(:domain, :name => 'a-domain.to-be-found.com')
+    domain2    = FactoryGirl.create(:domain, :name => 'another-domain.to-be-found.com')
+    auth       = Authorizer.new(@user, :collection => [domain2])
+
+    refute auth.can?(:view_domains, domain1)
+    assert auth.can?(:view_domains, domain2)
+  end
+end
diff --git a/test/unit/cached_user_role_test.rb b/test/unit/cached_user_role_test.rb
new file mode 100644
index 000000000..eb7dd466c
--- /dev/null
+++ b/test/unit/cached_user_role_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class CachedUserRoleTest < ActiveSupport::TestCase
+  # test "the truth" do
+  #   assert true
+  # end
+end
diff --git a/test/unit/cached_usergroup_member_test.rb b/test/unit/cached_usergroup_member_test.rb
new file mode 100644
index 000000000..e3004b390
--- /dev/null
+++ b/test/unit/cached_usergroup_member_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class CachedUsergroupMemberTest < ActiveSupport::TestCase
+  # test "the truth" do
+  #   assert true
+  # end
+end
diff --git a/test/unit/common_parameter_test.rb b/test/unit/common_parameter_test.rb
index 402d11bf4..93cd43ed8 100644
--- a/test/unit/common_parameter_test.rb
+++ b/test/unit/common_parameter_test.rb
@@ -43,58 +43,4 @@ class CommonParameterTest < ActiveSupport::TestCase
     assert !parameter2.valid?
     assert  parameter2.errors.full_messages[0] == "Name has already been taken"
   end
-
-  def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_global_variables"
-      role.permissions = ["#{operation}_global_variables".to_sym]
-      @one.roles = [role]
-      @one.save!
-    end
-    User.current = @one
-  end
-
-  test "user with create permissions should be able to create" do
-    setup_user "create"
-    record =  CommonParameter.create :name => "dummy", :value => "value"
-    assert record.valid?
-    assert !record.new_record?
-  end
-
-  test "user with view permissions should not be able to create" do
-    setup_user "view"
-    record =  CommonParameter.create :name => "dummy", :value => "value"
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy"
-    record =  CommonParameter.first
-    assert record.destroy
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record =  CommonParameter.first
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit"
-    record      =  CommonParameter.first
-    record.name = "renamed"
-    assert record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record      =  CommonParameter.first
-    record.name = "renamed"
-    assert !record.save
-  end
-
 end
diff --git a/test/unit/compute_resource_test.rb b/test/unit/compute_resource_test.rb
index 98deaa647..96f4ff297 100644
--- a/test/unit/compute_resource_test.rb
+++ b/test/unit/compute_resource_test.rb
@@ -5,87 +5,6 @@ class ComputeResourceTest < ActiveSupport::TestCase
     User.current = users(:admin)
   end
 
-  def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_compute_resources"
-      role.permissions = ["#{operation}_compute_resources".to_sym]
-      @one.roles = [role]
-      @one.compute_resources.destroy_all
-      @one.save!
-    end
-    User.current = @one
-  end
-
-  test "user with edit permissions should be able to edit when permitted" do
-    setup_user "edit"
-    as_admin do
-      @one.compute_resources = [compute_resources(:mycompute)]
-    end
-    record = compute_resources(:mycompute)
-    assert record.update_attributes(:name => "testing")
-    assert record.valid?
-  end
-
-  test "user with edit permissions should not be able to edit when not permitted" do
-    setup_user "edit"
-    record = compute_resources(:yourcompute)
-    assert !record.update_attributes(:name => "testing")
-    assert record.valid?
-  end
-
-  test "user with edit permissions should not be able to edit when unconstrained" do
-    setup_user "edit"
-    record = compute_resources(:mycompute)
-    assert !record.update_attributes(:name => "testing")
-    assert record.valid?
-  end
-
-  test "user with destroy permissions should be able to destroy when permitted" do
-    setup_user "destroy"
-    as_admin do
-      @one.compute_resources = [compute_resources(:mycompute)]
-    end
-    record = compute_resources(:mycompute)
-    assert record.destroy
-  end
-
-  test "user with destroy permissions should not be able to destroy when not permitted" do
-    setup_user "destroy"
-    record = compute_resources(:yourcompute)
-    assert !record.destroy
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    as_admin do
-      @one.compute_resources = [compute_resources(:mycompute)]
-    end
-    record = compute_resources(:mycompute)
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit"
-    as_admin do
-      @one.compute_resources = [compute_resources(:mycompute)]
-    end
-    record      =  compute_resources(:mycompute)
-    record.name = "renamed"
-    assert record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    as_admin do
-      @one.compute_resources = [compute_resources(:mycompute)]
-    end
-    record      =  compute_resources(:mycompute)
-    record.name = "renamed"
-    assert !record.save
-  end
-
   test "password is saved encrypted when updated" do
     compute_resource = compute_resources(:one)
     compute_resource.password = "123456"
diff --git a/test/unit/domain_parameter_test.rb b/test/unit/domain_parameter_test.rb
index 36e0a6135..6a8874eaf 100644
--- a/test/unit/domain_parameter_test.rb
+++ b/test/unit/domain_parameter_test.rb
@@ -26,81 +26,11 @@ class DomainParameterTest < ActiveSupport::TestCase
     assert parameter2.valid?
   end
 
-  def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_domains"
-      role.permissions = ["#{operation}_domains".to_sym]
-      @one.roles = [role]
-      @one.domains.destroy_all
-      @one.save!
+  def setup_user operation, type = 'domains'
+    super(operation, type) do |user|
+      user.domains.destroy_all
     end
-    User.current = @one
   end
 
-  test "user with create permissions should be able to create when permitted" do
-    setup_user "create"
-    as_admin do
-      @one.domains = [domains(:mydomain)]
-    end
-    record =  DomainParameter.create :name => "dummy", :value => "value", :reference_id => domains(:mydomain).id
-    assert record.valid?
-    assert !record.new_record?
-  end
-
-  test "user with create permissions should not be able to create when not permitted" do
-    setup_user "create"
-    as_admin do
-      @one.domains = [domains(:mydomain)]
-    end
-    record =  DomainParameter.create :name => "dummy", :value => "value", :reference_id => domains(:yourdomain).id
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with create permissions should be able to create when unconstrained" do
-    setup_user "create"
-    as_admin do
-      @one.domains.destroy_all
-    end
-    record =  DomainParameter.create :name => "dummy", :value => "value", :reference_id => domains(:mydomain).id
-    assert record.valid?
-    assert !record.new_record?
-  end
-
-  test "user with view permissions should not be able to create" do
-    setup_user "view"
-    record =  DomainParameter.create :name => "dummy", :value => "value", :reference_id => domains(:mydomain).id
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy"
-    record =  DomainParameter.first
-    assert record.destroy
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record =  DomainParameter.first
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit"
-    record      =  DomainParameter.first
-    record.name = "renamed"
-    assert record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record      =  DomainParameter.first
-    record.name = "renamed"
-    assert !record.save
-  end
 end
 
diff --git a/test/unit/domain_test.rb b/test/unit/domain_test.rb
index 8091f4823..2185d4ad5 100644
--- a/test/unit/domain_test.rb
+++ b/test/unit/domain_test.rb
@@ -72,83 +72,6 @@ class DomainTest < ActiveSupport::TestCase
     hosts(:one)
   end
 
-  def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_domains"
-      role.permissions = ["#{operation}_domains".to_sym]
-      @one.roles = [role]
-      @one.domains.destroy_all
-      @one.save!
-    end
-    User.current = @one
-  end
-
-  test "user with edit permissions should be able to edit when permitted" do
-    setup_user "edit"
-    as_admin do
-      @one.domains = [domains(:mydomain)]
-    end
-    record =  Domain.find_by_name "mydomain.net"
-    assert record.update_attributes(:name => "testing")
-    assert record.valid?
-  end
-
-  test "user with edit permissions should not be able to edit when not permitted" do
-    setup_user "edit"
-    as_admin do
-      @one.domains = [domains(:yourdomain)]
-    end
-    record =  Domain.find_by_name "mydomain.net"
-    assert !record.update_attributes(:name => "testing")
-    assert record.valid?
-  end
-
-  test "user with edit permissions should be able to edit when unconstrained" do
-    setup_user "edit"
-    record =  Domain.first
-    assert record.update_attributes(:name => "testing")
-    assert record.valid?
-  end
-
-  test "user with view permissions should not be able to create when not permitted" do
-    setup_user "view"
-    record =  Domain.create :name => "dummy", :fullname => "dummy.com"
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy"
-    record = domains(:useless)
-    record.interfaces.clear
-    record.hosts.clear
-    assert record.destroy
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record =  Domain.first
-    record.subnets.clear
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit"
-    record      =  Domain.first
-    record.name = "renamed"
-    assert record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record      =  Domain.first
-    record.name = "renamed"
-    assert !record.save
-  end
-
   test "should query local nameservers when enabled" do
     Setting['query_local_nameservers'] = true
     assert Domain.first.nameservers.empty?
diff --git a/test/unit/ensure_no_cycle_test.rb b/test/unit/ensure_no_cycle_test.rb
new file mode 100644
index 000000000..c5fe2043f
--- /dev/null
+++ b/test/unit/ensure_no_cycle_test.rb
@@ -0,0 +1,57 @@
+require 'test_helper'
+
+class EnsureNoCycleTest < ActiveSupport::TestCase
+  def setup
+    base = []
+    base.push edge(1, 2)
+    base.push edge(2, 3)
+    base.push edge(3, 4)
+    base.push edge(3, 5)
+    base.push edge(2, 6)
+    @graph = ActiveRecord::Base::EnsureNoCycle.new(base, :source, :target)
+  end
+
+  def edge(source, target)
+    OpenStruct.new(:source => source, :target => target)
+  end
+
+  test "#tsort_each_node iterates over all nodes" do
+    found = []
+    @graph.tsort_each_node { |node| found.push node }
+    assert_equal [1, 2, 3, 4, 5, 6], found
+  end
+
+  test "#tsort_each_child(node) finds all children for node" do
+    found = []
+    assert @graph.tsort_each_child(1) { |node| found.push node }
+    assert_equal [2], found
+
+    found = []
+    assert @graph.tsort_each_child(2) { |node| found.push node }
+    assert_equal [3, 6], found
+
+    found = []
+    assert @graph.tsort_each_child(3) { |node| found.push node }
+    assert_equal [4, 5], found
+
+    found = []
+    assert @graph.tsort_each_child(4) { |node| found.push node }
+    assert_equal [], found
+  end
+
+  test "#ensure detects cycle and raises an exception" do
+    record = edge(6, 1)
+    record.errors = ActiveModel::Errors.new(record)
+    assert_raises Foreman::CyclicGraphException do
+      @graph.ensure(record)
+    end
+    assert_present record.errors[:base], 'cycle did not add error to record'
+  end
+
+  test "#ensure passes when record does not create cycle" do
+    record = edge(2, 4)
+    assert_nothing_raised do
+      assert @graph.ensure(record)
+    end
+  end
+end
diff --git a/test/unit/filter_test.rb b/test/unit/filter_test.rb
new file mode 100644
index 000000000..bfb42f4ad
--- /dev/null
+++ b/test/unit/filter_test.rb
@@ -0,0 +1,157 @@
+require 'test_helper'
+
+class FilterTest < ActiveSupport::TestCase
+  test "#unlimited?" do
+    f = Factory.build :filter
+    assert_nil f.search, 'default filter is not unlimited'
+    assert f.unlimited?
+  end
+
+  test "#limited?" do
+    f = Factory.build :filter, :on_name_all
+    refute_nil f.search, 'filter is not limited'
+    assert f.limited?
+  end
+
+  test "#limited? even for empty string" do
+    f = Factory.build :filter
+    f.search = ''
+    assert f.limited?
+  end
+
+  test ".limited" do
+    f = Factory.create(:filter, :on_name_all)
+    assert_include Filter.limited, f
+  end
+
+  test ".unlimited" do
+    f = Factory.create(:filter)
+    assert_include Filter.unlimited, f
+  end
+
+  test "#resource_type for empty permissions collection" do
+    f = Factory.build(:filter)
+    f.permissions = []
+    assert_nil f.resource_type
+  end
+
+  test "#resource_type" do
+    f = Factory.build(:filter)
+    f.stub :permissions, [ OpenStruct.new(:resource_type => 'test') ] do
+      assert_equal 'test', f.resource_type
+    end
+  end
+
+  test ".get_resource_class known" do
+    assert_equal Bookmark, Filter.get_resource_class('Bookmark')
+  end
+
+  test ".get_resource_class unknown" do
+    assert_nil Filter.get_resource_class('BookmarkThatDoesNotExist')
+  end
+
+  test "#resource_class" do
+    f = Factory.build(:filter, :resource_type => 'Bookmark')
+    Filter.stub :get_resource_class, Architecture do
+      assert_equal Architecture, f.resource_class
+    end
+  end
+
+  test "#granular? for unknown resource type" do
+    f = Factory.build(:filter, :resource_type => 'BookmarkThatDoesNotExist')
+    refute f.granular?
+  end
+
+  test "#granular?" do
+    f = Factory.build(:filter, :resource_type => 'Domain')
+    assert f.granular?
+  end
+
+  test "unlimited filters have nilified search string" do
+    f = Factory.build(:filter, :search => 'name ~ a*', :unlimited => '1')
+    assert f.valid?
+    assert_nil f.search
+
+    f = Factory.build(:filter, :search => '', :unlimited => '1')
+    assert f.valid?
+    assert_nil f.search
+
+    f = Factory.build(:filter, :search => 'name ~ a*', :unlimited => '0')
+    assert f.valid?
+    assert_equal 'name ~ a*', f.search
+  end
+
+  test "filter with organization set is always limited before validation" do
+    o = Factory.create :organization
+    f = Factory.build(:filter, :search => '', :unlimited => '1', :organization_ids => [o.id])
+    assert f.valid?
+    assert f.limited?
+    assert_include f.taxonomy_search, "(organization_id = #{o.id})"
+    assert_not_include f.taxonomy_search, ' and '
+    assert_not_include f.taxonomy_search, ' or '
+  end
+
+  test "filter remains unlimited when no organization assigned" do
+    f = Factory.build(:filter, :search => '', :unlimited => '1', :organization_ids => [])
+    assert f.valid?
+    assert f.unlimited?
+    assert_empty f.taxonomy_search
+  end
+
+  test "filter remains set to unlimited when no taxonomy assigned and has empty search" do
+    f = Factory.build(:filter, :search => '', :unlimited => '0', :organization_ids => [],
+                      :location_ids => [])
+    assert f.valid?
+    assert f.unlimited?
+    assert_empty f.taxonomy_search
+  end
+
+  test "filter with location set is always limited before validation" do
+    l = Factory.create :location
+    f = Factory.build(:filter, :search => '', :unlimited => '1', :location_ids => [l.id])
+    assert f.valid?
+    assert f.limited?
+    assert_include f.taxonomy_search, "(location_id = #{l.id})"
+  end
+
+  test "filter with location set is always limited before validation" do
+    o1 = Factory.create :organization
+    o2 = Factory.create :organization
+    l  = Factory.create :location
+    f  = Factory.build(:filter, :search => '', :unlimited => '1',
+                       :organization_ids => [o1.id, o2.id], :location_ids => [l.id])
+    assert f.valid?
+    assert f.limited?
+    assert_include f.taxonomy_search, "(location_id = #{l.id})"
+    assert_include f.taxonomy_search, "organization_id = #{o1.id}"
+    assert_include f.taxonomy_search, "organization_id = #{o2.id}"
+  end
+
+  test "removing all organizations and locations from filter nilify taxonomy search" do
+    o1 = Factory.create :organization
+    o2 = Factory.create :organization
+    l  = Factory.create :location
+    f  = Factory.create(:filter, :search => '', :unlimited => '1',
+                        :organization_ids => [o1.id, o2.id], :location_ids => [l.id])
+
+    f.update_attributes :organization_ids => [], :location_ids => []
+    assert f.valid?
+    assert f.unlimited?
+    assert_nil f.taxonomy_search
+  end
+
+  test "search string composition" do
+    f = Factory.build :filter, :search => nil, :taxonomy_search => nil
+    assert_equal '', f.search_condition
+
+    f = Factory.build :filter, :search => 'domain ~ test*', :taxonomy_search => nil
+    assert_equal 'domain ~ test*', f.search_condition
+
+    f = Factory.build :filter, :search => nil, :taxonomy_search => 'organization_id = 1'
+    assert_equal 'organization_id = 1', f.search_condition
+
+    f = Factory.build :filter, :search => 'domain ~ test*', :taxonomy_search => 'organization_id = 1'
+    assert_equal '(domain ~ test*) and (organization_id = 1)', f.search_condition
+  end
+
+end
diff --git a/test/unit/group_parameter_test.rb b/test/unit/group_parameter_test.rb
index 67546f958..a4b5fa4d5 100644
--- a/test/unit/group_parameter_test.rb
+++ b/test/unit/group_parameter_test.rb
@@ -28,81 +28,5 @@ class GroupParameterTest < ActiveSupport::TestCase
     assert parameter2.valid?
   end
 
-  def setup_user operation, type = "hostgroups"
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_#{type}"
-      role.permissions = ["#{operation}_#{type}".to_sym]
-      @one.roles = [role]
-      @one.hostgroups.destroy_all
-      @one.save!
-    end
-    User.current = @one
-  end
-
-  test "user with create permissions should be able to create when permitted" do
-    setup_user "create", "params"
-    as_admin do
-      @one.hostgroups = [hostgroups(:common)]
-    end
-    record =  GroupParameter.create :name => "dummy", :value => "value", :reference_id => hostgroups(:common).id
-    assert record.valid?
-    assert !record.new_record?
-  end
-
-  test "user with create permissions should not be able to create when not permitted" do
-    setup_user "create"
-    as_admin do
-      @one.hostgroups = [hostgroups(:common)]
-    end
-    record =  GroupParameter.create :name => "dummy", :value => "value", :reference_id => hostgroups(:unusual).id
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with create permissions should be able to create when unconstrained" do
-    setup_user "create", "params"
-    as_admin do
-      @one.hostgroups.destroy_all
-    end
-    record =  GroupParameter.create :name => "dummy", :value => "value", :reference_id => hostgroups(:common).id
-    assert record.valid?
-    assert !record.new_record?
-  end
-
-  test "user with view permissions should not be able to create when not permitted" do
-    setup_user "view"
-    record =  GroupParameter.create :name => "dummy", :value => "value", :reference_id => hostgroups(:common).id
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy", "params"
-    record =  GroupParameter.first
-    assert record.destroy
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record =  GroupParameter.first
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit", "params"
-    record      =  GroupParameter.first
-    record.name = "renamed"
-    assert record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record      =  GroupParameter.first
-    record.name = "renamed"
-    assert !record.save
-  end
 end
 
diff --git a/test/unit/helpers/host_groups_helper_test.rb b/test/unit/helpers/host_groups_helper_test.rb
index 20322766a..958b7a90e 100644
--- a/test/unit/helpers/host_groups_helper_test.rb
+++ b/test/unit/helpers/host_groups_helper_test.rb
@@ -6,6 +6,7 @@ class HostGroupsHelperTest < ActionView::TestCase
 
   test "should have the full string of the parent class if the child is a substring" do
     test_group = Hostgroup.create(:name => "test/st")
+    stubs(:url_for).returns('/some/url')
     assert_match /test\/st/, label_with_link(test_group)
     refute_match /te\/st/, label_with_link(test_group)
   end
diff --git a/test/unit/host_class_test.rb b/test/unit/host_class_test.rb
index aaccddfb9..34ba7e801 100644
--- a/test/unit/host_class_test.rb
+++ b/test/unit/host_class_test.rb
@@ -9,47 +9,4 @@ class HostClassTest < ActiveSupport::TestCase
     EnvironmentClass.create(:puppetclass_id => puppetclasses(:two).id, :environment_id => environments(:production).id )
   end
 
-  test "non-admin user with permission :edit_classes can add puppetclass to host" do
-    # role "manager" has permission :edit_classes
-    User.current.roles << [roles(:manager)]
-    assert_difference('HostClass.count') do
-      host = hosts(:one)
-      puppetclass = puppetclasses(:two)
-      assert Host.my_hosts.include?(host)
-      assert host.update_attributes :puppetclass_ids => (host.puppetclass_ids + Array.wrap(puppetclass.id))
-    end
-  end
-
-  test "non-admin user without permission :edit_classes cannot add puppetclass to host" do
-    # do not assign any role to Current.user
-    assert_difference('HostClass.count', 0) do
-      host = hosts(:one)
-      puppetclass = puppetclasses(:two)
-      assert Host.my_hosts.include?(host)
-      assert_raises(ActiveRecord::RecordNotSaved) do
-        refute host.update_attributes :puppetclass_ids => (host.puppetclass_ids + Array.wrap(puppetclass.id))
-      end
-    end
-  end
-
-  test "non-admin user with permission :edit_classes can remove puppetclass from host" do
-    # role "manager" has permission :edit_classes
-    User.current.roles << [roles(:manager)]
-    assert_difference('HostClass.count', -1) do
-      host = hosts(:one)
-      assert Host.my_hosts.include?(host)
-      assert host.update_attributes :puppetclass_ids => []
-    end
-  end
-
-  test "non-admin user without permission :edit_classes cannot remove puppetclass from host" do
-    # do not assign any role to Current.user
-    assert_difference('HostClass.count', 0) do
-      host = hosts(:one)
-      puppetclass = puppetclasses(:two)
-      assert Host.my_hosts.include?(host)
-      refute host.update_attributes :puppetclass_ids => []
-    end
-  end
-
 end
diff --git a/test/unit/host_parameter_test.rb b/test/unit/host_parameter_test.rb
index cd21fcdcf..2f359dffd 100644
--- a/test/unit/host_parameter_test.rb
+++ b/test/unit/host_parameter_test.rb
@@ -35,83 +35,4 @@ class HostParameterTest < ActiveSupport::TestCase
     assert @parameter2.valid?
   end
 
-  def setup_user operation, type = "params"
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_#{type}"
-      role.permissions = ["#{operation}_#{type}".to_sym]
-      @one.roles      = [role]
-      @one.domains.destroy_all
-      @one.hostgroups.destroy_all
-      @one.user_facts.destroy_all
-      @one.save!
-    end
-    User.current = @one
-  end
-
-  test "user with create permissions should be able to create when permitted" do
-    setup_user "create"
-    as_admin do
-      @one.domains = [domains(:mydomain)]
-      @one.save!
-    end
-    host_parameter = HostParameter.create! :name => "dummy", :value => "value", :reference_id => hosts(:one).id
-    assert host_parameter
-  end
-
-  test "user with create permissions should not be able to create when not permitted" do
-    setup_user "create"
-    as_admin do
-      @one.hostgroups = [hostgroups(:common)]
-      @one.save!
-      hosts(:one).update_attribute :hostgroup, hostgroups(:unusual)
-    end
-    record = HostParameter.create :name => "dummy", :value => "value", :reference_id => hosts(:one).id
-    assert record.valid?
-    assert !record.save
-  end
-
-  test "user with create permissions should be able to create when unconstrained" do
-    setup_user "create"
-    as_admin do
-      @one.domains.destroy_all
-    end
-    host_parameter = HostParameter.create! :name => "dummy", :value => "value", :reference_id => hosts(:one).id
-    assert host_parameter
-  end
-
-  test "user with view permissions should not be able to create" do
-    setup_user "view", "hosts"
-    record = HostParameter.create :name => "dummy", :value => "value", :reference_id => hosts(:one).id
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy"
-    record =  HostParameter.first
-    assert record.destroy
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record =  HostParameter.first
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit"
-    record      =  HostParameter.first
-    record.name = "renamed"
-    assert record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record      =  HostParameter.first
-    record.name = "renamed"
-    assert !record.save
-  end
 end
diff --git a/test/unit/host_test.rb b/test/unit/host_test.rb
index 692ea11c0..355e4f409 100644
--- a/test/unit/host_test.rb
+++ b/test/unit/host_test.rb
@@ -351,207 +351,6 @@ class HostTest < ActiveSupport::TestCase
     assert host.disabled?
   end
 
-  def setup_user_and_host
-    @one            = users(:one)
-    @one.hostgroups.destroy_all
-    @one.domains.destroy_all
-    @one.user_facts.destroy_all
-    @one.save!
-    @host           = hosts(:one)
-    @host.owner     = users(:two)
-    @host.save!
-    User.current    = @one
-  end
-
-  def setup_filtered_user
-    # Can't use `setup_user_and_host` as it deletes the UserFacts
-    @one             = users(:one)
-    @one.hostgroups.destroy_all
-    @one.domains.destroy_all
-    @one.user_facts  = [user_facts(:one)]
-    @one.facts_andor = "and"
-    @one.save!
-    User.current    = @one
-  end
-
-  test "host cannot be edited without permission" do
-    setup_user_and_host
-    as_admin do
-      @one.roles = [Role.find_by_name("Viewer")]
-    end
-    assert !@host.update_attributes(:comment => "blahblahblah")
-    assert_match /do not have permission/, @host.errors.full_messages.join("\n")
-  end
-
-  test "any host can be edited when permitted" do
-    setup_user_and_host
-    as_admin do
-      @one.roles      = [Role.find_by_name("Edit hosts")]
-    end
-    assert @host.update_attributes(:comment => "blahblahblah")
-    assert_no_match /do not have permission/, @host.errors.full_messages.join("\n")
-  end
-
-  test "hosts can be edited when domains permit" do
-    setup_user_and_host
-    as_admin do
-      @one.roles      = [Role.find_by_name("Edit hosts")]
-      @one.domains    = [Domain.find_by_name("mydomain.net")]
-    end
-    assert @host.update_attributes(:comment => "blahblahblah")
-    assert_no_match /do not have permission/, @host.errors.full_messages.join("\n")
-  end
-
-  test "hosts cannot be edited when domains deny" do
-    setup_user_and_host
-    as_admin do
-      @one.roles      = [Role.find_by_name("Edit hosts")]
-      @one.domains    = [Domain.find_by_name("yourdomain.net")]
-    end
-    assert !@host.update_attributes(:comment => "blahblahblah")
-    assert_match /do not have permission/, @host.errors.full_messages.join("\n")
-  end
-
-  test "host cannot be created without permission" do
-    setup_user_and_host
-    as_admin do
-      @one.roles = [Role.find_by_name("Viewer")]
-    end
-    host = Host.create(:name => "blahblah", :mac => "aabbecddee19", :ip => "2.3.4.09",
-                       :domain => domains(:mydomain),  :operatingsystem => operatingsystems(:centos5_3),
-                       :architecture => architectures(:x86_64), :environment => environments(:production), :puppet_proxy => smart_proxies(:puppetmaster),
-                       :subnet => subnets(:one), :disk => "empty partition")
-    assert host.new_record?
-    assert_match /do not have permission/, host.errors.full_messages.join("\n")
-  end
-
-  test "any host can be created when permitted" do
-    setup_user_and_host
-    as_admin do
-      @one.roles = [Role.find_by_name("Create hosts")]
-    end
-    host = Host.create(:name => "blahblah", :mac => "aabbecddee19", :ip => "2.3.4.11",
-                       :domain => domains(:mydomain),  :operatingsystem => operatingsystems(:centos5_3),  :puppet_proxy => smart_proxies(:puppetmaster),
-                       :architecture => architectures(:x86_64), :environment => environments(:production),
-                       :subnet => subnets(:one), :disk => "empty partition")
-    assert !host.new_record?
-    assert_no_match /do not have permission/, host.errors.full_messages.join("\n")
-  end
-
-  test "hosts can be created when hostgroups permit" do
-    setup_user_and_host
-    as_admin do
-      @one.roles      = [Role.find_by_name("Create hosts")]
-      @one.hostgroups = [Hostgroup.find_by_name("Common")]
-    end
-    host = Host.create(:name => "blahblah", :mac => "aabbecddee19", :ip => "2.3.4.4",
-                       :domain => domains(:mydomain),  :operatingsystem => operatingsystems(:centos5_3),
-                       :architecture => architectures(:x86_64), :environment => environments(:production),
-                       :subnet => subnets(:one),
-                       :disk => "empty partition", :hostgroup => hostgroups(:common))
-    assert !host.new_record?
-    assert_no_match /do not have permission/, host.errors.full_messages.join("\n")
-  end
-
-  test "hosts cannot be created when hostgroups deny" do
-    setup_user_and_host
-    as_admin do
-      @one.roles      = [Role.find_by_name("Create hosts")]
-      @one.hostgroups = [Hostgroup.find_by_name("Unusual")]
-    end
-    host = Host.create(:name => "blahblah", :mac => "aabbecddee19", :ip => "2.3.4.9",
-                       :domain => domains(:mydomain),  :operatingsystem => operatingsystems(:centos5_3),
-                       :architecture => architectures(:x86_64), :environment => environments(:production),
-                       :subnet => subnets(:one),
-                       :disk => "empty partition", :hostgroup => hostgroups(:common))
-    assert host.new_record?
-    assert_match /do not have permission/, host.errors.full_messages.join("\n")
-  end
-
-  test "host cannot be destroyed without permission" do
-    setup_user_and_host
-    as_admin do
-      @one.roles = [Role.find_by_name("Viewer")]
-    end
-    assert !@host.destroy
-    assert_match /do not have permission/, @host.errors.full_messages.join("\n")
-  end
-
-  test "any host can be destroyed when permitted" do
-    setup_user_and_host
-    as_admin do
-      @one.roles = [Role.find_by_name("Destroy hosts")]
-      @host.host_classes.delete_all
-      assert @host.destroy
-    end
-    assert_no_match /do not have permission/, @host.errors.full_messages.join("\n")
-  end
-
-  test "hosts can be destroyed when ownership permits" do
-    setup_user_and_host
-    as_admin do
-      @one.roles = [Role.find_by_name("Destroy hosts")]
-      @host.update_attribute :owner,  users(:one)
-      @host.host_classes.delete_all
-      assert @host.destroy
-    end
-    assert_no_match /do not have permission/, @host.errors.full_messages.join("\n")
-  end
-
-  test "hosts cannot be destroyed when ownership denies" do
-    setup_user_and_host
-    as_admin do
-      @one.roles   = [Role.find_by_name("Destroy hosts")]
-      @one.domains = [domains(:yourdomain)] # This does not grant access but does ensure that access is constrained
-      @host.owner  = users(:two)
-      @host.save!
-    end
-    assert !@host.destroy
-    assert_match /do not have permission/, @host.errors.full_messages.join("\n")
-  end
-
-  test "fact filters restrict the my_hosts scope" do
-    setup_filtered_user
-    assert_equal 1, Host.my_hosts.count
-    assert_equal 'my5name.mydomain.net', Host.my_hosts.first.name
-  end
-
-  test "sti types altered in memory with becomes are still contained in my_hosts scope" do
-    class Host::Valid < Host::Base ; belongs_to :domain ; end
-    h = Host::Valid.new :name => "mytestvalidhost.foo.com"
-    setup_user_and_host
-    as_admin do
-      @one.domains = [domains(:yourdomain)] # ensure it matches the user filters
-      h.update_attribute :domain,  domains(:yourdomain)
-    end
-    h_new = h.becomes(Host::Managed) # change the type to break normal AR `==` method
-    assert Host::Base.my_hosts.include?(h_new)
-  end
-
-  test "host can be edited when user fact filter permits" do
-    setup_filtered_user
-    as_admin do
-      @one.roles  = [Role.find_by_name("Edit hosts")]
-      @host       = hosts(:one)
-      @host.owner = users(:two)
-      @host.save!
-    end
-    assert @host.update_attributes(:comment => "blahblahblah")
-    assert_no_match /do not have permission/, @host.errors.full_messages.join("\n")
-  end
-
-  test "host cannot be edited when user fact filter denies" do
-    setup_filtered_user
-    as_admin do
-      @one.roles  = [Role.find_by_name("Edit hosts")]
-      @host       = hosts(:two)
-      @host.owner = users(:two)
-      @host.save!
-    end
-    assert !@host.update_attributes(:comment => "blahblahblah")
-    assert_match /do not have permission/, @host.errors.full_messages.join("\n")
-  end
-
   test "a fqdn Host should be assigned to a domain if such domain exists" do
     domain = domains(:mydomain)
     host = Host.create :name => "host.mydomain.net", :mac => "aabbccddeaff", :ip => "2.3.04.03",
@@ -941,9 +740,13 @@ class HostTest < ActiveSupport::TestCase
     @one = users(:one)
     # add permission for user :one
     as_admin do
+      filter = FactoryGirl.build(:filter)
+      filter.permissions = [ Permission.find_by_name('edit_hosts') ]
+      filter.save!
       role = Role.find_or_create_by_name :name => "testing_role"
-      role.permissions = [:edit_hosts]
-      @one.roles = [role]
+      role.filters = [ filter ]
+      role.save!
+      @one.roles = [ role ]
       @one.save!
     end
     h = hosts(:one)
diff --git a/test/unit/hostgroup_class_test.rb b/test/unit/hostgroup_class_test.rb
index ac1ef39c1..337531fb6 100644
--- a/test/unit/hostgroup_class_test.rb
+++ b/test/unit/hostgroup_class_test.rb
@@ -18,47 +18,4 @@ class HostgroupClassTest < ActiveSupport::TestCase
       end
     end
   end
-
-  test "non-admin user with permission :edit_hostgroups can add puppetclass to hostgroup" do
-    # role "manager" has permission :edit_classes
-    User.current.roles << [roles(:manager)]
-    assert_difference('HostgroupClass.count') do
-      hostgroup = hostgroups(:common)
-      puppetclass = puppetclasses(:two)
-      assert Hostgroup.my_groups.include?(hostgroup)
-      assert hostgroup.update_attributes :puppetclass_ids => (hostgroup.puppetclass_ids + Array.wrap(puppetclass.id))
-    end
-  end
-
-  test "non-admin user without permission :edit_hostgroups cannot add puppetclass to hostgroup" do
-    # do not assign any role to Current.user
-    assert_difference('HostgroupClass.count', 0) do
-      hostgroup = hostgroups(:common)
-      puppetclass = puppetclasses(:two)
-      assert Hostgroup.my_groups.include?(hostgroup)
-      assert_raises(ActiveRecord::RecordNotSaved) do
-        refute hostgroup.update_attributes :puppetclass_ids => (hostgroup.puppetclass_ids + Array.wrap(puppetclass.id))
-      end
-    end
-  end
-
-  test "non-admin user with permission :edit_hostgroups can remove puppetclass from hostgroup" do
-    # role "manager" has permission :edit_classes
-    User.current.roles << [roles(:manager)]
-    assert_difference('HostgroupClass.count', -1) do
-      hostgroup = hostgroups(:common)
-      assert Hostgroup.my_groups.include?(hostgroup)
-      assert hostgroup.update_attributes :puppetclass_ids => []
-    end
-  end
-
-  test "non-admin user without permission :edit_hostgroups cannot remove puppetclass from hostgroup" do
-    # do not assign any role to Current.user
-    assert_difference('HostgroupClass.count', 0) do
-      hostgroup = hostgroups(:common)
-      puppetclass = puppetclasses(:two)
-      assert Hostgroup.my_groups.include?(hostgroup)
-      refute hostgroup.update_attributes :puppetclass_ids => []
-    end
-  end
 end
diff --git a/test/unit/hostgroup_test.rb b/test/unit/hostgroup_test.rb
index e41b88d7c..d56215160 100644
--- a/test/unit/hostgroup_test.rb
+++ b/test/unit/hostgroup_test.rb
@@ -28,64 +28,7 @@ class HostgroupTest < ActiveSupport::TestCase
   end
 
   def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_hostgroup"
-      role.permissions = ["#{operation}_hostgroups".to_sym]
-      @one.roles = [role]
-      @one.save!
-    end
-    User.current = @one
-  end
-
-  test "user with create permissions should be able to create" do
-    setup_user "create"
-    record =  Hostgroup.create :name => "dummy"
-    assert record.valid?
-    assert !record.new_record?
-  end
-
-  test "user with view permissions should not be able to create" do
-    setup_user "view"
-    record =  Hostgroup.create :name => "dummy"
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy"
-    record = hostgroups(:common)
-    as_admin do
-      record.hosts.destroy_all
-      record.hostgroup_classes.destroy_all
-      assert record.destroy
-    end
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record = hostgroups(:common)
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit"
-    record      =  Hostgroup.first
-    record.name = "renamed"
-    assert record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record      =  Hostgroup.first
-    record.name = "renamed"
-    as_admin do
-      record.hosts.destroy_all
-    end
-    assert !record.save
-    assert record.valid?
+    super operation, "hostgroups"
   end
 
   test "should be able to nest a group parameters" do
diff --git a/test/unit/lookup_value_test.rb b/test/unit/lookup_value_test.rb
index d4408a6d8..009fb0a1d 100644
--- a/test/unit/lookup_value_test.rb
+++ b/test/unit/lookup_value_test.rb
@@ -38,14 +38,15 @@ class LookupValueTest < ActiveSupport::TestCase
     end
   end
 
-  test "non-admin user cannot create lookup value if user has no matching host/hostgroup" do
-    # Host.my_hosts returns only hosts(:one)
+  test "non-admin user cannot view only his hosts restricted by filters" do
+    # Host.authorized(:view_hosts, Host) returns only hosts(:one)
     user = users(:one)
+    role = FactoryGirl.create(:role, :name => 'user_view_host_by_ip')
+    FactoryGirl.create(:filter, :role => role, :permissions => [Permission.find_by_name(:view_hosts)], :search => 'facts.ipaddress = 10.0.19.33')
+    user.roles<< [ role ]
     as_user :one do
-      refute Host.my_hosts.where(:name => hosts(:two).name).exists?
-      refute Hostgroup.my_groups.where(:name => hosts(:two).try(:hostgroup).try(:name)).exists?
-      lookup_value = LookupValue.new(valid_attrs2)
-      refute lookup_value.save
+      assert Host.authorized(:view_hosts, Host).where(:name => hosts(:one).name).exists?
+      refute Host.authorized(:view_hosts, Host).where(:name => hosts(:two).name).exists?
     end
   end
 
@@ -67,47 +68,12 @@ class LookupValueTest < ActiveSupport::TestCase
     end
   end
 
-  test "cannot update lookup value if user has no matching host/hostgroup" do
-    # Host.my_hosts returns only hosts(:one)
-    user = users(:one)
-    as_user :one do
-      refute Host.my_hosts.where(:name => hosts(:two).name).exists?
-      refute Hostgroup.my_groups.where(:name => hosts(:two).try(:hostgroup).try(:name)).exists?
-      refute lookup_values(:hosttwo).update_attributes(:value => "9000")
-    end
-  end
-
-  test "can create lookup value if user has matching host " do
-    # Host.my_hosts returns only hosts(:one)
-    user = users(:one)
-    as_user :one do
-      assert Host.my_hosts.where(:name => hosts(:one).name).exists?
-      refute Hostgroup.my_groups.where(:name => hosts(:one).try(:hostgroup).try(:name)).exists?
-      lookup_value = LookupValue.new(valid_attrs1)
-      assert_difference('LookupValue.count') do
-        assert lookup_value.save
-      end
-    end
-  end
-
-  test "can update lookup value if user has matching host " do
-    # Host.my_hosts returns only hosts(:one)
-    user = users(:one)
-    as_user :one do
-      assert Host.my_hosts.where(:name => hosts(:one).name).exists?
-      refute Hostgroup.my_groups.where(:name => hosts(:one).try(:hostgroup).try(:name)).exists?
-      assert lookup_values(:one).update_attributes(:value => "9000")
-    end
-  end
-
   test "can create lookup value if user has matching hostgroup " do
-    # Host.my_hosts returns only hosts(:one)
     user = users(:one)
     as_admin do
       assert user.hostgroups << hostgroups(:common)
     end
     as_user :one do
-      assert Hostgroup.my_groups.where(:name => "Common").exists?
       lookup_value = LookupValue.new(valid_attrs3)
       assert_difference('LookupValue.count') do
         assert lookup_value.save
@@ -115,18 +81,6 @@ class LookupValueTest < ActiveSupport::TestCase
     end
   end
 
-  test "can update lookup value if user has matching hostgroup " do
-    # Host.my_hosts returns only hosts(:one)
-    user = users(:one)
-    as_admin do
-      assert user.hostgroups << hostgroups(:common)
-    end
-    as_user :one do
-      assert Hostgroup.my_groups.where(:name => "Common").exists?
-      assert lookup_values(:hostgroupcommon).update_attributes(:value => "9000")
-    end
-  end
-
   test "smart class parameter accepts valid data" do
     as_admin do
       lk = LookupValue.new(:value => "---\nfoo:\n  bar: baz", :match => "hostgroup=Common", :lookup_key => lookup_keys(:six))
diff --git a/test/unit/medium_test.rb b/test/unit/medium_test.rb
index 7a7635938..08b3fe566 100644
--- a/test/unit/medium_test.rb
+++ b/test/unit/medium_test.rb
@@ -60,67 +60,6 @@ class MediumTest < ActiveSupport::TestCase
     assert !medium.destroy
   end
 
-  def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_media"
-      role.permissions = ["#{operation}_media".to_sym]
-      @one.roles = [role]
-      @one.save!
-    end
-    User.current = @one
-  end
-
-  test "user with create permissions should be able to create" do
-    setup_user "create"
-    record =  Medium.create :name => "dummy", :path => "http://hello"
-    assert record.valid?
-    assert !record.new_record?
-  end
-
-  test "user with view permissions should not be able to create" do
-    setup_user "view"
-    record =  Medium.create :name => "dummy", :path => "http://hello"
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy"
-    record =  Medium.first
-    as_admin do
-      record.hosts.delete_all
-      record.hostgroups.delete_all
-      assert record.destroy
-    end
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record =  Medium.first
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit"
-    record      =  Medium.first
-    record.name = "renamed"
-    assert record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record      =  Medium.first
-    record.name = "renamed"
-    as_admin do
-      record.hosts.delete_all
-    end
-    assert !record.save
-    assert record.valid?
-  end
-
   test "os family can be one of defined os families" do
     medium = Medium.new :name => "dummy", :path => "http://hello", :os_family => Operatingsystem.families[0]
     assert medium.valid?
diff --git a/test/unit/model_test.rb b/test/unit/model_test.rb
index 352e2f023..e1c4cb91f 100644
--- a/test/unit/model_test.rb
+++ b/test/unit/model_test.rb
@@ -26,65 +26,4 @@ class ModelTest < ActiveSupport::TestCase
     m.hosts << host
     assert !m.destroy
   end
-
-  def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_models"
-      role.permissions = ["#{operation}_models".to_sym]
-      @one.roles = [role]
-      @one.save!
-    end
-    User.current = @one
-  end
-
-  test "user with create permissions should be able to create" do
-    setup_user "create"
-    record =  Model.create :name => "dummy"
-    assert record.valid?
-    assert !record.new_record?
-  end
-
-  test "user with view permissions should not be able to create" do
-    setup_user "view"
-    record =  Model.create :name => "dummy"
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy"
-    record =  Model.first
-    as_admin do
-      record.hosts.destroy_all
-    end
-    assert record.destroy
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record =  Model.first
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit"
-    record      =  Model.first
-    record.name = "renamed"
-    assert record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record      =  Model.first
-    record.name = "renamed"
-    as_admin do
-      record.hosts.destroy_all
-    end
-    assert !record.save
-    assert record.valid?
-  end
-
 end
diff --git a/test/unit/operatingsystem_test.rb b/test/unit/operatingsystem_test.rb
index c55aec471..19d59c533 100644
--- a/test/unit/operatingsystem_test.rb
+++ b/test/unit/operatingsystem_test.rb
@@ -76,67 +76,6 @@ class OperatingsystemTest < ActiveSupport::TestCase
     assert operating_system.to_s == operating_system.to_label
   end
 
-  def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_operatingsystems"
-      role.permissions = ["#{operation}_operatingsystems".to_sym]
-      @one.roles = [role]
-      @one.save!
-    end
-    User.current = @one
-  end
-
-  test "user with create permissions should be able to create" do
-    setup_user "create"
-    record =  Operatingsystem.create :name => "dummy", :major => 7
-    assert record.valid?
-    assert !record.new_record?
-  end
-
-  test "user with view permissions should not be able to create" do
-    setup_user "view"
-    record =  Operatingsystem.create :name => "dummy", :major => 7
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy"
-    record =  Operatingsystem.first
-    as_admin do
-      record.hosts.delete_all
-      record.hostgroups.delete_all
-      assert record.destroy
-    end
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record =  Operatingsystem.first
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit"
-    record      =  Operatingsystem.first
-    record.name = "renamed"
-    assert record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record      =  Operatingsystem.first
-    record.name = "renamed"
-    as_admin do
-      record.hosts.destroy_all
-    end
-    assert !record.save
-    assert record.valid?
-  end
-
   test "should find by fullname string" do
     str = "Redhat 6.1"
     os = Operatingsystem.find_by_fullname(str)
@@ -166,7 +105,6 @@ class OperatingsystemTest < ActiveSupport::TestCase
     assert_equal ["centos 5.3"], medium.operatingsystem_names
   end
 
-
   describe "families" do
     let(:os) { Operatingsystem.new :name => "dummy", :major => 7 }
 
diff --git a/test/unit/permission_test.rb b/test/unit/permission_test.rb
new file mode 100644
index 000000000..34379bdcb
--- /dev/null
+++ b/test/unit/permission_test.rb
@@ -0,0 +1,18 @@
+require 'test_helper'
+
+class PermissionTest < ActiveSupport::TestCase
+  test ".resources" do
+    Permission.resources.each {|r| assert_kind_of String, r }
+  end
+
+  test ".resources works even for undefined resource types" do
+    FactoryGirl.create :permission, :resource_type => 'SomethingNotExisting'
+    Permission.resources.each {|r| assert_not_nil r }
+  end
+
+  test ".resources_with_translations are ordered by translation" do
+    Permission.stubs(:with_translations).returns([['Z', 'z'], ['A', 'b'], ['H', 'a']])
+    assert_equal [['A', 'b'], ['H', 'a'], ['Z', 'z']], Permission.resources_with_translations
+  end
+
+end
diff --git a/test/unit/ptable_test.rb b/test/unit/ptable_test.rb
index dad934570..06e245584 100644
--- a/test/unit/ptable_test.rb
+++ b/test/unit/ptable_test.rb
@@ -81,67 +81,6 @@ class PtableTest < ActiveSupport::TestCase
     assert !partition_table.destroy
   end
 
-  def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_ptables"
-      role.permissions = ["#{operation}_ptables".to_sym]
-      @one.roles = [role]
-      @one.save!
-    end
-    User.current = @one
-  end
-
-  test "user with create permissions should be able to create" do
-    setup_user "create"
-    record =  Ptable.create :name => "dummy", :layout => "layout"
-    assert record.valid?
-    assert !record.new_record?
-  end
-
-  test "user with view permissions should not be able to create" do
-    setup_user "view"
-    record =  Ptable.create :name => "dummy", :layout => "layout"
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy"
-    record =  Ptable.first
-    as_admin do
-      record.hosts.delete_all
-      record.hostgroups.delete_all
-      assert record.destroy
-    end
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record =  Ptable.first
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit"
-    record      =  Ptable.first
-    record.name = "renamed"
-    assert record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record      =  Ptable.first
-    record.name = "renamed"
-    as_admin do
-      record.hosts.destroy_all
-    end
-    assert !record.save
-    assert record.valid?
-  end
-
   test 'when creating a new ptable class object, an audit entry needs to be added' do
     as_admin do
       assert_difference('Audit.count') do
diff --git a/test/unit/puppetclass_test.rb b/test/unit/puppetclass_test.rb
index 184d2b4e2..2912bb8e6 100644
--- a/test/unit/puppetclass_test.rb
+++ b/test/unit/puppetclass_test.rb
@@ -27,84 +27,26 @@ class PuppetclassTest < ActiveSupport::TestCase
     assert !other_puppet_class.save
   end
 
-  def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_puppetclasses"
-      role.permissions = ["#{operation}_puppetclasses".to_sym]
-      @one.roles = [role]
-      @one.save!
-    end
-    User.current = @one
-  end
-
-  test "user with create permissions should be able to create" do
-    setup_user "create"
-    record =  Puppetclass.create :name => "dummy"
-    assert record.valid?
-    assert !record.new_record?
-  end
-
-  test "user with view permissions should not be able to create" do
-    setup_user "view"
-    record =  Puppetclass.create :name => "dummy"
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy"
-    record =  Puppetclass.first
-    as_admin do
-      record.hosts.destroy_all
-      record.lookup_keys.destroy_all
-    end
-    assert record.destroy
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record =  Puppetclass.first
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit"
-    record      =  Puppetclass.first
-    record.name = "renamed"
-    assert record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record      =  Puppetclass.first
-    record.name = "renamed"
-    as_admin do
-      record.hosts.destroy_all
-    end
-    assert !record.save
-    assert record.valid?
-  end
-
   test "looking for a nonexistent host returns no puppetclasses" do
     assert_equal [], Puppetclass.search_for("host = imaginaryhost.nodomain.what")
   end
 
-  test "user without create external_variables permission cannot create smart variable for puppetclass" do
-    setup_user "edit"
-    nested_lookup_key_params = {:new_1372154591368 => {:key=>"test_param", :key_type=>"string", :default_value => "7777", :path =>"fqdn\r\nhostgroup\r\nos\r\ndomain"}}
-    refute Puppetclass.first.update_attributes(:lookup_keys_attributes => nested_lookup_key_params)
-  end
-
   test "user with create external_variables permission can create smart variable for puppetclass" do
     @one = users(:one)
     # add permission for user :one
     as_admin do
+      filter1 = FactoryGirl.build(:filter)
+      filter1.permissions = Permission.find_all_by_name(['create_external_variables'])
+      filter2 = FactoryGirl.build(:filter)
+      filter2.permissions = Permission.find_all_by_name(['edit_puppetclasses'])
       role = Role.find_or_create_by_name :name => "testing_role"
-      role.permissions = [:edit_puppetclasses, :create_external_variables]
-      @one.roles = [role]
+      role.filters = [ filter1, filter2 ]
+      role.save!
+      filter1.role = role
+      filter1.save!
+      filter2.role = role
+      filter2.save!
+      @one.roles = [ role ]
       @one.save!
     end
     as_user :one do
diff --git a/test/unit/report_test.rb b/test/unit/report_test.rb
index 9b220c323..8ada44761 100644
--- a/test/unit/report_test.rb
+++ b/test/unit/report_test.rb
@@ -64,46 +64,4 @@ class ReportTest < ActiveSupport::TestCase
     assert Report.with("pending").include?(@r)
   end
 
-  def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_reports"
-      role.permissions = ["#{operation}_reports".to_sym]
-      @one.roles = [role]
-      @one.save!
-      @r.save!
-    end
-    User.current = @one
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy"
-    record = @r
-    assert record.destroy
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record =  @r
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should not be able to edit" do
-    # Reports are not an editable resource
-    setup_user "edit"
-    record        =  @r
-    record.status = {}
-    assert !record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record        =  @r
-    record.status = {}
-    assert !record.save
-    assert record.valid?
-  end
-
 end
diff --git a/test/unit/role_test.rb b/test/unit/role_test.rb
index ad95488e1..ddf5713e4 100644
--- a/test/unit/role_test.rb
+++ b/test/unit/role_test.rb
@@ -37,15 +37,6 @@ class RoleTest < ActiveSupport::TestCase
     role.wont_be :valid?
   end
 
-  it "should ensure length of name is at most 30" do
-    thirty = 'abcdefghijklmnopqrstuvwxyz1234'
-    thirtyone = 'abcdefghijklmnopqrstuvwxyz12345'
-    role = Role.new(:name => thirty)
-    role.must_be :valid?
-    role = Role.new(:name => thirtyone)
-    role.wont_be :valid?
-  end
-
   it "should allow value 'a role name' for name" do
     role = Role.new(:name => "a role name")
     role.must_be :valid?
@@ -66,25 +57,6 @@ class RoleTest < ActiveSupport::TestCase
     role.must_be :valid?
   end
 
-  def test_add_permission
-    role = Role.find(1)
-    size = role.permissions.size
-    role.add_permission!("apermission", "anotherpermission")
-    role.reload
-    assert role.permissions.include?(:anotherpermission)
-    assert_equal size + 2, role.permissions.size
-  end
-
-  def test_remove_permission
-    role = Role.find(1)
-    size = role.permissions.size
-    perm = role.permissions[0..1]
-    role.remove_permission!(*perm)
-    role.reload
-    assert ! role.permissions.include?(perm[0])
-    assert_equal size - 2, role.permissions.size
-  end
-
   context "System roles" do
     should "return the anonymous role" do
       role = Role.anonymous
@@ -97,6 +69,7 @@ class RoleTest < ActiveSupport::TestCase
         role_ids = Role.where("builtin = #{Role::BUILTIN_ANONYMOUS}").pluck(:id)
         user_ids = UserRole.where(:role_id => role_ids)
         UserRole.where(:role_id => role_ids).destroy_all
+        Filter.where(:role_id => role_ids).destroy_all
         Role.where(:id => role_ids).delete_all
       end
 
@@ -126,6 +99,7 @@ class RoleTest < ActiveSupport::TestCase
         role_ids = Role.where("builtin = #{Role::BUILTIN_DEFAULT_USER}").pluck(:id)
         user_ids = UserRole.where(:role_id => role_ids)
         UserRole.where(:role_id => role_ids).destroy_all
+        Filter.where(:role_id => role_ids).destroy_all
         Role.where(:id => role_ids).delete_all
       end
 
@@ -156,6 +130,64 @@ class RoleTest < ActiveSupport::TestCase
       it { subject.must_include(first) }
       it { subject.wont_include(second) }
     end
+  end
+
+  describe "#add_permissions" do
+    setup do
+      @permission1 = FactoryGirl.create(:permission, :name => 'permission1')
+      @permission2 = FactoryGirl.create(:permission, :architecture, :name => 'permission2')
+      @role = FactoryGirl.build(:role, :permissions => [])
+    end
+
+    it "should build filters with assigned permission" do
+      @role.add_permissions [@permission1.name, @permission2.name.to_sym]
+      assert @role.filters.all?(&:unlimited?)
+
+      permissions = @role.filters.map { |f| f.filterings.map(&:permission) }.flatten
+      assert_equal 2, @role.filters.size
+      assert_includes permissions, Permission.find_by_name(@permission1.name)
+      assert_includes permissions, Permission.find_by_name(@permission2.name)
+      # not saved yet
+      assert_empty @role.permissions
+    end
 
+    it "should raise error when given permission does not exist" do
+      assert_raises ArgumentError do
+        @role.add_permissions ['does_not_exist']
+      end
+    end
+
+    it "accespts one permissions instead of array as well" do
+      @role.add_permissions @permission1.name
+      permissions = @role.filters.map { |f| f.filterings.map(&:permission) }.flatten
+
+      assert_equal 1, @role.filters.size
+      assert_includes permissions, Permission.find_by_name(@permission1.name)
+    end
+
+    it "sets search filter to all filters" do
+      search = "id = 1"
+      @role.add_permissions [@permission1.name, @permission2.name.to_sym], :search => search
+      refute @role.filters.any?(&:unlimited?)
+      assert @role.filters.all? { |f| f.search == search }
+    end
+  end
+
+  describe "#add_permissions!" do
+    setup do
+      @permission1 = FactoryGirl.create(:permission, :name => 'permission1')
+      @permission2 = FactoryGirl.create(:permission, :architecture, :name => 'permission2')
+      @role = FactoryGirl.build(:role, :permissions => [])
+    end
+
+    it "persists built permissions" do
+      assert @role.add_permissions!([@permission1.name, @permission2.name.to_sym])
+      @role.reload
+
+      permissions = @role.permissions
+      assert_equal 2, @role.filters.size
+      assert_includes permissions, Permission.find_by_name(@permission1.name)
+      assert_includes permissions, Permission.find_by_name(@permission2.name)
+    end
   end
 end
diff --git a/test/unit/subnet_test.rb b/test/unit/subnet_test.rb
index 38e811bce..1b98a175d 100644
--- a/test/unit/subnet_test.rb
+++ b/test/unit/subnet_test.rb
@@ -86,70 +86,6 @@ class SubnetTest < ActiveSupport::TestCase
     @subnet.save!
   end
 
-  def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_subnets"
-      role.permissions = ["#{operation}_subnets".to_sym]
-      @one.roles = [role]
-      @one.save!
-    end
-    User.current = @one
-  end
-
-  test "user with create permissions should be able to create" do
-    setup_user "create"
-    record = Subnet.create :name => "dummy2", :network => "1.2.3.4", :mask => "255.255.255.0"
-    assert record.domain_ids = [Domain.first.id]
-    assert record.valid?
-    assert !record.new_record?
-  end
-
-  test "user with view permissions should not be able to create" do
-    setup_user "view"
-    record =  Subnet.new :name => "dummy", :network => "1.2.3.4", :mask => "255.255.255.0"
-    assert record.valid?
-    assert !record.save
-    assert record.new_record?
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy"
-    record = subnets(:two)
-    as_admin do
-      record.domains.destroy_all
-      record.hosts.clear
-      record.interfaces.clear
-    end
-    assert record.destroy
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record =  Subnet.first
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit"
-    record      =  Subnet.first
-    record.name = "renamed"
-    assert record.save
-  end
-
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record      =  Subnet.first
-    record.name = "renamed"
-    as_admin do
-      record.domains = [domains(:unuseddomain)]
-    end
-    assert !record.save
-    assert record.valid?
-  end
-
   test "from cant be bigger than to range" do
     s      = subnets(:one)
     s.to   = "2.3.4.15"
diff --git a/test/unit/user_role_test.rb b/test/unit/user_role_test.rb
new file mode 100644
index 000000000..a85cb1005
--- /dev/null
+++ b/test/unit/user_role_test.rb
@@ -0,0 +1,85 @@
+require 'test_helper'
+
+class UserRoleTest < ActiveSupport::TestCase
+
+  def setup
+    User.current = User.find_by_login "admin"
+  end
+
+  test "type detection" do
+    user_role = FactoryGirl.create :user_user_role
+    assert user_role.user_role?
+    usergroup_role = FactoryGirl.create :user_group_user_role
+    assert usergroup_role.user_group_role?
+  end
+
+  test "cache user roles" do
+    user             = FactoryGirl.create :user
+    user_role        = FactoryGirl.create :user_user_role, :owner => user
+    cached_user_role = user.cached_user_roles.first
+
+    assert_equal user_role.role, cached_user_role.role
+    assert_equal user_role, cached_user_role.user_role
+  end
+
+  test "cache usergroup roles" do
+    user_role = setup_admins_scenario
+
+    users = @semiadmin_users + [@admin_user] + [@superadmin_user]
+    users.each do |user|
+      cached_user_role = user.cached_user_roles.first
+      assert_equal user_role.role, cached_user_role.role
+      assert_equal user_role, cached_user_role.user_role
+    end
+  end
+
+  test "update role of usergroup role" do
+    new_role = FactoryGirl.create :role
+    user_role = setup_admins_scenario
+    user_role.role = new_role
+    user_role.save
+
+    users = @semiadmin_users + [@admin_user] + [@superadmin_user]
+    users.each  do |user|
+      cached_user_role = user.cached_user_roles.first
+      assert_equal new_role, cached_user_role.role
+    end
+  end
+
+  test "update owner of usergroup role" do
+    user_role = setup_admins_scenario
+    user_role.owner = @admins
+    user_role.save
+
+    users = [@admin_user, @superadmin_user]
+    users.each do |user|
+      cached_user_role = user.cached_user_roles.first
+      assert_equal user_role.role, cached_user_role.role
+    end
+
+    users = @semiadmin_users
+    users.each do |user|
+      assert_empty user.cached_user_roles
+    end
+  end
+
+  def setup_admins_scenario
+    @semiadmins  = FactoryGirl.create :usergroup
+    @admins      = FactoryGirl.create :usergroup
+    @superadmins = FactoryGirl.create :usergroup
+
+    @semiadmins.usergroups = [@admins]
+    @admins.usergroups     = [@superadmins]
+
+    @semiadmin_users = [FactoryGirl.create(:user, :login => 'ur_semiadmin1'),
+                       FactoryGirl.create(:user, :login => 'ur_semiadmin2')]
+    @admin_user      = FactoryGirl.create(:user, :login => 'ur_admin1')
+    @superadmin_user = FactoryGirl.create(:user, :login => 'ur_superadmin1')
+
+    @semiadmins.users  += @semiadmin_users
+    @admins.users      = [@admin_user]
+    @superadmins.users = [@superadmin_user]
+
+    FactoryGirl.create :user_group_user_role, :owner => @semiadmins
+  end
+end
diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb
index 4c0b256a2..b6592c7fc 100644
--- a/test/unit/user_test.rb
+++ b/test/unit/user_test.rb
@@ -127,14 +127,7 @@ class UserTest < ActiveSupport::TestCase
   end
 
   def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_users"
-      role.permissions = ["#{operation}_users".to_sym]
-      @one.roles = [role]
-      @one.save!
-    end
-    User.current = @one
+    super operation, "users"
   end
 
   test "user with create permissions should be able to create" do
@@ -194,36 +187,6 @@ class UserTest < ActiveSupport::TestCase
     end
   end
 
-  test "user with view permissions should not be able to create" do
-    setup_user "view"
-    record =  User.new :login => "dummy", :mail => "j@j.com", :auth_source_id => AuthSourceInternal.first.id
-    record.password_hash = "asd"
-    assert !record.save
-    assert record.valid?
-    assert record.new_record?
-  end
-
-  test "user with destroy permissions should be able to destroy" do
-    setup_user "destroy"
-    record =  users(:one)
-    assert record.destroy
-    assert record.frozen?
-  end
-
-  test "user with edit permissions should not be able to destroy" do
-    setup_user "edit"
-    record =  User.first
-    assert !record.destroy
-    assert !record.frozen?
-  end
-
-  test "user with edit permissions should be able to edit" do
-    setup_user "edit"
-    record      = users(:one)
-    record.login = "renamed"
-    assert record.save
-  end
-
   test "user cannot assign role he has not assigned himself" do
     setup_user "edit"
     extra_role      = Role.find_or_create_by_name :name => "foobar"
@@ -286,14 +249,6 @@ class UserTest < ActiveSupport::TestCase
     assert record.save
   end
 
-  test "user with destroy permissions should not be able to edit" do
-    setup_user "destroy"
-    record       = users(:two)
-    record.login = 'renamed'
-    assert !record.save
-    assert record.valid?
-  end
-
   test "should not be able to rename the admin account" do
     u = User.find_by_login("admin")
     u.login = "root"
@@ -363,6 +318,27 @@ class UserTest < ActiveSupport::TestCase
     assert_equal user.role_ids_was, [foobar.id, barfoo.id]
   end
 
+  test "admin? detection for user admin flag" do
+    admin = FactoryGirl.build(:user, :admin => true)
+    assert admin.admin?, 'user admin flag was missed'
+  end
+
+  test "admin? detection for group admin flag" do
+    admin = FactoryGirl.build(:user)
+    g1 = FactoryGirl.build(:usergroup)
+    g2 = FactoryGirl.build(:usergroup, :admin => true)
+    admin.cached_usergroups = [g1, g2]
+    assert admin.admin?, 'group admin flag was missed'
+  end
+
+  test "admin? is false if no flag is enabled" do
+    admin = FactoryGirl.build(:user)
+    g1 = FactoryGirl.build(:usergroup)
+    g2 = FactoryGirl.build(:usergroup)
+    admin.cached_usergroups = [g1, g2]
+    refute admin.admin?
+  end
+
   test ".find_or_create_external_user" do
     count = User.count
     # existing user
@@ -443,4 +419,16 @@ class UserTest < ActiveSupport::TestCase
     assert_not User.current.editing_self?(options)
   end
 
+  test "#can? for admin" do
+    Authorizer.any_instance.stubs(:can?).returns(false)
+    u = FactoryGirl.build(:user, :admin => true)
+    assert u.can?(:view_hosts_or_whatever_you_ask)
+  end
+
+  test "#can? for not admin" do
+    Authorizer.any_instance.stubs(:can?).returns('authorizer was asked')
+    u = FactoryGirl.build(:user)
+    assert_equal 'authorizer was asked', u.can?(:view_hosts_or_whatever_you_ask)
+  end
+
 end
diff --git a/test/unit/usergroup_member_test.rb b/test/unit/usergroup_member_test.rb
new file mode 100644
index 000000000..466e04c22
--- /dev/null
+++ b/test/unit/usergroup_member_test.rb
@@ -0,0 +1,368 @@
+require 'test_helper'
+
+class UsergroupMemberTest < ActiveSupport::TestCase
+
+  def setup
+    User.current = User.find_by_login "admin"
+  end
+
+  test "remove membership of user in a group" do
+    setup_admins_scenario
+    assert_includes @superadmin_user.cached_user_roles.map(&:role), @semiadmin_role
+
+    @superadmins.users.clear
+
+    assert_not_include @superadmin_user.reload.cached_user_roles.map(&:role), @semiadmin_role
+    cached_usergroups = @superadmin_user.cached_usergroups
+    assert_not_include cached_usergroups, @superadmins
+    assert_not_include cached_usergroups, @admins
+    assert_not_include cached_usergroups, @semiadmins
+  end
+
+  test "searching for user roles" do
+    setup_admins_scenario
+
+    found = @superadmins.usergroup_members.first.send :find_all_user_roles
+    assert_includes found, @semiadmin_ur
+    assert_includes found, @admin_ur
+    assert_includes found, @superadmin_ur
+    assert_not_includes found, @role1
+    assert_not_includes found, @role2
+    assert_not_includes found, @role3
+  end
+
+  test "searching for user groups" do
+    setup_admins_scenario
+
+    found = @admins.usergroup_members.first.send :find_all_usergroups
+    assert_includes found, @semiadmins
+    assert_includes found, @admins
+    assert_not_include found, @superadmins
+  end
+
+  test "searching for affected users memberships" do
+    setup_admins_scenario
+
+    found = @semiadmins.usergroup_members.where("member_type = 'Usergroup'").first.send :find_all_affected_users
+    assert_includes found, @admin_user
+    assert_includes found, @superadmin_user
+    assert_not_includes found, @semiadmin_user
+
+    found = @superadmins.usergroup_members.where("member_type = 'User'").first.send :find_all_affected_users
+    assert_not_includes found, @admin_user
+    assert_not_includes found, @semiadmin_user
+    assert_includes found, @superadmin_user
+  end
+
+  test "remove root member in tree" do
+    setup_admins_scenario
+
+    assert_include @admin_user.cached_user_roles.map(&:role), @semiadmin_role
+    assert_include @superadmin_user.cached_user_roles.map(&:role), @semiadmin_role
+
+    @semiadmins.usergroups.clear
+    @semiadmin_user.reload; @admin_user.reload; @superadmin_user.reload
+
+    assert_includes @semiadmin_user.cached_user_roles.map(&:role), @semiadmin_role
+    assert_includes @admin_user.cached_user_roles.map(&:role), @admin_role
+    assert_includes @superadmin_user.cached_user_roles.map(&:role), @admin_role
+    assert_includes @superadmin_user.cached_user_roles.map(&:role), @superadmin_role
+
+    assert_not_include @admin_user.cached_user_roles.map(&:role), @semiadmin_role
+    assert_not_include @superadmin_user.cached_user_roles.map(&:role), @semiadmin_role
+
+    assert_includes @semiadmin_user.cached_usergroups, @semiadmins
+    assert_not_includes @admin_user.cached_usergroups, @semiadmins
+    assert_includes @admin_user.cached_usergroups, @admins
+    assert_not_includes @superadmin_user.cached_usergroups, @semiadmins
+    assert_includes @superadmin_user.cached_usergroups, @admins
+    assert_includes @superadmin_user.cached_usergroups, @superadmins
+  end
+
+  test "remove leaf member in tree" do
+    setup_admins_scenario
+
+    assert_include @semiadmin_user.cached_user_roles.map(&:role), @semiadmin_role
+    assert_include @admin_user.cached_user_roles.map(&:role), @semiadmin_role
+    assert_include @superadmin_user.cached_user_roles.map(&:role), @semiadmin_role
+
+    @admins.usergroups.clear
+    @semiadmin_user.reload; @admin_user.reload; @superadmin_user.reload
+
+    assert_includes @semiadmin_user.cached_user_roles.map(&:role), @semiadmin_role
+    assert_includes @admin_user.cached_user_roles.map(&:role), @admin_role
+    assert_includes @superadmin_user.cached_user_roles.map(&:role), @superadmin_role
+    assert_include @admin_user.cached_user_roles.map(&:role), @semiadmin_role
+    assert_not_include @superadmin_user.cached_user_roles.map(&:role), @admin_role
+    assert_not_include @superadmin_user.cached_user_roles.map(&:role), @semiadmin_role
+
+    assert_includes @semiadmin_user.cached_usergroups, @semiadmins
+    assert_includes @admin_user.cached_usergroups, @semiadmins
+    assert_includes @admin_user.cached_usergroups, @admins
+    assert_not_includes @superadmin_user.cached_usergroups, @semiadmins
+    assert_not_includes @superadmin_user.cached_usergroups, @admins
+    assert_includes @superadmin_user.cached_usergroups, @superadmins
+  end
+
+  test "add new memership to the root" do
+    setup_admins_scenario
+
+    basic      = FactoryGirl.create :usergroup, :name => 'um_basic'
+    basic_role = FactoryGirl.create :role, :name => 'um_basic_role'
+    basic.roles<< basic_role
+    basic.usergroups<< @semiadmins
+    @semiadmin_user.reload; @admin_user.reload; @superadmin_user.reload
+
+    assert_includes @semiadmin_user.cached_user_roles.map(&:role), basic_role
+    assert_includes @admin_user.cached_user_roles.map(&:role), basic_role
+    assert_includes @superadmin_user.cached_user_roles.map(&:role), basic_role
+
+    assert_includes @semiadmin_user.cached_usergroups, basic
+    assert_includes @admin_user.cached_usergroups, basic
+    assert_includes @superadmin_user.cached_usergroups, basic
+  end
+
+  test "add new memership to the middle of chain" do
+    setup_admins_scenario
+
+    basic      = FactoryGirl.create :usergroup, :name => 'um_basic'
+    basic_role = FactoryGirl.create :role, :name => 'um_basic_role'
+    basic.roles<< basic_role
+    basic.usergroups<< @admins
+    @semiadmin_user.reload; @admin_user.reload; @superadmin_user.reload
+
+    assert_not_include @semiadmin_user.cached_user_roles.map(&:role), basic_role
+    assert_includes @admin_user.cached_user_roles.map(&:role), basic_role
+    assert_includes @superadmin_user.cached_user_roles.map(&:role), basic_role
+
+    assert_not_include @semiadmin_user.cached_usergroups, basic
+    assert_includes @admin_user.cached_usergroups, basic
+    assert_includes @superadmin_user.cached_usergroups, basic
+  end
+
+  test "add new memership to the leaf" do
+    setup_admins_scenario
+
+    basic      = FactoryGirl.create :usergroup, :name => 'um_basic'
+    basic_role = FactoryGirl.create :role, :name => 'um_basic_role'
+    basic.roles<< basic_role
+    basic.usergroups<< @superadmins
+    @semiadmin_user.reload; @admin_user.reload; @superadmin_user.reload
+
+    assert_not_includes @semiadmin_user.cached_user_roles.map(&:role), basic_role
+    assert_not_includes @admin_user.cached_user_roles.map(&:role), basic_role
+    assert_includes @superadmin_user.cached_user_roles.map(&:role), basic_role
+
+    assert_not_include @semiadmin_user.cached_usergroups, basic
+    assert_not_include @admin_user.cached_usergroups, basic
+    assert_includes @superadmin_user.cached_usergroups, basic
+  end
+
+  test "change membership (member) in the middle of chain" do
+    setup_admins_scenario
+
+    membership = @semiadmins.usergroup_members.where(:member_id => @admins.id).first
+    membership.member = @superadmins
+    membership.save
+    @semiadmin_user.reload; @admin_user.reload; @superadmin_user.reload
+
+    assert_includes @superadmin_user.cached_user_roles.map(&:role), @semiadmin_role
+    assert_includes @superadmin_user.cached_user_roles.map(&:role), @superadmin_role
+    assert_includes @superadmin_user.cached_user_roles.map(&:role), @admin_role
+    assert_not_includes @admin_user.cached_user_roles.map(&:role), @semiadmin_role
+    assert_includes @semiadmin_user.cached_user_roles.map(&:role), @semiadmin_role
+
+    assert_includes @superadmin_user.cached_usergroups, @semiadmins
+    assert_includes @superadmin_user.cached_usergroups, @admins
+    assert_includes @superadmin_user.cached_usergroups, @superadmins
+    assert_not_include @admin_user.cached_usergroups, @semiadmins
+    assert_include @admin_user.cached_usergroups, @admins
+    assert_include @semiadmin_user.cached_usergroups, @semiadmins
+  end
+
+  test "change membership (hostgroup) in the middle of chain" do
+    setup_admins_scenario
+
+    membership = @admins.usergroup_members.where(:member_id => @superadmins.id).first
+    membership.usergroup = @semiadmins
+    membership.save
+    @semiadmin_user.reload; @admin_user.reload; @superadmin_user.reload
+
+    assert_includes @superadmin_user.cached_user_roles.map(&:role), @semiadmin_role
+    assert_includes @superadmin_user.cached_user_roles.map(&:role), @superadmin_role
+    assert_not_includes @superadmin_user.cached_user_roles.map(&:role), @admin_role
+    assert_includes @admin_user.cached_user_roles.map(&:role), @semiadmin_role
+    assert_includes @admin_user.cached_user_roles.map(&:role), @admin_role
+    assert_includes @semiadmin_user.cached_user_roles.map(&:role), @semiadmin_role
+
+    assert_includes @superadmin_user.cached_usergroups, @superadmins
+    assert_includes @superadmin_user.cached_usergroups, @semiadmins
+    assert_not_include @superadmin_user.cached_usergroups, @admins
+    assert_includes @admin_user.cached_usergroups, @semiadmins
+    assert_includes @admin_user.cached_usergroups, @admins
+    assert_includes @semiadmin_user.cached_usergroups, @semiadmins
+  end
+
+  test "user is in two joined groups, second membership is removed" do
+    setup_redundant_scenario
+
+    @admins.users = []
+    assert_not_include @semiadmin_user.cached_user_roles.map(&:role), @admin_role
+  end
+
+  test "user is in three two joined groups, middle membership is removed" do
+    setup_redundant_scenario
+    @superadmins = FactoryGirl.create :usergroup, :name => 'um_superadmins'
+    @superadmins.usergroups = [@semiadmins]
+    @superadmins.roles<< @admin_role
+
+    @admins.users = []
+    @semiadmin_user.reload
+    assert_includes @semiadmin_user.cached_user_roles.map(&:role), @admin_role
+
+    assert_includes @semiadmin_user.cached_usergroups, @superadmins
+  end
+
+  test "diamond-with-tail usergroups, one of way is removed" do
+    @a = FactoryGirl.create :usergroup, :name => 'um_a'
+    @b = FactoryGirl.create :usergroup, :name => 'um_b'
+    @c = FactoryGirl.create :usergroup, :name => 'um_c'
+    @d = FactoryGirl.create :usergroup, :name => 'um_d'
+    @e = FactoryGirl.create :usergroup, :name => 'um_e'
+    @a.usergroups = [@b, @c]
+    @b.usergroups = [@d]
+    @c.usergroups = [@d]
+    @d.usergroups = [@e]
+
+    @user1 = FactoryGirl.create(:user, :login => 'um_user1')
+    @user2 = FactoryGirl.create(:user, :login => 'um_user2')
+    @user1.usergroups = [@d.reload] # @d cached #users already as []
+    @user2.usergroups = [@e]
+    @role = FactoryGirl.create(:role, :name => 'um_role')
+    @role_ur  = FactoryGirl.create :user_group_user_role, :owner => @a, :role => @role
+
+    assert_includes @user1.reload.cached_user_roles.map(&:role), @role
+    assert_includes @user2.reload.cached_user_roles.map(&:role), @role
+
+    assert_includes @user1.cached_usergroups, @a
+    assert_includes @user1.cached_usergroups, @b
+    assert_includes @user1.cached_usergroups, @c
+    assert_includes @user1.cached_usergroups, @d
+    assert_not_includes @user1.cached_usergroups, @e
+    assert_includes @user2.cached_usergroups, @a
+    assert_includes @user2.cached_usergroups, @b
+    assert_includes @user2.cached_usergroups, @c
+    assert_includes @user2.cached_usergroups, @d
+    assert_includes @user2.cached_usergroups, @e
+
+    @c.usergroups = []
+    assert_includes @user1.reload.cached_user_roles.map(&:role), @role
+    assert_includes @user2.reload.cached_user_roles.map(&:role), @role
+
+    assert_includes @user1.cached_usergroups, @a
+    assert_includes @user1.cached_usergroups, @b
+    assert_not_includes @user1.cached_usergroups, @c
+    assert_includes @user1.cached_usergroups, @d
+    assert_not_includes @user1.cached_usergroups, @e
+    assert_includes @user2.cached_usergroups, @a
+    assert_includes @user2.cached_usergroups, @b
+    assert_not_includes @user2.cached_usergroups, @c
+    assert_includes @user2.cached_usergroups, @d
+    assert_includes @user2.cached_usergroups, @e
+  end
+
+  test "user is in two joined groups, first membership is removed" do
+    setup_redundant_scenario
+
+    @semiadmins.users = []
+    @semiadmin_user.reload
+    assert_includes @semiadmin_user.cached_user_roles.map(&:role), @admin_role
+    assert_includes @semiadmin_user.cached_usergroups, @semiadmins
+    assert_includes @semiadmin_user.cached_usergroups, @admins
+  end
+
+  test "user is in two joined groups, joining is removed" do
+    setup_redundant_scenario
+
+    @semiadmins.usergroups = []
+    @semiadmin_user.reload
+
+    assert_includes @semiadmin_user.cached_user_roles.map(&:role), @admin_role
+    assert_includes @semiadmin_user.cached_usergroups, @semiadmins
+    assert_includes @semiadmin_user.cached_usergroups, @admins
+  end
+
+  test "user is in two joined groups with redundant role, second membership is removed" do
+    setup_redundant_scenario
+    @semiadmin_ur = FactoryGirl.create :user_group_user_role, :owner => @semiadmins, :role => @admin_role
+
+    @admins.users = []
+    @semiadmin_user.reload
+    assert_includes @semiadmin_user.cached_user_roles.map(&:role), @admin_role
+    assert_includes @semiadmin_user.cached_usergroups, @semiadmins
+  end
+
+  test "user is in two joined groups with redundant role, first membership is removed" do
+    setup_redundant_scenario
+    @semiadmin_ur = FactoryGirl.create :user_group_user_role, :owner => @semiadmins, :role => @admin_role
+
+    @semiadmins.users = []
+    assert_includes @semiadmin_user.cached_user_roles.map(&:role), @admin_role
+  end
+
+  test "user is in two joined groups with redundant role, joining is removed" do
+    setup_redundant_scenario
+    @semiadmin_ur = FactoryGirl.create :user_group_user_role, :owner => @semiadmins, :role => @admin_role
+
+    @semiadmins.usergroups = []
+    assert_includes @semiadmin_user.cached_user_roles.map(&:role), @admin_role
+  end
+
+  def setup_redundant_scenario
+    @semiadmins = FactoryGirl.create :usergroup, :name => 'um_semiadmins'
+    @admins     = FactoryGirl.create :usergroup, :name => 'um_admins'
+
+    @semiadmins.usergroups = [@admins]
+
+    @semiadmin_user = FactoryGirl.create(:user, :login => 'um_semiadmin1')
+
+    @semiadmins.users = [@semiadmin_user]
+    @admins.users     = [@semiadmin_user]
+
+    @admin_role = FactoryGirl.create(:role, :name => 'um_admin')
+
+    @admin_ur = FactoryGirl.create :user_group_user_role, :owner => @admins, :role => @admin_role
+  end
+
+  def setup_admins_scenario
+    @semiadmins  = FactoryGirl.create :usergroup, :name => 'um_semiadmins'
+    @admins      = FactoryGirl.create :usergroup, :name => 'um_admins'
+    @superadmins = FactoryGirl.create :usergroup, :name => 'um_superadmins'
+
+    @semiadmins.usergroups = [@admins]
+    @admins.usergroups     = [@superadmins]
+
+    @semiadmin_user  = FactoryGirl.create(:user, :login => 'um_semiadmin1')
+    @admin_user      = FactoryGirl.create(:user, :login => 'um_admin1')
+    @superadmin_user = FactoryGirl.create(:user, :login => 'um_superadmin1')
+
+    @semiadmins.users  = [@semiadmin_user]
+    @admins.users      = [@admin_user]
+    @superadmins.users = [@superadmin_user]
+
+    @semiadmin_role  = FactoryGirl.create(:role, :name => 'um_semiadmin')
+    @admin_role      = FactoryGirl.create(:role, :name => 'um_admin')
+    @superadmin_role = FactoryGirl.create(:role, :name => 'um_superadmin')
+
+    @semiadmin_ur  = FactoryGirl.create :user_group_user_role, :owner => @semiadmins, :role => @semiadmin_role
+    @admin_ur      = FactoryGirl.create :user_group_user_role, :owner => @admins, :role => @admin_role
+    @superadmin_ur = FactoryGirl.create :user_group_user_role, :owner => @superadmins, :role => @superadmin_role
+
+    @role1 = FactoryGirl.create(:role)
+    @role2 = FactoryGirl.create(:role)
+    @role3 = FactoryGirl.create(:role)
+    @semiadmin_user.roles<< @role1
+    @admin_user.roles<< @role2
+    @superadmin_user.roles<< @role3
+  end
+end
diff --git a/test/unit/usergroup_test.rb b/test/unit/usergroup_test.rb
index c3f561306..7031c3bb2 100644
--- a/test/unit/usergroup_test.rb
+++ b/test/unit/usergroup_test.rb
@@ -89,12 +89,13 @@ class UsergroupTest < ActiveSupport::TestCase
     assert_equal @ug1.errors.full_messages[0], "ug1 is used by #{@h1}"
   end
 
-  test "cannot be destroyed when in use by another usergroup" do
+  test "can be destroyed when in use by another usergroup, it removes association automatically" do
     @ug1 = Usergroup.find_or_create_by_name :name => "ug1"
     @ug2 = Usergroup.find_or_create_by_name :name => "ug2"
     @ug1.usergroups = [@ug2]
-    @ug1.destroy
-    assert @ug1.errors.full_messages[0] == "ug1 is used by ug2"
+    assert @ug1.destroy
+    assert @ug2.reload
+    assert_empty UsergroupMember.where(:member_id => @ug2.id)
   end
 
   test "removes user join model records" do
@@ -106,58 +107,29 @@ class UsergroupTest < ActiveSupport::TestCase
     end
   end
 
-  def setup_user operation
-    @one = users(:one)
-    as_admin do
-      role = Role.find_or_create_by_name :name => "#{operation}_usergroups"
-      role.permissions = ["#{operation}_usergroups".to_sym]
-      @one.roles = [role]
-      @one.save!
-    end
-    User.current = @one
-  end
+  test "removes all cached_user_roles when roles are disassociated" do
+    user         = FactoryGirl.create(:user)
+    record       = FactoryGirl.create(:usergroup)
+    record.users = [user]
+    one          = FactoryGirl.create(:role)
+    two          = FactoryGirl.create(:role)
 
-  test "user with create permissions should be able to create" do
-    setup_user "create"
-    record =  Usergroup.create :name => "dummy"
-    assert record.valid?
-    assert !record.new_record?
-  end
+    record.roles = [one, two]
+    assert_equal 2, user.reload.cached_user_roles.size
 
-  test "user with view permissions should not be able to create" do
-    setup_user "view"
-    record =  Usergroup.create :name => "dummy"
-    assert record.valid?
-    assert record.new_record?
-  end
+    assert record.update_attributes(:role_ids => [ two.id ])
+    assert_equal 1, user.reload.cached_user_roles.size
 
-  test "user with destroy permissions should be able to destroy" do
-    record = FactoryGirl.create(:usergroup)
-    setup_user "destroy"
-    assert record.destroy
-    assert record.frozen?
-  end
+    record.role_ids = [ ]
+    assert_equal 0, user.reload.cached_user_roles.size
 
-  test "user with edit permissions should not be able to destroy" do
-    record = FactoryGirl.create(:usergroup)
-    setup_user "edit"
-    assert !record.destroy
-    assert !record.frozen?
-  end
+    assert record.update_attribute(:role_ids, [ one.id ])
+    assert_equal 1, user.reload.cached_user_roles.size
 
-  test "user with edit permissions should be able to edit" do
-    record = FactoryGirl.create(:usergroup)
-    setup_user "edit"
-    record.name = "renamed"
-    assert record.save
+    record.roles<< two
+    assert_equal 2, user.reload.cached_user_roles.size
   end
 
-  test "user with destroy permissions should not be able to edit" do
-    record = FactoryGirl.create(:usergroup)
-    setup_user "destroy"
-    record.name = "renamed"
-    assert !record.save
-    assert record.valid?
-  end
+  # TODO test who can modify usergroup roles and who can assign users!!! possible privileges escalation
 
 end
