diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index e43e0d6..0a4bb6d 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -3,6 +3,7 @@
 
 class ApplicationController < ActionController::Base
   protect_from_forgery # See ActionController::RequestForgeryProtection for details
+  rescue_from ActionController::RoutingError, :with => :no_puppetclass_documentation_handler
 
   # standard layout to all controllers
   layout 'standard'
@@ -11,6 +12,18 @@ class ApplicationController < ActionController::Base
   before_filter :require_ssl, :require_login
   before_filter :load_tabs, :manage_tabs, :unless => :request_json?
   before_filter :welcome, :detect_notices, :only => :index, :unless => :request_json?
+  before_filter :authorize, :except => :login
+
+  # Authorize the user for the requested action
+  def authorize(ctrl = params[:controller], action = params[:action])
+    return true if request.xhr?
+    allowed = User.current.allowed_to?({:controller => ctrl, :action => action})
+    allowed ? true : deny_access
+  end
+
+  def deny_access
+    User.current.logged? ? render_403 : require_login
+  end
 
   protected
 
@@ -25,32 +38,32 @@ class ApplicationController < ActionController::Base
 
 
   # Force a user to login if authentication is enabled
-  # Sets @user to the logged in user, or to admin if logins are not used
+  # Sets User.current to the logged in user, or to admin if logins are not used
   def require_login
-    unless session[:user] and @user = User.find(session[:user])
+    unless session[:user] and User.current = User.find(session[:user])
       # User is not found or first login
       if SETTINGS[:login] and SETTINGS[:login] == true
         # authentication is enabled
         if request_json?
           # JSON requests (REST API calls) use basic http authenitcation and should not use/store cookies
-          @user = authenticate_or_request_with_http_basic { |u, p| User.try_to_login(u, p) }
-          return !@user.nil?
+          User.current = authenticate_or_request_with_http_basic { |u, p| User.try_to_login(u, p) }
+          return !User.current.nil?
         end
         session[:original_uri] = request.request_uri # keep the old request uri that we can redirect later on
         redirect_to login_users_path and return
       else
         # We assume we always have a user logged in, if authentication is disabled, the user is the build-in admin account.
-        unless @user = User.find_by_login("admin")
+        unless User.current = User.find_by_login("admin")
           flash[:foreman_error] = "Unable to find internal system admin account - Recreating . . ."
-          @user = User.create_admin
+          User.current = User.current = User.create_admin
         end
-        session[:user] = @user.id unless request_json?
+        session[:user] = User.current.id unless request_json?
       end
     end
   end
 
   def current_user
-    @user
+    User.current
   end
 
   def invalid_request
@@ -103,9 +116,9 @@ class ApplicationController < ActionController::Base
     session[:last_controller] = controller_name
     self.active_tab           = "" if @controller_changed
 
-    return if params[:tab_name].empty? or params[:action] != "index"
+    return true if params[:tab_name].empty? or params[:action] != "index"
 
-    if    params[:tab_name] == "Reset"
+    if params[:tab_name] == "Reset"
       self.active_tab    = ""
     elsif params[:remove_me] and @tabs.has_key? params[:tab_name]
       @tabs.delete params[:tab_name]
@@ -122,4 +135,33 @@ class ApplicationController < ActionController::Base
     end
   end
 
+  def require_admin
+    unless User.current.admin?
+      render_403
+      return false
+    end
+    true
+  end
+
+  def render_403
+    respond_to do |format|
+      format.html { render :template => "common/403", :layout => (request.xhr? ? false : 'standard'), :status => 403 }
+      format.atom { head 403 }
+      format.yaml { head 403 }
+      format.yml  { head 403 }
+      format.xml  { head 403 }
+      format.json { head 403 }
+    end
+    return false
+  end
+
+  # this has to be in the application controller, as the new request (for puppetdoc) url is not controller specific.
+  def no_puppetclass_documentation_handler(exception)
+    if exception.message =~ /No route matches "\/puppet\/rdoc\/([^\/]+)\/classes\/(.+?)\.html/
+      render :template => "puppetclasses/no_route", :locals => {:environment => $1, :name => $2.gsub("/","::")}, :layout => false
+    else
+      local_request? ? rescue_action_locally(exception) : rescue_action_in_public(exception)
+    end
+  end
+
 end
diff --git a/app/controllers/environments_controller.rb b/app/controllers/environments_controller.rb
index 6f9cef5..b9a105e 100644
--- a/app/controllers/environments_controller.rb
+++ b/app/controllers/environments_controller.rb
@@ -1,6 +1,5 @@
 class EnvironmentsController < ApplicationController
   include Foreman::Controller::Environments
-  rescue_from ActionController::RoutingError, :with => :no_puppetclass_documentation_handler
 
   before_filter :find_environment, :only => %w{show edit update destroy}
 
diff --git a/app/controllers/fact_values_controller.rb b/app/controllers/fact_values_controller.rb
index 1217c4c..bb1496c 100644
--- a/app/controllers/fact_values_controller.rb
+++ b/app/controllers/fact_values_controller.rb
@@ -1,6 +1,7 @@
 class FactValuesController < ApplicationController
-  skip_before_filter :require_ssl, :only => :create
-  skip_before_filter :require_login, :only => :create
+  skip_before_filter :require_ssl,               :only => :create
+  skip_before_filter :require_login,             :only => :create
+  skip_before_filter :authorize,                 :only => :create
   skip_before_filter :verify_authenticity_token, :only => :create
 
   # avoids storing the facts data in the log files
diff --git a/app/controllers/hosts_controller.rb b/app/controllers/hosts_controller.rb
index cd033d1..7d7b029 100644
--- a/app/controllers/hosts_controller.rb
+++ b/app/controllers/hosts_controller.rb
@@ -8,15 +8,20 @@ class HostsController < ApplicationController
     :multiple_enable, :multiple_disable, :submit_multiple_disable, :submit_multiple_enable]
   before_filter :find_host, :only => %w[show edit update destroy puppetrun setBuild cancelBuild report
     reports facts storeconfig_klasses clone externalNodes pxe_config]
+  before_filter :authorize
 
   filter_parameter_logging :root_pass
   helper :hosts, :reports
 
   def index
+
+    # restrict allowed hosts list based on the user permissions
+    @search = User.current.admin? ? Host.search(params[:search]) : Host.my_hosts.search(params[:search])
+
     respond_to do |format|
-      @search = Host.search(params[:search])
       format.html do
-        @hosts = @search.paginate :page => params[:page], :include => [:hostgroup, :domain, :operatingsystem, :environment, :model]
+        # You can see a host if you can CRUD it
+        @hosts = @search.paginate :page => params[:page], :include => [:hostgroup, :domain, :operatingsystem, :environment, :model, :fact_values]
         @via    = "fact_values_"
         @last_reports = Report.maximum(:id, :group => :host_id, :conditions => {:host_id => @hosts})
       end
@@ -343,7 +348,8 @@ class HostsController < ApplicationController
     hosts.delete_if {|host| host.destroy}
 
     session[:selected] = []
-    flash[:foreman_notice] = hosts.empty? ? "Destroyed selected hosts" : "The following hosts were not deleted: #{hosts.map(&:name).join('<br/>')}"
+    missed_hosts       = hosts.map(&:name).join('<br/>')
+    flash[:foreman_notice] = hosts.empty? ? "Destroyed selected hosts" : "The following hosts were not deleted: #{missed_hosts}"
     redirect_to(hosts_path)
   end
 
@@ -487,7 +493,8 @@ class HostsController < ApplicationController
     action = mode ? "enabled" : "disabled"
 
     session[:selected] = []
-    flash[:foreman_notice] = @hosts.empty? ? "#{action.capitalize} selected hosts" : "The following hosts were not #{action}: #{hosts.map(&:name).join('<br/>')}"
+    missed_hosts       = hosts.map(&:name).join('<br/>')
+    flash[:foreman_notice] = @hosts.empty? ? "#{action.capitalize} selected hosts" : "The following hosts were not #{action}: #{missed_hosts}"
     redirect_to(hosts_path) and return
   end
 
@@ -503,7 +510,7 @@ class HostsController < ApplicationController
     @search = list.search(params[:search])
     respond_to do |format|
       format.html do
-        @hosts = @search.paginate :page => params[:page], :include => [:hostgroup, :domain, :operatingsystem, :environment, :model]
+        @hosts = @search.paginate :page => params[:page], :include => [:hostgroup, :domain, :operatingsystem, :environment, :model, :fact_values]
         @via    = "fact_values_"
         @last_reports = Report.maximum(:id, :group => :host_id, :conditions => {:host_id => @hosts})
         @title = title
diff --git a/app/controllers/models_controller.rb b/app/controllers/models_controller.rb
index 71cb102..413e306 100644
--- a/app/controllers/models_controller.rb
+++ b/app/controllers/models_controller.rb
@@ -34,7 +34,7 @@ class ModelsController < ApplicationController
 
   def destroy
     @model = Model.find(params[:id])
-    if  @model.destroy
+    if @model.destroy
       flash[:foreman_notice] = "Successfully destroyed model."
     else
       flash[:foreman_error] = @model.errors.full_messages.join("<br/>")
diff --git a/app/controllers/notices_controller.rb b/app/controllers/notices_controller.rb
index 6344cbe..f07bd9d 100644
--- a/app/controllers/notices_controller.rb
+++ b/app/controllers/notices_controller.rb
@@ -1,12 +1,9 @@
 class NoticesController < ApplicationController
+  skip_before_filter :authorize, :only => :destroy
+
   def destroy
     @notice = Notice.find(params[:id])
-    if @notice.global
-      @notice.destroy
-    else
-      @notice.users.delete(current_user)
-      @notice.destroy unless @notice.users.any?
-    end
+    @notice.destroy_notice
     redirect_to :back
   end
 end
diff --git a/app/controllers/puppetclasses_controller.rb b/app/controllers/puppetclasses_controller.rb
index c272508..b26b845 100644
--- a/app/controllers/puppetclasses_controller.rb
+++ b/app/controllers/puppetclasses_controller.rb
@@ -1,6 +1,5 @@
 class PuppetclassesController < ApplicationController
   include Foreman::Controller::Environments
-  rescue_from ActionController::RoutingError, :with => :no_puppetclass_documentation_handler
 
   def index
     respond_to do |format|
diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb
index e740d95..951f25b 100644
--- a/app/controllers/reports_controller.rb
+++ b/app/controllers/reports_controller.rb
@@ -1,6 +1,7 @@
 class ReportsController < ApplicationController
-  skip_before_filter :require_login, :only => :create
-  skip_before_filter :require_ssl, :only => :create
+  skip_before_filter :require_login,             :only => :create
+  skip_before_filter :require_ssl,               :only => :create
+  skip_before_filter :authorize,                 :only => :create
   skip_before_filter :verify_authenticity_token, :only => :create
   helper :reports
 
diff --git a/app/controllers/roles_controller.rb b/app/controllers/roles_controller.rb
new file mode 100644
index 0000000..612a027
--- /dev/null
+++ b/app/controllers/roles_controller.rb
@@ -0,0 +1,80 @@
+# redMine - project management software
+# Copyright (C) 2006  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.
+
+class RolesController < ApplicationController
+  before_filter :require_admin
+
+  def index
+    @search = Role.search(params[:search])
+    @roles = @search.paginate(:page => params[:page], :order => "builtin ASC, name ASC")
+  end
+
+  def new
+    # Prefills the form with 'default user' role permissions
+    @role        = Role.new({:permissions => Role.default_user.permissions})
+    @permissions = @role.setable_permissions
+  end
+
+  def create
+    @role = Role.new(params[:role])
+    @permissions = @role.setable_permissions
+    if @role.save
+      flash[:foreman_notice] = "#{@role.name} succcessfully created"
+      redirect_to roles_url
+    else
+      render :action => 'new'
+    end
+  end
+
+  def edit
+    @role = Role.find(params[:id])
+    @permissions = @role.setable_permissions
+  end
+
+  def update
+    @role = Role.find(params[:id])
+    if @role.update_attributes(params[:role])
+      flash[:foreman_notice] = "#{@role.name} successfully updated"
+      redirect_to roles_url
+    else
+      render :action => 'edit'
+    end
+  end
+
+  def destroy
+    @role = Role.find(params[:id])
+    if @role.destroy
+      flash[:foreman_notice] = "Successfully destroyed role."
+    else
+      flash[:foreman_error] = @role.errors.full_messages.join("<br/>")
+    end
+    redirect_to roles_url
+  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
+      flash[:foreman_notice] = "All non public permissions successfuly updated"
+      redirect_to roles_url
+    end
+  end
+end
diff --git a/app/controllers/usergroups_controller.rb b/app/controllers/usergroups_controller.rb
index e3d788c..87377b5 100644
--- a/app/controllers/usergroups_controller.rb
+++ b/app/controllers/usergroups_controller.rb
@@ -1,6 +1,6 @@
 class UsergroupsController < ApplicationController
   def index
-    @usergroups = Usergroup.all
+    @usergroups = Usergroup.all(:order => "name")
   end
 
   def new
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 88485e8..0aee9cc 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -2,6 +2,7 @@ class UsersController < ApplicationController
 
   filter_parameter_logging :password, :password_confirmation
   skip_before_filter :require_login, :only => [:login, :logout]
+  skip_before_filter :authorize, :only => [:login, :logout]
 
   def index
     # set defaults search order - cant use default scope due to bug in AR
@@ -21,6 +22,7 @@ class UsersController < ApplicationController
     @user = User.new(params[:user])
     @user.admin = params[:admin]
     if @user.save
+      @user.roles = Role.name_is("Anonymous")
       flash[:foreman_notice] = "Successfully created user."
       redirect_to users_url
     else
@@ -30,13 +32,26 @@ class UsersController < ApplicationController
 
   def edit
     @user = User.find(params[:id])
+    if @user.user_facts.count == 0
+      user_fact = @user.user_facts.build :operator => "==", :andor => "or"
+      user_fact.fact_name_id = Puppet::Rails::FactName.first.id if Puppet::Rails::FactName.first
+      true
+    end
   end
 
   def update
     @user = User.find(params[:id])
     admin = params[:user].delete :admin
+    # Remove keys for resticted variables when the user is granted minimal access only to their own account
+    if @minimal_edit
+      for key in params[:user].keys
+        params[:user].delete key unless %w{password_confirmation password mail firstname lastname}.include? key
+      end
+    end
     if @user.update_attributes(params[:user])
-      @user.update_attribute :admin, admin
+      # Only an admin can update admin attribute of another use
+      # this is required, as the admin field is blacklisted above
+      @user.update_attribute(:admin, admin) if User.current.admin
       flash[:foreman_notice] = "Successfully updated user."
       redirect_to users_url
     else
@@ -46,7 +61,7 @@ class UsersController < ApplicationController
 
   def destroy
     user = User.find(params[:id])
-    if user == current_user
+    if user == User.current
       flash[:foreman_notice] = "You are currently logged in, suicidal?"
       redirect_to :back and return
     end
@@ -80,10 +95,11 @@ class UsersController < ApplicationController
   # Called from the logout link
   # Clears the rails session and redirects to the login action
   def logout
-    session[:user] = @user = nil
+    session[:user] = @user = User.current = nil
     if flash[:foreman_notice] or flash[:foreman_error]
       flash.keep
     else
+      session.clear
       flash[:foreman_notice] = "Logged out - See you soon"
     end
     redirect_to login_users_path
@@ -99,4 +115,18 @@ class UsersController < ApplicationController
     end
   end
 
+  private
+
+  def authorize(ctrl = params[:controller], action = params[:action])
+    # Minimal edit is true when the user is granted access to just their
+    # own account details
+    @minimal_edit = false
+    return true if User.current.allowed_to?({:controller => ctrl, :action => action})
+    if action =~ /edit|update/ and params[:id] == User.current.id.to_s
+      return @minimal_edit=true
+    else
+      deny_access and return
+    end
+  end
+
 end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 1c68dc6..81af172 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,6 +1,9 @@
-# Methods added to this helper will be available to all templates in the application.
+# Methods added to this helper will be available to all templates in the application
 module ApplicationHelper
 
+  def contract model
+    model.to_s
+  end
   def graph(type,opts = {})
     Gchart.send(type, {:size => '400x150', :bg => "E6DFCF", :format => "image_tag"}.merge(opts))
   end
@@ -10,13 +13,18 @@ module ApplicationHelper
   end
 
   def edit_habtm klass, association
-    render :partial => 'common/edit_habtm', :locals =>{ :klass => klass, :associations => association.all.delete_if{|e| e == klass}}
+    render :partial => 'common/edit_habtm', :locals =>{ :klass => klass, :associations => association.all.sort.delete_if{|e| e == klass}}
   end
 
   def link_to_remove_fields(name, f)
     f.hidden_field(:_destroy) + link_to_function(image_tag("false.png", :title => "remove"), "remove_fields(this)")
   end
 
+  # Creates a link to a javascript function that creates field entries for the association on the web page
+  # +name+       : String containing links's text
+  # +f+          : FormBuiler object
+  # +association : The field are created to allow entry into this association
+  # +partial+    : String containing an optional partial into which we render
   def link_to_add_fields(name, f, association, partial = nil)
     new_object = f.object.class.reflect_on_association(association).klass.new
     fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
@@ -52,6 +60,12 @@ module ApplicationHelper
       :position => {:after => {:success => "selected_classes" }}
   end
 
+  def check_all_links(form_name)
+    link_to_function("Check all", "checkAll('#{form_name}', true)") +
+    " | " +
+    link_to_function("Uncheck all", "checkAll('#{form_name}', false)")
+  end
+
   def searchtab title, search, options
     opts = {:action => params[:action], :tab_name => title, :search => search}
     selected_class = options[:selected] ? "selectedtab" : ""
@@ -81,4 +95,45 @@ module ApplicationHelper
     return param.to_i unless param.empty?
     return @fact_name_id if @fact_name_id
   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)
+    User.current.allowed_to?({:controller => controller, :action => action}) rescue false
+  end
+
+  # Display a link if user is authorized, otherwise a string
+  # +name+    : String to be displayed
+  # +options+ : Hash containing
+  #             :enable_link  : Boolean indicating whether the link is to be displayed
+  #             :controlller  : String or Symbol representing the controller
+  #             :auth_action  : String or Symbol representing the action to be used for authorization checks
+  # +html_options+ : Hash containing html options for the link or span
+  def link_to_if_authorized(name, options = {}, html_options = {})
+    enable_link = options.has_key?(:enable_link) ? options.delete(:enable_link) : true
+    auth_action = options.delete :auth_action
+    if enable_link
+      link_to_if authorized_for(options[:controller] || params[:controller], auth_action || options[:action]), name, options, html_options
+    else
+      content_tag(:span, name, {:class => "entry"}.merge(html_options))
+    end
+  end
+
+  # Display a link if user is authorized, otherwise nothing
+  # +name+    : String to be displayed
+  # +options+ : Hash containing
+  #             :controlller  : String or Symbol representing the controller
+  #             :auth_action  : String or Symbol representing the action to be used for authorization checks
+  # +html_options+ : Hash containing html options for the link or span
+  def display_link_if_authorized(name, options = {}, html_options = nil)
+    auth_action = options.delete :auth_action
+    link_to(name, options, html_options) if authorized_for(options[:controller] || params[:controller], auth_action || options[:action])
+  end
+
+  def authorized_edit_habtm klass, association
+    return edit_habtm(klass, association) if authorized_for params[:controller], params[:action]
+    show_habtm klass.send(association.name.pluralize.downcase)
+  end
+
 end
diff --git a/app/helpers/common_parameters_helper.rb b/app/helpers/common_parameters_helper.rb
index 8de8751..3eae3fd 100644
--- a/app/helpers/common_parameters_helper.rb
+++ b/app/helpers/common_parameters_helper.rb
@@ -1,2 +1,9 @@
 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?(eval("@#{controller.singularize}"))
+  end
 end
diff --git a/app/helpers/domains_helper.rb b/app/helpers/domains_helper.rb
index 04b2f61..80925d5 100644
--- a/app/helpers/domains_helper.rb
+++ b/app/helpers/domains_helper.rb
@@ -1,2 +1,3 @@
 module DomainsHelper
+  include CommonParametersHelper
 end
diff --git a/app/helpers/host_groups_helper.rb b/app/helpers/host_groups_helper.rb
index 7724b25..3250558 100644
--- a/app/helpers/host_groups_helper.rb
+++ b/app/helpers/host_groups_helper.rb
@@ -1,2 +1,3 @@
 module HostGroupsHelper
+  include CommonParametersHelper
 end
diff --git a/app/helpers/hostgroups_helper.rb b/app/helpers/hostgroups_helper.rb
index 337ce33..2132d78 100644
--- a/app/helpers/hostgroups_helper.rb
+++ b/app/helpers/hostgroups_helper.rb
@@ -1,2 +1,3 @@
 module HostgroupsHelper
+  include CommonParametersHelper
 end
diff --git a/app/helpers/hosts_helper.rb b/app/helpers/hosts_helper.rb
index f82f0f7..5396ccb 100644
--- a/app/helpers/hosts_helper.rb
+++ b/app/helpers/hosts_helper.rb
@@ -1,9 +1,11 @@
 module HostsHelper
+  include CommonParametersHelper
   def last_report_column(record)
     return nil if record.last_report.nil?
     time = time_ago_in_words(record.last_report.getlocal)
     image_tag("#{not (record.error_count > 0 or record.no_report)}.png", :size => "18x18") +
-      link_to_if(@last_reports[record.id], time, report_path(@last_reports[record.id].to_i))
+      link_to_if_authorized(time,  hash_for_report_path(:id => @last_reports[record.id], :enable_link => @last_reports[record.id])
+    )
   end
 
 # method that reformats the hostname column by adding the status icons
diff --git a/app/models/architecture.rb b/app/models/architecture.rb
index 1dd60ae..dccc436 100644
--- a/app/models/architecture.rb
+++ b/app/models/architecture.rb
@@ -1,4 +1,5 @@
 class Architecture < ActiveRecord::Base
+  include Authorization
   has_many :hosts
   has_and_belongs_to_many :operatingsystems
   validates_uniqueness_of :name
diff --git a/app/models/auth_source.rb b/app/models/auth_source.rb
index 4e17c7b..83bd4a7 100644
--- a/app/models/auth_source.rb
+++ b/app/models/auth_source.rb
@@ -16,6 +16,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 class AuthSource < ActiveRecord::Base
+  include Authorization
   has_many :users
 
   validates_presence_of :name
@@ -49,6 +50,7 @@ class AuthSource < ActiveRecord::Base
   end
 
   # Try to authenticate a user not yet registered against available sources
+  # Returns : user's attributes OR nil
   def self.authenticate(login, password)
     AuthSource.find(:all).each do |source|
       begin
diff --git a/app/models/auth_source_ldap.rb b/app/models/auth_source_ldap.rb
index 1347f4b..26c3b65 100644
--- a/app/models/auth_source_ldap.rb
+++ b/app/models/auth_source_ldap.rb
@@ -31,6 +31,9 @@ class AuthSourceLdap < AuthSource
     self.port = 389 if self.port == 0
   end
 
+  # Loads the LDAP info for a user and authenticates the user with their password
+  # Returns : Array of Strings.
+  #           Either the users's DN or the user's full details OR nil
   def authenticate(login, password)
     return nil if login.blank? || password.blank?
     attrs = []
diff --git a/app/models/authorization.rb b/app/models/authorization.rb
new file mode 100644
index 0000000..eb848d2
--- /dev/null
+++ b/app/models/authorization.rb
@@ -0,0 +1,41 @@
+module Authorization
+  def self.included(base)
+    base.class_eval do
+      before_save    :enforce_edit_permissions
+      before_destroy :enforce_destroy_permissions
+      before_create  :enforce_create_permissions
+    end
+  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?
+
+    klass   = self.class.name.downcase
+    klass.gsub!(/authsource.*/, "authenticator")
+    klass.gsub!(/commonparameter.*/, "global_variable")
+    klasses = klass.pluralize
+    return true if User.current.allowed_to?("#{operation}_#{klasses}".to_sym)
+
+    errors.add_to_base "You do not have permission to #{operation} this #{klass}"
+    false
+  end
+
+  private
+  def enforce?
+    not (defined?(Rake) or User.current.admin?)
+  end
+end
diff --git a/app/models/domain.rb b/app/models/domain.rb
index e462731..86da39a 100644
--- a/app/models/domain.rb
+++ b/app/models/domain.rb
@@ -1,8 +1,11 @@
 # This models a DNS domain and so represents a site.
 class Domain < ActiveRecord::Base
+  include Authorization
   has_many :hosts
   has_many :subnets
   has_many :domain_parameters, :dependent => :destroy, :foreign_key => :reference_id
+  has_and_belongs_to_many :users, :join_table => "user_domains"
+
   accepts_nested_attributes_for :domain_parameters, :reject_if => lambda { |a| a[:value].blank? }, :allow_destroy => true
   validates_uniqueness_of :name
   validates_uniqueness_of :fullname, :allow_blank => true, :allow_nil => true
@@ -24,5 +27,24 @@ class Domain < ActiveRecord::Base
     super({:only => [:name, :id]}.merge(options))
   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 contrained
+      if current.domains.empty? or current.domains.map(&:id).include? self.id
+        return true
+      end
+    end
+
+    errors.add_to_base "You do not have permission to #{operation} this domain"
+    false
+  end
+
 end
 
diff --git a/app/models/domain_parameter.rb b/app/models/domain_parameter.rb
index fcff915..5392b0a 100644
--- a/app/models/domain_parameter.rb
+++ b/app/models/domain_parameter.rb
@@ -1,4 +1,21 @@
 class DomainParameter < Parameter
   belongs_to :domain, :foreign_key => :reference_id
   validates_uniqueness_of :name, :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_to_base "You do not have permission to #{operation} this domain parameter"
+    false
+  end
 end
diff --git a/app/models/group_parameter.rb b/app/models/group_parameter.rb
index ec5c67f..2fce0c5 100644
--- a/app/models/group_parameter.rb
+++ b/app/models/group_parameter.rb
@@ -1,4 +1,21 @@
 class GroupParameter < Parameter
   belongs_to :hostgroup, :foreign_key => :reference_id
   validates_uniqueness_of :name, :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}_hostgroups".to_sym)
+      if current.hostgroups.empty? or current.hostgroups.include? hostgroup
+        return true
+      end
+    end
+
+    errors.add_to_base "You do not have permission to #{operation} this group parameter"
+    false
+  end
 end
diff --git a/app/models/host.rb b/app/models/host.rb
index 9c74f29..c538bf5 100644
--- a/app/models/host.rb
+++ b/app/models/host.rb
@@ -1,4 +1,5 @@
 class Host < Puppet::Rails::Host
+  include Authorization
   belongs_to :architecture
   belongs_to :media
   belongs_to :model
@@ -52,6 +53,34 @@ class Host < Puppet::Rails::Host
   named_scope :successful, {:conditions => "puppet_status = 0"}
   named_scope :alerts_disabled, {:conditions => ["enabled = ?", false] }
 
+  named_scope :my_hosts, lambda {
+    user                 = User.current
+    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.domains).map(&:id)])
+    hostgroup_conditions = sanitize_sql_for_conditions([" (hosts.hostgroup_id in (?))",(hgs = user.hostgroups).map(&:id)])
+
+    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(/^(.*).....$/)
+      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.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.sub!(/\s*\(\)\s*/, "")
+      conditions.sub!(/^(?:\(\))?\s?(?:and|or)\s*/, "")
+      conditions.sub!(/\(\s*(?:or|and)\s*\(/, "((")
+    end
+    {:conditions => conditions}
+  }
+
   # audit the changes to this model
   acts_as_audited :except => [:last_report, :puppet_status, :last_compile]
 
@@ -70,10 +99,10 @@ class Host < Puppet::Rails::Host
     validates_format_of      :sp_name, :with => /.*-sp/, :allow_nil => true, :allow_blank => true
     validates_presence_of    :architecture_id, :domain_id, :mac, :operatingsystem_id
     validates_length_of      :root_pass, :minimum => 8,:too_short => 'should be 8 characters or more'
-    validates_format_of      :mac,       :with => /([a-f0-9]{1,2}:){5}[a-f0-9]{1,2}/
-    validates_format_of      :ip,        :with => /(\d{1,3}\.){3}\d{1,3}/
+    validates_format_of      :mac,       :with => (/([a-f0-9]{1,2}:){5}[a-f0-9]{1,2}/)
+    validates_format_of      :ip,        :with => (/(\d{1,3}\.){3}\d{1,3}/)
     validates_presence_of    :ptable, :message => "cant be blank unless a custom partition has been defined",
-                             :if => Proc.new { |host| host.disk.empty? and not defined?(Rake) }
+      :if => Proc.new { |host| host.disk.empty? and not defined?(Rake) }
     validates_format_of      :sp_mac,    :with => /([a-f0-9]{1,2}:){5}[a-f0-9]{1,2}/, :allow_nil => true, :allow_blank => true
     validates_format_of      :sp_ip,     :with => /(\d{1,3}\.){3}\d{1,3}/, :allow_nil => true, :allow_blank => true
     validates_format_of      :serial,    :with => /[01],\d{3,}n\d/, :message => "should follow this format: 0,9600n8", :allow_blank => true, :allow_nil => true
@@ -103,8 +132,8 @@ class Host < Puppet::Rails::Host
   # supports a simple user, or a usergroup
   # selection parameter is expected to be an ActiveRecord id_and_type method (see Foreman's AR extentions).
   def is_owned_by=(selection)
-    oid = User.find(selection.to_i) if selection =~ /-Users$/
-    oid = Usergroup.find(selection.to_i) if selection =~ /-Usergroups$/
+    oid = User.find(selection.to_i) if selection =~ (/-Users$/)
+    oid = Usergroup.find(selection.to_i) if selection =~ (/-Usergroups$/)
     self.owner = oid
   end
 
@@ -193,11 +222,11 @@ class Host < Puppet::Rails::Host
 
   # returns the list of puppetclasses a host is in.
   def puppetclasses_names
-      return all_puppetclasses.collect {|c| c.name}
+    return all_puppetclasses.collect {|c| c.name}
   end
 
   def all_puppetclasses
-     return hostgroup.nil? ? puppetclasses : (hostgroup.puppetclasses + puppetclasses).uniq
+    return hostgroup.nil? ? puppetclasses : (hostgroup.puppetclasses + puppetclasses).uniq
   end
 
   # provide information about each node, mainly used for puppet external nodes
@@ -278,7 +307,7 @@ class Host < Puppet::Rails::Host
 
   def fv name
     v=fact_values.first(:select => "fact_values.value", :joins => :fact_name,
-                       :conditions => "fact_names.name = '#{name}'")
+                        :conditions => "fact_names.name = '#{name}'")
     v.value unless v.nil?
   end
 
@@ -404,9 +433,9 @@ class Host < Puppet::Rails::Host
   end
 
   def classes_from_storeconfigs
-   klasses = resources.find(:all, :conditions => {:restype => "Class"}, :select => :title, :order => :title)
-   klasses.map!(&:title).delete(:main)
-   return klasses
+    klasses = resources.find(:all, :conditions => {:restype => "Class"}, :select => :title, :order => :title)
+    klasses.map!(&:title).delete(:main)
+    return klasses
   end
 
   def can_be_build?
@@ -422,6 +451,28 @@ class Host < Puppet::Rails::Host
     return hash
   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.my_hosts(current).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 contrained 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_to_base "You do not have permission to #{operation} this host"
+    false
+  end
+
   private
   # align common mac and ip address input
   def normalize_addresses
@@ -459,5 +510,4 @@ class Host < Puppet::Rails::Host
     return if name.empty? or domain.nil?
     self.name += ".#{domain}" unless name =~ /.#{domain}$/
   end
-
 end
diff --git a/app/models/host_parameter.rb b/app/models/host_parameter.rb
index 932881d..7a6f883 100644
--- a/app/models/host_parameter.rb
+++ b/app/models/host_parameter.rb
@@ -3,7 +3,14 @@ class HostParameter < Parameter
   validates_uniqueness_of :name, :scope => :reference_id
 
   def to_s
-    "#{host_id ? host.name : "unassociated"}: #{name} = #{value}"
+    "#{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?
+
+  self.host.enforce_permissions operation
+  end
 end
diff --git a/app/models/hostgroup.rb b/app/models/hostgroup.rb
index faf61a2..4557158 100644
--- a/app/models/hostgroup.rb
+++ b/app/models/hostgroup.rb
@@ -1,5 +1,7 @@
 class Hostgroup < ActiveRecord::Base
+  include Authorization
   has_and_belongs_to_many :puppetclasses
+  has_and_belongs_to_many :users, :join_table => "user_hostgroups"
   validates_uniqueness_of :name
   validates_format_of :name, :with => /\A(\S+\s?)+\Z/, :message => "can't be blank or contain trailing white spaces."
   has_many :group_parameters, :dependent => :destroy, :foreign_key => :reference_id
@@ -10,18 +12,17 @@ class Hostgroup < ActiveRecord::Base
 
   acts_as_audited
 
-#TODO: add a method that returns the valid os for a hostgroup
+  #TODO: add a method that returns the valid os for a hostgroup
 
- def all_puppetclasses
-   puppetclasses
- end
-
- def hostgroup
-   self
- end
+  def all_puppetclasses
+    puppetclasses
+  end
 
   def as_json(options={})
     super({:only => [:name, :id]}.merge(options))
   end
 
+  def hostgroup
+    self
+  end
 end
diff --git a/app/models/media.rb b/app/models/media.rb
index 95e3155..ec27953 100644
--- a/app/models/media.rb
+++ b/app/models/media.rb
@@ -1,4 +1,5 @@
 class Media < ActiveRecord::Base
+  include Authorization
   has_and_belongs_to_many :operatingsystems
   has_many :hosts
   validates_uniqueness_of :name
diff --git a/app/models/model.rb b/app/models/model.rb
index 75d414b..34e541e 100644
--- a/app/models/model.rb
+++ b/app/models/model.rb
@@ -1,4 +1,5 @@
 class Model < ActiveRecord::Base
+  include Authorization
   has_many :hosts
   before_destroy Ensure_not_used_by.new(:hosts)
   validates_uniqueness_of :name
diff --git a/app/models/notice.rb b/app/models/notice.rb
index 177a60a..dad6696 100644
--- a/app/models/notice.rb
+++ b/app/models/notice.rb
@@ -12,6 +12,14 @@ class Notice < ActiveRecord::Base
     "#{global? ? "global" : "individual"} #{content}"
   end
 
+  def destroy_notice
+    if global
+      destroy
+    else
+      users.delete(User.current)
+      destroy unless users.any?
+    end
+  end
   private
 
   def add_to_all_users
diff --git a/app/models/operatingsystem.rb b/app/models/operatingsystem.rb
index 21bded0..eb82383 100644
--- a/app/models/operatingsystem.rb
+++ b/app/models/operatingsystem.rb
@@ -2,6 +2,7 @@ require 'ostruct'
 require 'uri'
 
 class Operatingsystem < ActiveRecord::Base
+  include Authorization
   has_many :hosts
   has_and_belongs_to_many :medias
   has_and_belongs_to_many :ptables
diff --git a/app/models/parameter.rb b/app/models/parameter.rb
index cd5383a..7a8cc54 100644
--- a/app/models/parameter.rb
+++ b/app/models/parameter.rb
@@ -1,4 +1,5 @@
 class Parameter < ActiveRecord::Base
+  include Authorization
   acts_as_audited
   validates_presence_of   :name, :value
   validates_format_of     :name, :value, :with => /^.*\S$/, :message => "can't be blank or contain trailing white space"
diff --git a/app/models/ptable.rb b/app/models/ptable.rb
index 89084c6..34da00e 100644
--- a/app/models/ptable.rb
+++ b/app/models/ptable.rb
@@ -3,6 +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
   has_many :hosts
   has_and_belongs_to_many :operatingsystems
   before_destroy Ensure_not_used_by.new(:hosts)
diff --git a/app/models/puppetclass.rb b/app/models/puppetclass.rb
index 07a2e9f..f00ceb9 100644
--- a/app/models/puppetclass.rb
+++ b/app/models/puppetclass.rb
@@ -1,4 +1,5 @@
 class Puppetclass < ActiveRecord::Base
+  include Authorization
   has_and_belongs_to_many :environments
   has_and_belongs_to_many :operatingsystems
   has_and_belongs_to_many :hosts
diff --git a/app/models/report.rb b/app/models/report.rb
index d59f9ad..ff54b79 100644
--- a/app/models/report.rb
+++ b/app/models/report.rb
@@ -1,4 +1,5 @@
 class Report < ActiveRecord::Base
+  include Authorization
   belongs_to :host
   has_many :messages, :through => :logs, :dependent => :destroy
   has_many :sources, :through => :logs, :dependent => :destroy
@@ -244,12 +245,11 @@ class Report < ActiveRecord::Base
     nil
   end
 
-
   # The metrics layout has changed in Puppet 2.6.x release,
   # this method attempts to align the bit value metrics and the new name scheme in 2.6.x
   # returns a hash of { :type => "metric type", :name => "metric_name"}
   def self.translate_metrics_to26 metric
-   case metric
+    case metric
     when "applied"
       { :type => "changes", :name => :total}
     else
@@ -257,4 +257,15 @@ class Report < ActiveRecord::Base
     end
   end
 
+  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_to_base "You do not have permission to #{operation} this report"
+    false
+  end
 end
diff --git a/app/models/role.rb b/app/models/role.rb
new file mode 100644
index 0000000..9c96329
--- /dev/null
+++ b/app/models/role.rb
@@ -0,0 +1,154 @@
+# redMine - project management software
+# Copyright (C) 2006  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.
+
+class Role < ActiveRecord::Base
+  # Built-in roles
+  BUILTIN_DEFAULT_USER  = 1
+  BUILTIN_ANONYMOUS     = 2
+
+  named_scope :givable, { :conditions => "builtin = 0", :order => 'name' }
+  named_scope :builtin, lambda { |*args|
+    compare = 'not' if args.first == true
+    { :conditions => "#{compare} builtin = 0" }
+  }
+
+  before_destroy :check_deletable
+
+  has_many :user_roles, :dependent => :destroy
+  has_many :users, :through => :user_roles
+
+  serialize :permissions, Array
+  attr_protected :builtin
+
+  validates_presence_of :name
+  validates_uniqueness_of :name
+  validates_length_of :name, :maximum => 30
+  validates_format_of :name, :with => /^\w[\w\s\'\-]*\w$/i
+  validates_inclusion_of :builtin, :in => 0..2
+
+  def initialize *args
+    super *args
+    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)
+  end
+
+  # Return true if the role is a builtin role
+  def builtin?
+    self.builtin != 0
+  end
+
+  # Return true if the role is a user role
+  def user?
+    !self.builtin?
+  end
+
+  # Return true if role is allowed to do the specified action
+  # action can be:
+  # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
+  # * a permission Symbol (eg. :edit_project)
+  def allowed_to?(action)
+    if action.is_a? Hash
+      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
+    find(:all, :conditions => {:builtin => 0}, :order => 'name')
+  end
+
+  # Return the builtin 'default user' role.  If the role doesn't exist,
+  # it will be created on the fly.
+  def self.default_user
+    default_user_role = find(:first, :conditions => {:builtin => BUILTIN_DEFAULT_USER})
+    if default_user_role.nil?
+      default_user_role = create(:name => 'Default user') do |role|
+        role.builtin = BUILTIN_DEFAULT_USER
+      end
+      raise 'Unable to create the default user role.' if default_user_role.new_record?
+    end
+    default_user_role
+  end
+
+  # Return the builtin 'anonymous' role.  If the role doesn't exist,
+  # it will be created on the fly.
+  def self.anonymous
+    anonymous_role = find(:first, :conditions => {:builtin => BUILTIN_ANONYMOUS})
+    if anonymous_role.nil?
+      anonymous_role = create(:name => 'Anonymous') do |role|
+        role.builtin = BUILTIN_ANONYMOUS
+      end
+      raise "Unable to create the anonymous role." if anonymous_role.new_record?
+    end
+    anonymous_role
+  end
+
+private
+  def allowed_permissions
+    @allowed_permissions ||= permissions + Foreman::AccessControl.public_permissions.collect {|p| p.name}
+  end
+
+  def allowed_actions
+    @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Foreman::AccessControl.allowed_actions(permission) }.flatten
+  end
+
+  def check_deletable
+    errors.add_to_base "Role is in use" if users.any?
+    errors.add_to_base "Can't delete builtin role" if builtin?
+    errors.empty?
+  end
+end
diff --git a/app/models/subnet.rb b/app/models/subnet.rb
index 181121b..6f122ab 100644
--- a/app/models/subnet.rb
+++ b/app/models/subnet.rb
@@ -1,6 +1,7 @@
 class Subnet < ActiveRecord::Base
+  include Authorization
   has_many :hosts, :through => :domain
-  has_many :sps, :through => :hosts
+  #has_many :sps, :through => :hosts
   belongs_to :domain
   validates_presence_of   :number, :mask
   validates_uniqueness_of :number
@@ -9,7 +10,7 @@ class Subnet < ActiveRecord::Base
   validates_uniqueness_of :name, :scope => :domain_id
   validates_associated :domain
 
-  before_destroy Ensure_not_used_by.new(:hosts, :sps)
+  before_destroy Ensure_not_used_by.new(:hosts) #, :sps)
 
   # Subnets are displayed in the form of their network number/network mask
   def to_label
diff --git a/app/models/user.rb b/app/models/user.rb
index 64becfd..594c017 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,6 +1,7 @@
 require 'digest/sha1'
 
 class User < ActiveRecord::Base
+  include Authorization
   attr_protected :password_hash, :password_salt, :admin
   attr_accessor :password, :password_confirmation
 
@@ -9,6 +10,14 @@ class User < ActiveRecord::Base
   has_many :usergroups, :through => :usergroup_member
   has_many :direct_hosts, :as => :owner, :class_name => "Host"
   has_and_belongs_to_many :notices, :join_table => 'user_notices'
+  has_many :user_roles
+  has_many :roles, :through => :user_roles
+  has_and_belongs_to_many :domains,    :join_table => "user_domains"
+  has_and_belongs_to_many :hostgroups, :join_table => "user_hostgroups"
+  has_many :user_facts, :dependent => :destroy
+  has_many :facts, :through => :user_facts, :source => :fact_name
+
+  accepts_nested_attributes_for :user_facts, :reject_if => lambda { |a| a[:criteria].blank? }, :allow_destroy => true
 
   validates_uniqueness_of :login, :message => "already exists"
   validates_presence_of :login, :mail, :auth_source_id
@@ -24,6 +33,9 @@ class User < ActiveRecord::Base
   before_destroy Ensure_not_used_by.new(:hosts), :ensure_admin_is_not_deleted
   validate :name_used_in_a_usergroup
   before_validation :prepare_password
+  after_destroy Proc.new {|user| user.domains.clear; user.hostgroups.clear}
+
+  cattr_accessor :current
 
   def to_label
     "#{firstname} #{lastname}"
@@ -31,7 +43,7 @@ class User < ActiveRecord::Base
   alias_method :name, :to_label
 
   def <=>(other)
-    self.name <=> other.name
+    self.name.downcase <=> other.name.downcase
   end
 
   # The text item to see in a select dropdown menu
@@ -42,35 +54,48 @@ class User < ActiveRecord::Base
   def self.create_admin
     email = SETTINGS[:administrator] || "root@" + Facter.domain
     user = User.create(:login => "admin", :firstname => "Admin", :lastname => "User",
-                :mail => email, :auth_source => AuthSourceInternal.first, :password => "changeme")
+                       :mail => email, :auth_source => AuthSourceInternal.first, :password => "changeme")
     user.update_attribute :admin, true
     user
   end
 
+  # Tries to find the user in the DB and then authenticate against their authentication source
+  # If the user is not in the DB then try to login the user on each available athentication source
+  # If this succeeds then copy the user's details from the authentication source into the User table
+  # Returns : User object OR nil
   def self.try_to_login(login, password)
     # Make sure no one can sign in with an empty password
     return nil if password.to_s.empty?
-    if user = find(:first, :conditions => ["login=?", login])
+
+    if user = find_by_login(login)
       # user is already in local database
-      if user.auth_source
-        # user has an authentication method
-        return nil unless user.auth_source.authenticate(login, password)
+      if user.auth_source and user.auth_source.authenticate(login, password)
+        # user has an authentication method and the authentication was successful
+        User.current = user
+        user.update_attribute(:last_login_on, Time.now.utc)
+      else
+        user = nil
       end
     else
       # user is not yet registered, try to authenticate with available sources
       attrs = AuthSource.authenticate(login, password)
       if attrs
         user = new(*attrs)
+        # The default user must be given :create_users permissions for on-the-fly to work.
         user.login = login
+        User.current = user
         if user.save
           user.reload
           logger.info "User '#{user.login}' auto-created from #{user.auth_source}"
+          user.update_attribute(:last_login_on, Time.now.utc)
         else
           logger.info "Failed to save User '#{user.login}' #{user.errors.full_messages}"
+          user = nil
         end
       end
     end
-    user.update_attribute(:last_login_on, Time.now.utc) if user and not user.new_record?
+    anonymous = Role.find_by_name("Anonymous")
+    User.current.roles <<  anonymous unless user.nil? or User.current.roles.include?(anonymous)
     return user
   rescue => text
     raise text
@@ -80,12 +105,16 @@ class User < ActiveRecord::Base
     self.password_hash == encrypt_password(pass)
   end
 
-  def indirect_hosts
+  def my_usergroups
     all_groups = []
     for usergroup in usergroups
       all_groups += usergroup.all_usergroups
     end
-    all_groups.uniq.map{|g| g.hosts}.flatten.uniq
+    all_groups.uniq
+  end
+
+  def indirect_hosts
+    my_usergroups.map{|g| g.hosts}.flatten.uniq
   end
 
   def hosts
@@ -100,6 +129,30 @@ class User < ActiveRecord::Base
     auth_source and auth_source.can_set_password?
   end
 
+  # Return true if the user is allowed to do the specified action
+  # action can be:
+  # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
+  # * a permission Symbol (eg. :edit_project)
+  def allowed_to?(action, options={})
+    return true if admin?
+
+    return false if roles.empty?
+    roles.detect {|role| role.allowed_to?(action)}
+  end
+
+  def logged?
+    true
+  end
+
+  # Indicates whether the user has host filtering enabled
+  # Returns : Boolean
+  def filtering?
+    filter_on_owner or
+    domains.any?    or
+    hostgroups.any? or
+    facts.any?
+  end
+
   private
 
   def prepare_password
@@ -113,6 +166,8 @@ class User < ActiveRecord::Base
     Digest::SHA1.hexdigest([pass, password_salt].join)
   end
 
+  protected
+
   def name_used_in_a_usergroup
     if Usergroup.all.map(&:name).include?(self.login)
       errors.add_to_base "A usergroup already exists with this name"
@@ -124,8 +179,8 @@ class User < ActiveRecord::Base
   # admin account automatically
   def ensure_admin_is_not_deleted
     if login == "admin"
-      errors.add_to_base "Can't Delete Internal Admin account"
-      logger.warn "Unable to delete Internal Admin Account"
+      errors.add_to_base "Can't delete internal admin account"
+      logger.warn "Unable to delete internal admin account"
       return false
     end
   end
diff --git a/app/models/user_fact.rb b/app/models/user_fact.rb
new file mode 100644
index 0000000..8291c3a
--- /dev/null
+++ b/app/models/user_fact.rb
@@ -0,0 +1,22 @@
+class UserFact < ActiveRecord::Base
+  belongs_to :user
+  belongs_to :fact_name, :class_name => "Puppet::Rails::FactName"
+
+  validates_inclusion_of :andor,    :in => %w{and or}
+  validates_inclusion_of :operator, :in => %w{= !=  > >= < <= }
+  validates_presence_of  :fact_name
+  validates_presence_of  :criteria
+  validates_presence_of  :user
+
+  def initialize *args
+    super *args
+    operator = "="  if operator.empty?
+    andor    = "or" if andor.empty?
+  end
+
+  def to_s
+    n  = user.try(:name) || "Unknown user"
+    fn = fact_name.try(:name) || "Unknown fact"
+    "#{n}:#{fn}:#{criteria.empty? ? "Empty" : criteria}:#{operator}:#{andor}"
+  end
+end
diff --git a/app/models/user_role.rb b/app/models/user_role.rb
new file mode 100644
index 0000000..a66f652
--- /dev/null
+++ b/app/models/user_role.rb
@@ -0,0 +1,30 @@
+# Redmine - project management software
+# Copyright (C) 2006-2009  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.
+
+class UserRole < ActiveRecord::Base
+  belongs_to :user
+  belongs_to :role
+
+  validates_presence_of :role
+  validates_presence_of :user
+
+  def inherited?
+    !inherited_from.nil?
+  end
+
+  private
+end
diff --git a/app/models/usergroup.rb b/app/models/usergroup.rb
index cba8a0c..641d0b3 100644
--- a/app/models/usergroup.rb
+++ b/app/models/usergroup.rb
@@ -1,4 +1,5 @@
 class Usergroup < ActiveRecord::Base
+  include Authorization
   has_many_polymorphs :members, :from => [:usergroups, :users ], :as => :member,
     :through => :usergroup_member, :foreign_key => :usergroup_id, :dependent => :destroy
 
@@ -9,11 +10,6 @@ class Usergroup < ActiveRecord::Base
   # The text item to see in a select dropdown menu
   alias_attribute :select_title, :to_s
 
-  # Support for sorting the groups by name
-  def <=>(other)
-    self.name <=> other.name
-  end
-
   # This methods retrieves all user addresses in a usergroup
   # Returns: Array of strings representing the user's email addresses
   def recipients
@@ -28,7 +24,7 @@ class Usergroup < ActiveRecord::Base
   end
 
   # This methods retrieves all usergroups in a usergroup
-  # Returns: Array of usergroups
+  # Returns: Array of unique usergroups
   def all_usergroups(group_list=[self], user_list=[])
     retrieve_users_and_groups group_list, user_list
     group_list.sort.uniq
diff --git a/app/views/architectures/_form.html.erb b/app/views/architectures/_form.html.erb
index a47567d..9fe9912 100644
--- a/app/views/architectures/_form.html.erb
+++ b/app/views/architectures/_form.html.erb
@@ -5,8 +5,8 @@
   <%= f.text_field :name %>
   </p>
   <p>
-  <%= edit_habtm @architecture, Operatingsystem %>
+  <%= authorized_edit_habtm @architecture, Operatingsystem %>
   </p>
-
-  <p><%= f.submit "Submit" %></p>
+  <br/>
+  <%= f.submit "Submit" %>
 <% end %>
diff --git a/app/views/architectures/index.html.erb b/app/views/architectures/index.html.erb
index 03e52fe..4644244 100644
--- a/app/views/architectures/index.html.erb
+++ b/app/views/architectures/index.html.erb
@@ -8,10 +8,10 @@
   </tr>
   <% for architecture in @architectures %>
     <tr class="<%= cycle("even", "odd") -%>">
-      <td><%= link_to h(architecture.name), edit_architecture_path(architecture) %></td>
+      <td><%= link_to_if_authorized(h(architecture.name), hash_for_edit_architecture_path(:id => architecture.id)) %></td>
       <td><%=h architecture.operatingsystems.to_sentence %></td>
-      <td anign="right">
-        <%= link_to "Destroy", architecture, :confirm => "Delete #{architecture.name}?", :method => :delete %>
+      <td align="right">
+        <%= display_link_if_authorized "Destroy", hash_for_architecture_path(:id => architecture.id, :auth_action => :destroy), :confirm => "Delete #{architecture.name}?", :method => :delete  %>
       </td>
     </tr>
   <% end %>
@@ -19,4 +19,4 @@
 <%= page_entries_info @architectures %>
 <%= will_paginate @architectures %>
 
-<p><%= link_to "New Architecture", new_architecture_path %></p>
+<p><%= display_link_if_authorized "New Architecture", hash_for_new_architecture_path%></p>
diff --git a/app/views/auth_source_ldaps/index.html.erb b/app/views/auth_source_ldaps/index.html.erb
index 0f4584b..5288401 100644
--- a/app/views/auth_source_ldaps/index.html.erb
+++ b/app/views/auth_source_ldaps/index.html.erb
@@ -10,13 +10,13 @@
   </tr>
   <% for auth_source_ldap in @auth_source_ldaps %>
     <tr class="<%= cycle("even", "odd") -%>">
-      <td><%=link_to h(auth_source_ldap.name), edit_auth_source_ldap_path(auth_source_ldap) %></td>
+      <td><%=link_to_if_authorized h(auth_source_ldap.name), hash_for_edit_auth_source_ldap_path(:id => auth_source_ldap.id) %></td>
       <td><%=h auth_source_ldap.host %></td>
       <td><%=h auth_source_ldap.onthefly_register %></td>
       <td><%=h auth_source_ldap.tls %></td>
-      <td><%= link_to "Destroy", auth_source_ldap, :confirm => 'Are you sure?', :method => :delete %></td>
+      <td><%= display_link_if_authorized "Destroy", hash_for_auth_source_ldap_path(:id => auth_source_ldap, :auth_action => :destroy), :confirm => "Delete #{auth_source_ldap.name}?", :method => :delete %></td>
     </tr>
   <% end %>
 </table>
 
-<p><%= link_to "New Ldap Source", new_auth_source_ldap_path %></p>
+<p><%= display_link_if_authorized "New Ldap Source", hash_for_new_auth_source_ldap_path %></p>
diff --git a/app/views/common/403.rhtml b/app/views/common/403.rhtml
new file mode 100644
index 0000000..2b689be
--- /dev/null
+++ b/app/views/common/403.rhtml
@@ -0,0 +1,5 @@
+<h2>Permission denied</h2>
+
+<p>You are not authorised to perform this action</p>
+<p>Please request the required privileges from a foreman administrator</p>
+<p><%= link_to hosts_url -%></p>
diff --git a/app/views/common/_edit_habtm.erb b/app/views/common/_edit_habtm.erb
index 475d7cd..54a253f 100644
--- a/app/views/common/_edit_habtm.erb
+++ b/app/views/common/_edit_habtm.erb
@@ -1,8 +1,12 @@
-| <% for association in associations -%>
-  <%= check_box_tag(
-    "#{klass.class.to_s.downcase}[#{association.class.to_s.downcase}_ids][]",
-      association.id, klass.send(association.class.table_name).to_a.include?(association))
-    %>
-  <%= association %> |
-  <%= hidden_field_tag "#{klass.class.to_s.downcase}[#{association.class.to_s.downcase}_ids][]" %>
-<% end -%>
+<div class="box tabular">
+  <% for association in associations -%>
+    <% content_tag_for :label, association, :class=>"floating" do %>
+      <%= check_box_tag(
+        "#{klass.class.to_s.downcase}[#{association.class.to_s.downcase}_ids][]",
+          association.id, klass.send(association.class.table_name).to_a.include?(association))
+      %>
+      <%= contract association %>
+      <%= hidden_field_tag "#{klass.class.to_s.downcase}[#{association.class.to_s.downcase}_ids][]" %>
+    <% end -%>
+  <% end -%>
+</div>
diff --git a/app/views/common_parameters/_parameter.erb b/app/views/common_parameters/_parameter.erb
index 1163b06..7b6984e 100644
--- a/app/views/common_parameters/_parameter.erb
+++ b/app/views/common_parameters/_parameter.erb
@@ -6,7 +6,7 @@
     <td>
       <%= f.text_field :value, :size => 75 %>
       <%= f.hidden_field :nested %>
-      <%= link_to_remove_fields("remove", f) %>
+      <%= authorized_via_my_scope(params[:controller], params[:action]) ? link_to_remove_fields("remove", f) : "" %>
     </td>
   </tr>
 </table>
diff --git a/app/views/common_parameters/_parameters.erb b/app/views/common_parameters/_parameters.erb
index 8161f2a..bc5d1b0 100644
--- a/app/views/common_parameters/_parameters.erb
+++ b/app/views/common_parameters/_parameters.erb
@@ -2,7 +2,7 @@
   <% f.fields_for type do |builder| -%>
     <%= render "common_parameters/parameter", :f => builder %>
   <% end -%>
-  <p><%= link_to_add_fields "Add a parameter", f, type, "common_parameters/parameter" %></p>
+  <p><%= authorized_via_my_scope(params[:controller], params[:action]) ? link_to_add_fields("Add a parameter", f, type, "common_parameters/parameter") : "Add a parameter" %></p>
 <% end -%>
 
 
diff --git a/app/views/common_parameters/index.html.erb b/app/views/common_parameters/index.html.erb
index fac3ed0..bfb3c57 100644
--- a/app/views/common_parameters/index.html.erb
+++ b/app/views/common_parameters/index.html.erb
@@ -7,11 +7,11 @@
     <th></th>
   </tr>
   <% for common_parameter in @common_parameters %>
-    <tr>
-      <td><%= link_to h(common_parameter), edit_common_parameter_path(common_parameter)%></td>
+    <tr class="<%= cycle("even", "odd") -%>" >
+      <td><%= link_to_if_authorized h(common_parameter), hash_for_edit_common_parameter_path(:id => common_parameter.id)%></td>
       <td><%=h common_parameter.value %></td>
       <td align=""right>
-        <%= link_to "Destroy", common_parameter, :confirm => "Delete #{common_parameter.name}?", :method => :delete %>
+        <%= display_link_if_authorized "Destroy", hash_for_common_parameter_path(:id => common_parameter, :auth_action => :destroy), :confirm => "Delete #{common_parameter.name}?", :method => :delete %>
       </td>
     </tr>
   <% end %>
@@ -19,4 +19,4 @@
 <%= page_entries_info @common_parameters %>
 <%= will_paginate     @common_parameters %>
 
-<p><%= link_to "New Common Parameter", new_common_parameter_path %></p>
+<p><%= display_link_if_authorized "New Common Parameter", hash_for_new_common_parameter_path %></p>
diff --git a/app/views/domains/index.html.erb b/app/views/domains/index.html.erb
index c706e98..337b0c2 100644
--- a/app/views/domains/index.html.erb
+++ b/app/views/domains/index.html.erb
@@ -8,13 +8,13 @@
   </tr>
   <% for domain in @domains %>
     <tr class="<%= cycle("even", "odd") -%>">
-      <td><%= link_to h(domain.fullname.empty? ? domain.name : domain.fullname), edit_domain_path(domain)%></td>
+      <td><%= link_to_if_authorized h(domain.fullname.empty? ? domain.name : domain.fullname), hash_for_edit_domain_path(:id => domain.id)%></td>
       <td><%= domain.hosts.size %>
-      <td align="right"> <%= link_to "Destroy", domain, :confirm => 'Are you sure?', :method => :delete %></td>
+      <td align="right"> <%= display_link_if_authorized "Destroy", hash_for_domain_path(:id => domain, :auth_action => :destroy), :confirm => "Delete #{domain.name}?", :method => :delete %></td>
     </tr>
   <% end %>
 </table>
 <%= page_entries_info @domains %>
 <%= will_paginate     @domains %>
 
-<p><%= link_to "New Domain", new_domain_path %></p>
+<p><%= display_link_if_authorized "New Domain", hash_for_new_domain_path%></p>
diff --git a/app/views/environments/index.html.erb b/app/views/environments/index.html.erb
index e09bc09..f22d7cd 100644
--- a/app/views/environments/index.html.erb
+++ b/app/views/environments/index.html.erb
@@ -8,12 +8,12 @@
   <% for environment in @environments %>
     <tr class="<%= cycle("even", "odd") -%>">
       <td>
-        <%= link_to h(environment.name), edit_environment_path(environment) %>
+        <%= link_to_if_authorized h(environment.name), hash_for_edit_environment_path(:id=> environment.id) %>
       </td>
       <td style="float:right;">
         <%= link_to "Hosts",   hosts_path         + "?search[environment_id_equals]=#{environment.id}"%>
         <%= link_to "classes", puppetclasses_path + "?search[environments_id_equals]=#{environment.id}"%>
-        <%= link_to "Destroy", environment, :confirm => "Delete environment '#{environment.name}'?", :method => :delete %>
+        <%= display_link_if_authorized "Destroy", hash_for_environment_path(:id => environment, :auth_action => :destroy), :confirm => "Delete #{environment.name}?", :method => :delete %>
       </td>
     </tr>
   <% end %>
@@ -22,6 +22,6 @@
 <%= page_entries_info @environments %>
 <%= will_paginate @environmentsS %>
 
-<p><%= link_to "New Environment", new_environment_path %>
-   <%= link_to "Import all classes and environments",  import_environments_environments_path %>
+<p><%= display_link_if_authorized "New Environment", hash_for_new_environment_path %> 
+   <%= display_link_if_authorized "Import all classes and environments",  hash_for_import_environments_environments_path, :action => "import_environments" %>
 </p>
diff --git a/app/views/home/_menu.erb b/app/views/home/_menu.erb
index 2622866..d5447d0 100644
--- a/app/views/home/_menu.erb
+++ b/app/views/home/_menu.erb
@@ -1,18 +1,20 @@
 <div id="menu">
-  <table>
+  <table width="100%">
     <tr>
       <td style="width: 100%;">
         <ul>
-          <li> <%= link_to "Dashboard",  dashboard_path   %> </li>
-          <li> <%= link_to "Hosts",      hosts_path       %> </li>
-          <li> <%= link_to "Reports",    reports_path     %> </li>
-          <li> <%= link_to "Facts",      fact_values_path %> </li>
-          <li> <%= link_to "Audit Log",  audits_path      %> </li>
-          <li> <%= link_to "Statistics", statistics_path  %> </li>
+          <li> <%= link_to_if_authorized "Dashboard",  hash_for_dashboard_path %> </li>
+          <li> <%= link_to_if_authorized "Hosts",      hash_for_hosts_path %> </li>
+          <li> <%= link_to_if_authorized "Reports",    hash_for_reports_path %> </li>
+          <li> <%= link_to_if_authorized "Facts",      hash_for_fact_values_path %> </li>
+          <li> <%= link_to_if_authorized "Audit Log",  hash_for_audits_path %> </li>
+          <li> <%= link_to_if_authorized "Statistics", hash_for_statistics_path %> </li>
         </ul>
       </td>
       <td>
+        <% if User.current.admin? or User.current.allowed_to?(:access_settings) -%>
         <%= render "home/settings" -%>
+        <% end -%>
       </td>
     </tr>
   </table>
diff --git a/app/views/home/_settings.html.erb b/app/views/home/_settings.html.erb
index 32bcb77..d4e4b62 100644
--- a/app/views/home/_settings.html.erb
+++ b/app/views/home/_settings.html.erb
@@ -15,6 +15,9 @@
     ['Puppet Classes',      puppetclasses_url],
   ]
   choices += [
+    ['Roles',               roles_url]
+  ] if SETTINGS[:login] and User.current.admin?
+  choices += [
     ['Users',               users_url],
     ['Usergroups',          usergroups_url]
   ] if  SETTINGS[:login]
diff --git a/app/views/home/_topbar.rhtml b/app/views/home/_topbar.rhtml
index 56d36ff..59a7f8e 100644
--- a/app/views/home/_topbar.rhtml
+++ b/app/views/home/_topbar.rhtml
@@ -1,5 +1,3 @@
-<% if SETTINGS[:login] and SETTINGS[:login] == true -%>
-  <%= link_to "Sign Out #{@user.login}", logout_users_path if @user %>
-<% end -%>
-<%= link_to           'Wiki',      "http://theforeman.org/wiki/foreman" %>
-<%= link_to           'Support',   "http://theforeman.org/projects/foreman/boards" %>
+<%= link_to("Sign Out #{User.current.login}", logout_users_path) if SETTINGS[:login] and SETTINGS[:login] == true and  User.current %>
+<%= link_to 'Wiki',    "http://theforeman.org/wiki/foreman" %>
+<%= link_to 'Support', "http://theforeman.org/projects/foreman/boards" %>
diff --git a/app/views/hostgroups/index.html.erb b/app/views/hostgroups/index.html.erb
index 4d508cb..eff581a 100644
--- a/app/views/hostgroups/index.html.erb
+++ b/app/views/hostgroups/index.html.erb
@@ -7,13 +7,13 @@
   </tr>
   <% for hostgroup in @hostgroups %>
     <tr class="<%= cycle("even", "odd") -%>">
-      <td><%=link_to h(hostgroup.name), edit_hostgroup_path(hostgroup) %></td>
+      <td><%=link_to_if_authorized h(hostgroup.name), hash_for_edit_hostgroup_path(:id => hostgroup) %></td>
       <td align="right">
-        <%= link_to "Destroy", hostgroup, :confirm => 'Are you sure?', :method => :delete %>
+        <%= display_link_if_authorized "Destroy", hash_for_hostgroup_path(:id => hostgroup, :auth_action => :destroy), :confirm => 'Are you sure?', :method => :delete %>
       </td>
     </tr>
   <% end %>
 </table>
 <%= page_entries_info @hostgroups %>
 <%= will_paginate     @hostgroups %>
-<p><%= link_to "New Hostgroup", new_hostgroup_path %></p>
+<p><%= display_link_if_authorized "New Hostgroup", hash_for_new_hostgroup_path %></p>
diff --git a/app/views/hosts/_minilist.html.erb b/app/views/hosts/_minilist.html.erb
index fc748cf..1d4d618 100644
--- a/app/views/hosts/_minilist.html.erb
+++ b/app/views/hosts/_minilist.html.erb
@@ -1,4 +1,5 @@
 <% if hosts.size > 0 -%>
+  <% allow_multiple = authorized_for(:hosts, :edit) or authorized_for(:hosts, :destroy) -%>
   <% form_tag(multiple_actions_hosts_path,:method => "get", :id => "host_select_form") do %>
     <% title header ||= "" %>
     <table class="list">
@@ -14,7 +15,9 @@
       <% hosts.each do |host| -%>
         <tr class="<%= cycle("even", "odd") -%>">
           <td>
-            <%= check_box_tag "host_ids[]", host.id, selected?(host), :id => "host_ids_#{host.id}", :class => 'host_select_boxes', :onclick => 'insertHostVal(this)' -%>
+            <% if allow_multiple -%>
+              <%= check_box_tag "host_ids[]", host.id, selected?(host), :id => "host_ids_#{host.id}", :class => 'host_select_boxes', :onclick => 'insertHostVal(this)' -%>
+            <% end -%>
             <%=name_column(host) %>
           </td>
           <td><%=h host.try(:os) %></td>
@@ -23,15 +26,15 @@
           <td><%=h host.try(:hostgroup) %></td>
           <td><%=last_report_column(host) %></td>
           <td>
-            <%= link_to 'Edit', edit_host_url(host)  %>
-            <%= link_to 'Clone', clone_host_url(host)  %>
-            <%= link_to 'Destroy', host, :confirm => "Delete #{host.name}?", :method => :delete %>
+            <%= 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_link_if_authorized 'Destroy', hash_for_host_path(:id => host, :auth_action => :destroy), :confirm => "Delete #{host.name}?", :method => :delete, :action => :destroy %>
           </td>
         </tr>
       <% end -%>
     </table>
     <div>
-      <%= submit_tag("Edit Multiple Hosts", :title => "Mass assign values to many hosts") %>
+      <%= submit_tag("Edit Multiple Hosts", :title => "Mass assign values to many hosts") if allow_multiple %><br />
     </div>
   <% end -%>
 <% end -%>
diff --git a/app/views/hosts/index.html.erb b/app/views/hosts/index.html.erb
index ec7c95b..62eaa93 100644
--- a/app/views/hosts/index.html.erb
+++ b/app/views/hosts/index.html.erb
@@ -1,7 +1,9 @@
 <%= javascript "host_checkbox" %>
 <%= render 'minilist', :hosts => @hosts, :header => @title || "Hosts"  %>
 <span class="title_action">
-  <%= link_to "New Host", new_host_path %> |
-  <%= link_to_function "Toggle All", "toggleCheck()" %> |
+  <%= display_link_if_authorized "New Host", hash_for_new_host_path %> |
+  <% if authorized_for(:hosts, :edit) or authorized_for(:hosts, :destroy) -%>
+  <%= link_to_function "Toggle All", "toggleCheck()" %>
   <%= link_to "Reset Selection", reset_multiple_hosts_path %>
+  <% end -%>
 </span>
diff --git a/app/views/hosts/show.html.erb b/app/views/hosts/show.html.erb
index 0a01942..4136588 100644
--- a/app/views/hosts/show.html.erb
+++ b/app/views/hosts/show.html.erb
@@ -1,24 +1,20 @@
 <% gcharts_script %>
 <% title @host.to_label %>
 <span class="title_action">
-  <%= link_to "Edit", edit_host_path(@host), :title => "Edit your host" %> |
-  <%= link_to "Facts", facts_host_path, :title => "Browse host facts" %> |
-  <%= link_to "Reports", reports_host_path(@host), :title => "Browse host reports" %> |
-  <%= link_to "YAML", externalNodes_host_path(@host), :title => "Puppet external nodes YAML dump" %> |
+  <%= link_to_if_authorized "Edit",    hash_for_edit_host_path(:id => @host),                                        :title => "Edit your host" %> |
+  <%= link_to_if_authorized "Facts",   hash_for_fact_values_path(:id => @host, "search[host_id_eq]" => @host.id),    :title => "Browse host facts" %> |
+  <%= link_to_if_authorized "Reports", hash_for_reports_host_path(:id => @host),                                     :title => "Browse host reports" %> |
+  <%= link_to_if_authorized "YAML",    hash_for_externalNodes_host_path(:id => @host),                               :title => "Puppet external nodes YAML dump" %> |
   <% unless @host.build -%>
-    <span title="prepare this host for build; requires unattended mode">
-      <%= link_to_if @host.can_be_build?, "Build", setBuild_host_path(@host) %>
-    </span>
+    <%= link_to_if_authorized "Build",        hash_for_setBuild_host_path(:id => @host).merge(:auth_action => :edit, :enable_link => @host.can_be_build?),      :title => "Enable rebuild on next host boot" %>
   <% else -%>
-    <%= link_to "Cancel Build", cancelBuild_host_path(@host), :title => "cancel build request for this host" %>
+    <%= link_to_if_authorized "Cancel Build", hash_for_cancelBuild_host_path(:id => @host).merge(:auth_action => :edit, :enable_link => !@host.can_be_build?),  :title => "Cancel build request for this host" %>
   <% end -%> |
-  <span title="trigger a puppetrun on a node; requires that Puppet run is enabled">
-    <%= link_to_if SETTINGS[:puppetrun], "Run Puppet", puppetrun_host_path(@host) %>
-  </span> |
+    <%= link_to_if_authorized "Run puppet", hash_for_puppetrun_host_path(:id => @host).merge(:auth_action => :edit, :enable_link => SETTINGS[:puppetrun]),  :title => "Trigger a puppetrun on a node; requires that puppet run is enabled" %> |
   <span title="Show all host puppet classes, requires storeconfigs">
     <%= link_to_if @host.resources.count > 0, "All Puppet Classes", storeconfig_klasses_host_path(@host) %>
   </span> |
-  <%= link_to "Delete", host_path(@host), :confirm => 'Are you sure?', :method => :delete%>
+  <%= link_to_if_authorized "Delete", hash_for_host_path(:id => @host, :auth_action => :destroy), :confirm => 'Are you sure?', :method => :delete%>
 </span>
 
 <div id="host_overview">
diff --git a/app/views/layouts/standard.rhtml b/app/views/layouts/standard.rhtml
index 64b833c..37dadb3 100644
--- a/app/views/layouts/standard.rhtml
+++ b/app/views/layouts/standard.rhtml
@@ -12,7 +12,7 @@
       <div class="title" style="text-align:center;">
         The Foreman
       </div>
-      <%= render "home/menu" -%>
+      <%= render("home/menu") unless session[:user].nil? -%>
     </div>
     <%= render "common/searchbar" rescue "<!-- The search facility is not available in this controllers -->" unless params[:action] == 'show' %>
     <%= content_tag('div', flash[:foreman_error],  :class => 'flash error')  if flash[:foreman_error] -%>
diff --git a/app/views/lookup_keys/_fields.html.erb b/app/views/lookup_keys/_fields.html.erb
index 8375802..03d2db8 100644
--- a/app/views/lookup_keys/_fields.html.erb
+++ b/app/views/lookup_keys/_fields.html.erb
@@ -6,4 +6,4 @@
 <% f.fields_for :lookup_values do |builder| -%>
   <%= render 'value', :f => builder %>
 <% end -%>
-<p><%= link_to_add_fields "Add a value", f, :lookup_values, "value" %></p>
+<p><%= authorized_for(params[:controller], params[:action]) ? link_to_add_fields("Add a value", f, :lookup_values, "value") : "Add a value" %></p>
diff --git a/app/views/lookup_keys/_value.html.erb b/app/views/lookup_keys/_value.html.erb
index 0210378..1c0b2db 100644
--- a/app/views/lookup_keys/_value.html.erb
+++ b/app/views/lookup_keys/_value.html.erb
@@ -3,6 +3,6 @@
 <%= f.text_field :value %>
 <%= f.label :priority, "Priority" %>
 <%= f.text_field :priority %>
-<%= link_to_remove_fields("remove", f) %>
+<%= link_to_remove_fields("remove", f) if authorized_for(params[:controller], params[:action]) %>
 </p>
 
diff --git a/app/views/lookup_keys/index.html.erb b/app/views/lookup_keys/index.html.erb
index 5c25042..fd6495a 100644
--- a/app/views/lookup_keys/index.html.erb
+++ b/app/views/lookup_keys/index.html.erb
@@ -9,13 +9,13 @@
 
 <% @lookup_key.each do |lookup_key| %>
   <tr class="<%= cycle("even", "odd") -%>">
-    <td><%= link_to h(lookup_key.key), edit_lookup_key_path(lookup_key) %></td>
+    <td><%= link_to_if_authorized h(lookup_key.key), hash_for_edit_lookup_key_path(:id => lookup_key) %></td>
     <td><%=h lookup_key.lookup_values.count %></td>
-    <td><%= link_to 'Destroy', lookup_key, :confirm => 'Are you sure?', :method => :delete %></td>
+    <td><%= display_link_if_authorized 'Destroy', hash_for_lookup_key_path(:id => lookup_key, :auth_action => :destroy), :confirm => "Delete #{lookup_key.key}?", :method => :delete %></td>
   </tr>
 <% end %>
 </table>
 
 <br />
 
-<%= link_to 'New Variable', new_lookup_key_path %>
+<%= display_link_if_authorized 'New Variable', hash_for_new_lookup_key_path %>
diff --git a/app/views/medias/_form.html.erb b/app/views/medias/_form.html.erb
index 4b33b72..f52d205 100644
--- a/app/views/medias/_form.html.erb
+++ b/app/views/medias/_form.html.erb
@@ -15,8 +15,9 @@
   </small>
   <p>
   <%= f.label :operatingsystems %><br />
-  <%= edit_habtm @media, Operatingsystem %>
+  <%= authorized_edit_habtm @media, Operatingsystem %>
   </p>
+   <br/>
   <% unless @media.new_record? -%>
     <p>Currently used by <%= @media.hosts.count %> hosts</p>
   <% end -%>
diff --git a/app/views/medias/index.html.erb b/app/views/medias/index.html.erb
index ba6863e..a3a93e4 100644
--- a/app/views/medias/index.html.erb
+++ b/app/views/medias/index.html.erb
@@ -9,15 +9,15 @@
   </tr>
   <% for media in @medias %>
     <tr class="<%= cycle("even", "odd") -%>">
-      <td><%= link_to h(media), edit_media_path(media) %></td>
+      <td><%= link_to_if_authorized h(media), hash_for_edit_media_path(:id => media.id) %></td>
       <td><%=h media.path %></td>
       <td><%=h media.operatingsystems.to_sentence %></td>
       <td align="right">
-        <%= link_to "Destroy", media, :confirm => "Delete #{media.name}?", :method => :delete %>
+        <%= display_link_if_authorized "Destroy", hash_for_media_path(:id => media, :auth_action => :destroy), :confirm => "Delete #{media.name}?", :method => :delete %>
       </td>
     </tr>
   <% end %>
 </table>
 <%= page_entries_info @medias %>
 <%= will_paginate     @medias %>
-<p><%= link_to "New Media", new_media_path %></p>
+<p><%= display_link_if_authorized "New Media", hash_for_new_media_path %></p>
diff --git a/app/views/models/index.html.erb b/app/views/models/index.html.erb
index 386dda0..3e204d6 100644
--- a/app/views/models/index.html.erb
+++ b/app/views/models/index.html.erb
@@ -8,10 +8,10 @@
   </tr>
   <% for model in @models %>
     <tr class="<%= cycle("even", "odd") -%>">
-      <td><%=link_to h(model.name), edit_model_path(model)%></td>
+      <td><%=link_to_if_authorized h(model.name), hash_for_edit_model_path(:id => model)%></td>
       <td><%= model.hosts.count %></td>
       <td align="right">
-        <%= link_to "Destroy", model, :confirm => "Delete #{model.name}?", :method => :delete %>
+        <%= display_link_if_authorized "Destroy", hash_for_model_path(:id => model, :auth_action => :destroy), :confirm => "Delete #{model.name}?", :method => :delete %>
       </td>
     </tr>
   <% end %>
@@ -19,4 +19,4 @@
 <%= page_entries_info @models %>
 <%= will_paginate     @models %>
 
-<p><%= link_to "New Model", new_model_path %></p>
+<p><%= display_link_if_authorized "New Model", hash_for_new_model_path %></p>
diff --git a/app/views/operatingsystems/_form.html.erb b/app/views/operatingsystems/_form.html.erb
index c3f6df8..69804e2 100644
--- a/app/views/operatingsystems/_form.html.erb
+++ b/app/views/operatingsystems/_form.html.erb
@@ -20,17 +20,17 @@
   </p>
   <p>
   <% field_set_tag("Architecture") do %>
-    <%= edit_habtm @operatingsystem, Architecture %>
+    <%= authorized_edit_habtm @operatingsystem, Architecture %>
   <% end -%>
   </p>
   <p>
   <% field_set_tag("Partition tables") do %>
-    <%= edit_habtm @operatingsystem, Ptable %>
+    <%= authorized_edit_habtm @operatingsystem, Ptable %>
   <% end -%>
   </p>
   <p>
   <% field_set_tag("Installation Medias") do %>
-    <%= edit_habtm @operatingsystem, Media %>
+    <%= authorized_edit_habtm @operatingsystem, Media %>
   <% end -%>
   </p>
 
diff --git a/app/views/operatingsystems/_search_line.html.erb b/app/views/operatingsystems/_search_line.html.erb
index 90bcc4a..eedd517 100644
--- a/app/views/operatingsystems/_search_line.html.erb
+++ b/app/views/operatingsystems/_search_line.html.erb
@@ -3,9 +3,9 @@
     <%= f.label :name_like, "Name" %>
     <%= f.text_field :name_like, :size => 10 %>
   </div>
-	<div>
-	  <%= f.label :family, "OS family" %>
-	  <%= f.collection_select :family_id, Family.families_as_collection, :value, :name,{ :include_blank => "Choose a family" } %>
-	  <%= f.submit "Go" %> Save as tab: <%= text_field_tag :tab_name, "" %>
-	</div>
+  <div>
+    <%= f.label :family, "OS family" %>
+    <%= f.collection_select :family_id, Family.families_as_collection, :value, :name,{ :include_blank => "Choose a family" } %>
+    <%= f.submit "Go" %> Save as tab: <%= text_field_tag :tab_name, "" %>
+  </div>
 <% end %>
diff --git a/app/views/operatingsystems/index.html.erb b/app/views/operatingsystems/index.html.erb
index 5e62ca8..93c9ba9 100644
--- a/app/views/operatingsystems/index.html.erb
+++ b/app/views/operatingsystems/index.html.erb
@@ -8,10 +8,10 @@
   </tr>
   <% for operatingsystem in @operatingsystems %>
     <tr class="<%= cycle("even", "odd") -%>">
-      <td><%= link_to h(operatingsystem.to_label), edit_operatingsystem_path(operatingsystem) %></td>
+      <td><%= link_to_if_authorized h(operatingsystem.to_label), hash_for_edit_operatingsystem_path(:id => operatingsystem.id) %></td>
       <td><%= h operatingsystem.family %></td>
       <td align="right">
-        <%= link_to "Destroy", operatingsystem_path(operatingsystem), :confirm => "Delete #{operatingsystem.fullname}?", :method => :delete %>
+        <%= display_link_if_authorized "Destroy", hash_for_operatingsystem_path(:id => operatingsystem, :auth_action => :destroy), :confirm => "Delete #{operatingsystem.fullname}?", :action => :destroy %>
       </td>
     </tr>
   <% end %>
@@ -20,4 +20,4 @@
 <%= page_entries_info @operatingsystems, :entry_name => "operating system" %>
 <%= will_paginate @operatingsystems %>
 
-<p><%= link_to "New Operatingsystem", new_operatingsystem_path %></p>
+<p><%= display_link_if_authorized "New Operatingsystem", hash_for_new_operatingsystem_path %></p>
diff --git a/app/views/ptables/_form.html.erb b/app/views/ptables/_form.html.erb
index 9ac8c34..f9253d8 100644
--- a/app/views/ptables/_form.html.erb
+++ b/app/views/ptables/_form.html.erb
@@ -9,7 +9,8 @@
   <%= f.text_area :layout, :size => "80x20" %>
   </p>
   <p>
-  <%= edit_habtm @ptable, Operatingsystem %>
+  <%= authorized_edit_habtm @ptable, Operatingsystem %>
   </p>
+  <br/>
   <p><%= f.submit "Submit" %></p>
 <% end %>
diff --git a/app/views/ptables/index.html.erb b/app/views/ptables/index.html.erb
index dc8d313..4808a09 100644
--- a/app/views/ptables/index.html.erb
+++ b/app/views/ptables/index.html.erb
@@ -8,10 +8,10 @@
   </tr>
   <% for ptable in @ptables %>
     <tr class="<%= cycle("even", "odd") -%>">
-      <td><%=link_to h(ptable.name), edit_ptable_path(ptable) %></td>
+      <td><%=link_to_if_authorized h(ptable.name), hash_for_edit_ptable_path(:id => ptable.id) %></td>
       <td><%=h ptable.operatingsystems.to_sentence %></td>
       <td align="right">
-        <%= link_to "Destroy", ptable, :confirm => "Delete #{ptable.name}?", :method => :delete %>
+        <%= display_link_if_authorized "Destroy", hash_for_ptable_path(:id => ptable, :auth_action => :destroy), :confirm => "Delete #{ptable.name}?", :method => :delete %>
       </td>
     </tr>
   <% end %>
@@ -19,4 +19,4 @@
 <%= page_entries_info @ptables %>
 <%= will_paginate     @ptables %>
 
-<p><%= link_to "New Partition table layout", new_ptable_path %></p>
+<p><%= display_link_if_authorized "New Partition table layout", hash_for_new_ptable_path %></p>
diff --git a/app/views/puppetclasses/_form.html.erb b/app/views/puppetclasses/_form.html.erb
index d3604d4..12494d4 100644
--- a/app/views/puppetclasses/_form.html.erb
+++ b/app/views/puppetclasses/_form.html.erb
@@ -6,17 +6,17 @@
   </p>
   <p>
   <% field_set_tag("Operating Systems") do %>
-    <%= edit_habtm @puppetclass, Operatingsystem %>
+    <%= authorized_edit_habtm @puppetclass, Operatingsystem %>
   <% end -%>
   </p>
   <p>
   <% field_set_tag("Puppet Environments") do %>
-    <%= edit_habtm @puppetclass, Environment %>
+    <%= authorized_edit_habtm @puppetclass, Environment %>
   <% end -%>
   </p>
   <% field_set_tag("Class Groups") do %>
     <p>
-    <%= edit_habtm @puppetclass, Hostgroup %>
+    <%= authorized_edit_habtm @puppetclass, Hostgroup %>
     </p>
   <% end -%>
   <% unless @puppetclass.new_record? -%>
diff --git a/app/views/puppetclasses/index.html.erb b/app/views/puppetclasses/index.html.erb
index b8b3e2b..9496919 100644
--- a/app/views/puppetclasses/index.html.erb
+++ b/app/views/puppetclasses/index.html.erb
@@ -9,16 +9,16 @@
   </tr>
   <% for puppetclass in @puppetclasses %>
     <tr class="<%= cycle("even", "odd") -%>">
-      <td><%=link_to h(puppetclass.name), edit_puppetclass_path(puppetclass) %></td>
+      <td><%=link_to_if_authorized h(puppetclass.name), hash_for_edit_puppetclass_path(:id => puppetclass.id) %></td>
       <td><%=h puppetclass.operatingsystems.to_sentence %></td>
       <td>
         <% for environment in puppetclass.environments -%>
           <%= link_to h(environment), "#", :onclick => rdoc_classes_path(environment, puppetclass.name) %>
-				<% end %>
-			</td>
+        <% end %>
+      </td>
       <td><%=h puppetclass.hostgroups.to_sentence %></td>
       <td align="right">
-        <%= link_to "Destroy", puppetclass, :confirm => "Delete #{puppetclass.name}?", :method => :delete %>
+        <%= display_link_if_authorized "Destroy", hash_for_puppetclass_path(:id => puppetclass, :auth_action => :destroy), :confirm => "Delete #{puppetclass.name}?", :method => :delete %>
       </td>
     </tr>
   <% end %>
@@ -27,7 +27,7 @@
 <%= page_entries_info @puppetclasses %>
 <%= will_paginate @puppetclasses %>
 <span class="title_action">
-  <%= link_to "New Puppetclass", new_puppetclass_path %> |
-  <%= link_to "Import new puppet classes and environments", import_environments_puppetclasses_path %>
+  <%= display_link_if_authorized "New Puppetclass", hash_for_new_puppetclass_path %> |
+  <%= display_link_if_authorized "Import new puppet classes and environments", hash_for_import_environments_puppetclasses_path %>
 </span>
 
diff --git a/app/views/puppetclasses/show.html.erb b/app/views/puppetclasses/show.html.erb
deleted file mode 100644
index 0350fe1..0000000
--- a/app/views/puppetclasses/show.html.erb
+++ /dev/null
@@ -1,20 +0,0 @@
-<% title "Puppetclass" %>
-
-<p>
-  <strong>Name:</strong>
-  <%=h @puppetclass.name %>
-</p>
-<p>
-  <strong>Nameindicator:</strong>
-  <%=h @puppetclass.nameindicator %>
-</p>
-<p>
-  <strong>Operatingsystem:</strong>
-  <%=h @puppetclass.operatingsystem_id %>
-</p>
-
-<p>
-  <%= link_to "Edit", edit_puppetclass_path(@puppetclass) %> |
-  <%= link_to "Destroy", @puppetclass, :confirm => 'Are you sure?', :method => :delete %> |
-  <%= link_to "View All", puppetclasses_path %>
-</p>
diff --git a/app/views/reports/_list.html.erb b/app/views/reports/_list.html.erb
index 8448eeb..5919918 100644
--- a/app/views/reports/_list.html.erb
+++ b/app/views/reports/_list.html.erb
@@ -28,7 +28,7 @@
       <td><%= h report.runtime %></td>
       <td align="right">
         <%= link_to "Details", report %>
-        <%= link_to "Destroy", report, :confirm => "Delete report for #{report.host.name}?", :method => :delete %>
+        <%= display_link_if_authorized "Destroy", hash_for_report_path(:id => report.id, :auth_action => :destroy), :confirm => "Delete report for #{report.host.name}?", :method => :delete %>
       </td>
     </tr>
   <% end %>
diff --git a/app/views/reports/show.rhtml b/app/views/reports/show.rhtml
index ae21806..ec613c5 100644
--- a/app/views/reports/show.rhtml
+++ b/app/views/reports/show.rhtml
@@ -37,6 +37,6 @@ Reported at <%= @report.reported_at.getlocal %>, which is <b><%= time_ago_in_wor
 </div>
 
 <%= link_to 'Back', :back %> |
-<%= link_to 'Delete', report_path(@report, :method => :delete) -%> |
+<%= display_link_if_authorized 'Delete', hash_for_report_path(:id => @report, :auth_action => :destroy), :method => :delete -%> |
 <%= link_to "Host details", @report.host %> |
 <%= link_to "Other reports for this hosts", reports_host_path(@report.host) %>
diff --git a/app/views/roles/_form.rhtml b/app/views/roles/_form.rhtml
new file mode 100644
index 0000000..b7d88d1
--- /dev/null
+++ b/app/views/roles/_form.rhtml
@@ -0,0 +1,27 @@
+<% form_for @role do |f| %>
+  <%= error_messages_for 'role' %>
+
+  <% if @role.builtin? %>
+    <h2><b>Name</b> <i><%= @role.name -%></i></h2>
+  <% else %>
+    <%= f.text_field :name, :label => "Name", :style => "margin-left:1px;", :required => true %>
+  <% end %>
+
+  <h3>Permissions</h3>
+  <div class="box" id="permissions">
+    <% perms_by_block = @permissions.group_by {|p| p.security_block.to_s} %>
+    <% perms_by_block.keys.sort.each do |block| %>
+      <fieldset><legend><%=  block.humanize %></legend>
+        <% perms_by_block[block].each do |permission| %>
+          <label class="floating">
+            <%= check_box_tag 'role[permissions][]', permission.name, (@role.permissions.include? permission.name) %>
+            <%= permission.name.to_s.humanize %>
+          </label>
+        <% end %>
+      </fieldset>
+    <% end %>
+    <br /><%= check_all_links 'permissions' %>
+    <%= hidden_field_tag 'role[permissions][]', '' %>
+  </div>
+  <%= submit_tag "Submit" %>
+<% end %>
diff --git a/app/views/roles/edit.rhtml b/app/views/roles/edit.rhtml
new file mode 100644
index 0000000..c84de0a
--- /dev/null
+++ b/app/views/roles/edit.rhtml
@@ -0,0 +1,4 @@
+<%= javascript "redmine-application" %>
+<% title "Edit roles" %>
+
+<%= render 'form' %>
diff --git a/app/views/roles/index.html.erb b/app/views/roles/index.html.erb
new file mode 100644
index 0000000..93615d9
--- /dev/null
+++ b/app/views/roles/index.html.erb
@@ -0,0 +1,32 @@
+<%= javascript "redmine-application" %>
+<h2>Roles</h2>
+
+<table class="list">
+  <thead>
+    <tr>
+      <th>Role</th>
+      <th></th>
+    </tr>
+  </thead>
+  <tbody>
+    <% for role in @roles %>
+      <tr class="<%= cycle("odd", "even") %>">
+        <td>
+          <%= content_tag(role.builtin? ? 'em' : 'span', link_to_if_authorized(role.name, :action => 'edit', :id => role)) %>
+        </td>
+        <td class="buttons">
+          <%= display_link_if_authorized("Delete", hash_for_role_path(:id => role, :auth_action => :destroy),
+                                    :method => :delete,
+                                    :confirm => "Delete #{role.name}?",
+                                    :class => 'icon icon-del') unless role.builtin?
+          %>
+        </td>
+      </tr>
+    <% end %>
+  </tbody>
+</table>
+<%= page_entries_info @roles %>
+<%= will_paginate     @roles %>
+<p><%= link_to "New role", {:action => 'new'} %>
+
+<%= link_to_if_authorized "Permissions report", :action => 'report' %></p>
diff --git a/app/views/roles/new.rhtml b/app/views/roles/new.rhtml
new file mode 100644
index 0000000..5c32d07
--- /dev/null
+++ b/app/views/roles/new.rhtml
@@ -0,0 +1,4 @@
+<%= javascript "redmine-application" %>
+<% title "New Role" %>
+
+<%= render 'form' %>
diff --git a/app/views/roles/report.rhtml b/app/views/roles/report.rhtml
new file mode 100644
index 0000000..4503256
--- /dev/null
+++ b/app/views/roles/report.rhtml
@@ -0,0 +1,54 @@
+<%= javascript "redmine-application" %>
+<% title "Permissions report" %>
+
+<% form_tag(report_roles_path, :id => 'permissions_form') do %>
+  <%= hidden_field_tag 'permissions[0]', '', :id => nil %>
+  <table class="list">
+    <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><%= check_all_links 'permissions_form' %></p>
+  <p><%= submit_tag "Save" %></p>
+<% end %>
diff --git a/app/views/unattended/jumpstart_finish.rhtml b/app/views/unattended/jumpstart_finish.rhtml
index 3756abb..3c61bb1 100644
--- a/app/views/unattended/jumpstart_finish.rhtml
+++ b/app/views/unattended/jumpstart_finish.rhtml
@@ -22,7 +22,7 @@ EOF
 if [ -f /etc/opt/csw/puppet/puppetd.conf ]
 then
     rm -f /etc/opt/csw/puppet/puppetd.conf
-	ln -s /etc/puppet/puppet.conf /etc/opt/csw/puppet/puppetd.conf
+    ln -s /etc/puppet/puppet.conf /etc/opt/csw/puppet/puppetd.conf
 fi
 echo "Informing Foreman that we are built"
 /opt/csw/bin/wget --no-check-certificate -O /dev/null <%= foreman_url %>
diff --git a/app/views/usergroups/index.html.erb b/app/views/usergroups/index.html.erb
index 34dd4cb..a849c24 100644
--- a/app/views/usergroups/index.html.erb
+++ b/app/views/usergroups/index.html.erb
@@ -9,14 +9,14 @@
   </tr>
   <% for usergroup in @usergroups %>
     <tr class="<%= cycle("even", "odd") -%>">
-      <td><%= link_to h(usergroup.name), edit_usergroup_path(usergroup) %></td>
+      <td><%= link_to_if_authorized h(usergroup.name), hash_for_edit_usergroup_path(:id => usergroup.id) %></td>
       <td><%= usergroup.users.map(&:login).to_sentence %></td>
       <td><%= usergroup.usergroups.map(&:name).to_sentence %></td>
       <td align="right">
-        <%= link_to "Destroy", usergroup, :confirm => 'Are you sure?', :method => :delete %>
+        <%= display_link_if_authorized "Destroy", hash_for_usergroup_path(:id => usergroup, :auth_action => :destroy), :confirm => "Delete #{usergroup.name}?", :method => :delete %>
       </td>
     </tr>
   <% end %>
 </table>
 
-<p><%= link_to("New Usergroup", new_usergroup_path) %></p>
+<p><%= display_link_if_authorized "New Usergroup", hash_for_new_usergroup_path %></p>
diff --git a/app/views/usergroups/update.html.erb b/app/views/usergroups/update.html.erb
deleted file mode 100644
index e69de29..0000000
diff --git a/app/views/users/_filters.html.erb b/app/views/users/_filters.html.erb
new file mode 100644
index 0000000..2c7645f
--- /dev/null
+++ b/app/views/users/_filters.html.erb
@@ -0,0 +1,52 @@
+<td>
+  <% field_set_tag("Select the subset of all hosts to which these permissions apply", :id => "filters", :style => @user.filtering? ? "display:inline;" : "display:none") do %>
+    <table>
+      <tr>
+        <td>
+          Select
+          <%= f.check_box :filter_on_owner %>
+        </td>
+        <td colspan="2">
+          <% field_set_tag "Owned hosts" do %>
+            <%= @user.hosts.empty? ? "None" : @user.hosts.to_sentence %>
+          <% end -%>
+        </td>
+      </tr>
+      <tr>
+        <td>
+          <%= f.select :domains_andor, [["in domain", "and"], ["plus all", "or"]] %>
+        </td>
+        <td style="width:100%">
+          <% field_set_tag "Domain hosts" do %>
+            <%= edit_habtm @user, Domain %>
+          <% end %>
+        </td>
+      </tr>
+      <tr>
+        <td>
+          <%= f.select :hostgroups_andor, [["must be", "and"], ["plus all", "or"]] %>
+        </td>
+        <td>
+          <% field_set_tag "Hostgroup hosts" do %>
+            <%= edit_habtm @user, Hostgroup %>
+          <% end %>
+        </td>
+      </tr>
+      <tr>
+        <td>
+          <%= f.select :facts_andor, [["must match", "and"], ["plus all", "or"]] %>
+        </td>
+        <td>
+          <% field_set_tag "Fact filters" do %>
+            <%= render :partial => "user_facts", :locals => {:f => f} %>
+          <% end %>
+        </td>
+      </tr>
+    </table>
+  <% end -%>
+  <%= link_to_function("Display host filters", nil, :id => "more_link") do |page|
+    page['filters'].visual_effect :blindDown
+    page[:more_link].hide
+  end unless @user.filtering?
+%>
+</td>
diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb
index 610d05a..8b17b54 100644
--- a/app/views/users/_form.html.erb
+++ b/app/views/users/_form.html.erb
@@ -2,34 +2,50 @@
   <%= f.error_messages %>
   <table border="0">
     <tr>
-      <td style="width:1%"> Login</td> <td> <%= f.text_field :login %> </td>
-    </tr>
-    <tr>
-      <td> First name</td> <td> <%= f.text_field :firstname %> </td>
-    </tr>
-    <tr>
-      <td> Last name</td> <td> <%= f.text_field :lastname %> </td>
-    </tr>
-    <tr>
-      <td> Mail</td> <td> <%= f.text_field :mail %> </td>
-    </tr>
-    <tr>
-      <td nowrap> Authorized by</td>
-      <td>
-        <%= f.collection_select :auth_source_id, AuthSource.all.delete_if{|a| a.to_label.nil?}, :id, :to_label, {:include_blank => true}  %>
-      </td>
-    </tr>
-    <tr id="password" <%= 'style="display:none"' unless @user.manage_password? -%>>
-      <td> Password </td>
-      <td> <%= password_field :user, :password %>	</td>
-		</tr>
-		<tr id="verification" <%= 'style="display:none"' unless @user.manage_password? -%>>
-      <td>  Verification </td>
-      <td>  <%= password_field :user, :password_confirmation %> 	</td>
-    </tr>
-    <tr>
-      <td colspan="2"> Administrator <%= f.check_box :admin %> </td>
-    </tr>
+      <td colspan="2">
+        <table>
+          <tr>
+            <td style="width:1%"> Login</td> <td> <%= f.text_field :login %> </td>
+          </tr>
+          <tr>
+            <td> First name</td> <td> <%= f.text_field :firstname %> </td>
+          </tr>
+          <tr>
+            <td> Last name</td> <td> <%= f.text_field :lastname %> </td>
+          </tr>
+          <tr>
+            <td> Mail</td> <td> <%= f.text_field :mail %> </td>
+          </tr>
+          <tr>
+            <td nowrap> Authorized by</td>
+            <td>
+              <%= f.collection_select :auth_source_id, AuthSource.all.delete_if{|a| a.to_label.nil?}, :id, :to_label, {:include_blank => true}  %>
+            </td>
+          </tr>
+          <tr id="password" <%= 'style="display:none"' unless @user.manage_password? -%>>
+            <td> Password </td>
+            <td> <%= password_field :user, :password %>  </td>
+          </tr>
+          <tr id="verification" <%= 'style="display:none"' unless @user.manage_password? -%>>
+            <td>  Verification </td>
+            <td>  <%= password_field :user, :password_confirmation %>   </td>
+          </tr>
+          <% unless @minimal_edit -%>
+          <tr>
+            <td> Administrator</td><td><%= f.check_box :admin -%> </td>
+          </tr>
+          <% end -%>
+        </table>
+      </td>  
+    </tr>
+	<tr>
+	  <td colspan="2">
+	    <% field_set_tag "Roles" do %>
+	      <%= edit_habtm @user, Role.givable %>
+	    <% end %>
+	  </td>
+	</tr>
+    <%= render("filters", :f => f) unless @minimal_edit -%>
     <tr>
       <td colspan="2"> <%= f.submit "Submit" %> </td>
     </tr>
diff --git a/app/views/users/_user_fact.html.erb b/app/views/users/_user_fact.html.erb
new file mode 100644
index 0000000..9ab0252
--- /dev/null
+++ b/app/views/users/_user_fact.html.erb
@@ -0,0 +1,12 @@
+<table class="fields">
+  <tr>
+    <td>Name</td>
+    <td> <%= f.collection_select :fact_name_id, Puppet::Rails::FactName.all.sort, :id, :name, :size => 10 %> </td>
+    <td> <%= f.select :operator, %w{= !=  > >= < <= } %></td>
+    <td>Value</td>
+    <td>
+      <%= f.text_field :criteria, :size => 75 %>
+      <%= authorized_for(params[:controller], params[:action]) ? link_to_remove_fields("remove", f) : "" %>
+    </td>
+  </tr>
+</table>
diff --git a/app/views/users/_user_facts.html.erb b/app/views/users/_user_facts.html.erb
new file mode 100644
index 0000000..7a63140
--- /dev/null
+++ b/app/views/users/_user_facts.html.erb
@@ -0,0 +1,4 @@
+<% f.fields_for :user_facts do |builder| -%>
+  <%= render "user_fact", :f => builder %>
+<% end -%>
+<p><%= authorized_for(params[:controller], params[:action]) ? link_to_add_fields("Add a fact filter", f, :user_facts, "user_fact") : "Add a fact filter" %></p>
diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb
index 979759e..e964135 100644
--- a/app/views/users/index.html.erb
+++ b/app/views/users/index.html.erb
@@ -13,14 +13,14 @@
   </tr>
   <% for user in @users %>
     <tr class="<%= cycle("even", "odd")-%>" >
-      <td><%=link_to h(user.login), edit_user_path(user) %></td>
+      <td><%=link_to_if_authorized h(user.login), hash_for_edit_user_path(:id => user) %></td>
       <td><%=h user.firstname %></td>
       <td><%=h user.lastname %></td>
       <td><%=h user.mail %></td>
       <td><%=admin_column user %></td>
-      <td><%=h last_login_on_column(user) %></td>
-      <td><%=h auth_source_column(user) %></td>
-      <td><%= link_to "Destroy", user, :confirm => 'Are you sure?', :method => :delete %></td>
+      <td><%=h last_login_on_column(user)%></td>
+      <td><%=h auth_source_column(user)%></td>
+      <td><%= display_link_if_authorized "Destroy", hash_for_user_path(:id => user, :auth_action => :destroy), :confirm => "Delete #{user.name}?", :method => :delete %></td>
     </tr>
   <% end %>
 </table>
@@ -28,4 +28,4 @@
 <%= page_entries_info @users %>
 <%= will_paginate @users %>
 
-<p><%= link_to "New User", new_user_path %></p>
+<p><%= display_link_if_authorized "New User", hash_for_new_user_path %></p>
diff --git a/config/environments/test.rb b/config/environments/test.rb
index 2b35a53..1fa7765 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -1,6 +1,6 @@
 require 'shoulda'
 # Settings specified here will take precedence over those in config/environment.rb
-
+require 'shoulda'
 # The test environment is used exclusively to run your application's
 # test suite.  You never need to work with it otherwise.  Remember that
 # your test database is "scratch space" for the test suite and is wiped
diff --git a/config/initializers/foreman.rb b/config/initializers/foreman.rb
index 8c92765..55f0252 100644
--- a/config/initializers/foreman.rb
+++ b/config/initializers/foreman.rb
@@ -1,3 +1,4 @@
+require 'access_permissions'
 require 'puppet'
 require 'puppet/rails'
 require 'gchart'
@@ -14,6 +15,9 @@ $puppet = Puppet.settings.instance_variable_get(:@values) if Rails.env == "test"
 
 SETTINGS[:login] ||= SETTINGS[:ldap]
 
+# We load the default settings for the roles if they are not already present
+Foreman::DefaultData::Loader.load(false)
+
 # Add an empty method to nil. Now no need for if x and x.empty?. Just x.empty?
 class NilClass
   def empty?
@@ -22,6 +26,9 @@ class NilClass
 end
 
 class ActiveRecord::Base
+  def <=>(other)
+    self.name <=> other.name
+  end
 
   def update_single_attribute(attribute, value)
     connection.update(
diff --git a/config/initializers/puppet.rb b/config/initializers/puppet.rb
index f84e7ea..7ef4608 100644
--- a/config/initializers/puppet.rb
+++ b/config/initializers/puppet.rb
@@ -7,6 +7,12 @@ end
 class ParamValue < Puppet::Rails::ParamValue
 end
 
+# We reopen the class and add the associations here as we canot subclass it in app/models as this breaks puppet internals
+class Puppet::Rails::FactName
+  has_many :user_facts
+  has_many :users, :through => :user_facts
+end
+
 # workaround for puppet bug http://projects.reductivelabs.com/issues/3949
 if Facter.puppetversion == "0.25.5"
   begin
diff --git a/config/routes.rb b/config/routes.rb
index 2c7c014..cc9b9b0 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -35,6 +35,7 @@ ActionController::Routing::Routes.draw do |map|
   map.resources :environments, :collection => {:import_environments => :get, :obsolete_and_new => :post}
   map.resources :fact_values
   map.resources :ptables
+  map.resources :roles, :collection => {:report => [:get, :post]}
   map.resources :auth_source_ldaps
   map.resources :users, :collection => {:login => [:get, :post], :logout => :get, :auth_source_selected => :get}
   #default
diff --git a/db/migrate/20100629093200_create_roles.rb b/db/migrate/20100629093200_create_roles.rb
new file mode 100644
index 0000000..24d9be0
--- /dev/null
+++ b/db/migrate/20100629093200_create_roles.rb
@@ -0,0 +1,23 @@
+class CreateRoles  < ActiveRecord::Migration
+  def self.up
+    add_column :users, :role_id, :integer
+    create_table "roles", :force => true do |t|
+      t.column "name",        :string,  :limit => 30
+      t.column "builtin",     :integer
+      t.column "permissions", :text
+    end
+
+    create_table :user_roles do |t|
+      t.column :user_id, :integer
+      t.column :role_id, :integer
+      t.columm :inherited_from, :integer
+    end
+
+  end
+
+  def self.down
+    drop_table :user_roles
+    drop_table :roles
+    remove_column :users, :role_id
+  end
+end
diff --git a/db/migrate/20100701081235_add_user_domains_and_hostgroups.rb b/db/migrate/20100701081235_add_user_domains_and_hostgroups.rb
new file mode 100644
index 0000000..752a10a
--- /dev/null
+++ b/db/migrate/20100701081235_add_user_domains_and_hostgroups.rb
@@ -0,0 +1,20 @@
+# To suport "Edit my domain" and "edit my hostgroup" security access control we need
+# a mechanism to associate a user with some domains and hostgroups
+class AddUserDomainsAndHostgroups < ActiveRecord::Migration
+  def self.up
+    create_table :user_domains, :id => false do |t|
+      t.references :user
+      t.references :domain
+    end
+      
+    create_table :user_hostgroups, :id => false do |t|
+      t.references :user
+      t.references :hostgroup
+    end
+  end
+
+  def self.down
+    drop_table :user_domains
+    drop_table :user_hostgroups
+  end
+end
diff --git a/db/migrate/20100822072954_create_user_facts.rb b/db/migrate/20100822072954_create_user_facts.rb
new file mode 100644
index 0000000..1020d7a
--- /dev/null
+++ b/db/migrate/20100822072954_create_user_facts.rb
@@ -0,0 +1,18 @@
+# To suport "Edit my domain" and "edit my hostgroup" security access control we need
+# a mechanism to associate a user with a fact and a criria for thet fact to evaluate against
+class CreateUserFacts < ActiveRecord::Migration
+  def self.up
+    create_table :user_facts do |t|
+      t.references :user
+      t.references :fact_name
+      t.string     :criteria, :limit => 255
+      t.string     :operator, :limit => 3, :default => "="
+      t.string     :andor,    :limit => 3, :default => "or"
+      t.timestamps
+    end
+  end
+
+  def self.down
+    drop_table :user_facts
+  end
+end
diff --git a/db/migrate/20100914092104_add_my_booleans_to_user.rb b/db/migrate/20100914092104_add_my_booleans_to_user.rb
new file mode 100644
index 0000000..d0221ac
--- /dev/null
+++ b/db/migrate/20100914092104_add_my_booleans_to_user.rb
@@ -0,0 +1,13 @@
+class AddMyBooleansToUser < ActiveRecord::Migration
+  def self.up
+    add_column :users, :domains_andor,    :string, :limit => 3, :default => "or"
+    add_column :users, :hostgroups_andor, :string, :limit => 3, :default => "or"
+    add_column :users, :facts_andor,      :string, :limit => 3, :default => "or"
+  end
+
+  def self.down
+    remove_column :users, :facts_andor
+    remove_column :users, :hostgroups_andor
+    remove_column :users, :domains_andor
+  end
+end
diff --git a/db/migrate/20101103150254_add_owned_filter_to_user.rb b/db/migrate/20101103150254_add_owned_filter_to_user.rb
new file mode 100644
index 0000000..192dd35
--- /dev/null
+++ b/db/migrate/20101103150254_add_owned_filter_to_user.rb
@@ -0,0 +1,9 @@
+class AddOwnedFilterToUser < ActiveRecord::Migration
+  def self.up
+    add_column :users, :filter_on_owner, :boolean
+  end
+
+  def self.down
+    remove_column :users, :filter_on_owner
+  end
+end
diff --git a/lib/access_permissions.rb b/lib/access_permissions.rb
new file mode 100644
index 0000000..81fdbbf
--- /dev/null
+++ b/lib/access_permissions.rb
@@ -0,0 +1,143 @@
+require 'foreman/access_control'
+
+# Permissions
+Foreman::AccessControl.map do |map|
+  map.security_block :architectures do |map|
+    map.permission :view_architectures,    {:architectures => [:index, :show]}
+    map.permission :create_architectures,  {:architectures => [:new, :create]}
+    map.permission :edit_architectures,    {:architectures => [:edit, :update]}
+    map.permission :destroy_architectures, {:architectures => [:destroy]}
+  end
+
+  map.security_block :authentication_providers do |map|
+    map.permission :view_authenticators,    {:auth_source_ldaps => [:index, :show]}
+    map.permission :create_authenticators,  {:auth_source_ldaps => [:new, :create]}
+    map.permission :edit_authenticators,    {:auth_source_ldaps => [:edit, :update]}
+    map.permission :destroy_authenticators, {:auth_source_ldaps => [:destroy]}
+  end
+
+  map.security_block :environments do |map|
+    map.permission :view_environments,    {:environments => [:index, :show]}
+    map.permission :create_environments,  {:environments => [:new, :create]}
+    map.permission :edit_environments,    {:environments => [:edit, :update]}
+    map.permission :destroy_environments, {:environments => [:destroy]}
+    map.permission :import_environments,  {:environments => [:import_environments]}
+  end
+
+  map.security_block :external_variables do |map|
+    map.permission :view_external_variables,    {:lookup_keys => [:index, :show]}
+    map.permission :create_external_variables,  {:lookup_keys => [:new, :create]}
+    map.permission :edit_external_variables,    {:lookup_keys => [:edit, :update]}
+    map.permission :destroy_external_variables, {:lookup_keys => [:destroy]}
+  end
+  
+  map.security_block :domains do |map|
+    map.permission :view_domains,       {:domains => [:index, :show]}
+    map.permission :create_domain,      {:domains => [:new, :create]}
+    map.permission :edit_domains,       {:domains => [:edit, :update]}
+    map.permission :destroy_domains,    {:domains => [:destroy]}
+  end
+
+  map.security_block :global_variables do |map|
+    map.permission :view_globals,    {:common_parameters => [:index, :show]}
+    map.permission :create_globals,  {:common_parameters => [:new, :create]}
+    map.permission :edit_globals,    {:common_parameters => [:edit, :update]}
+    map.permission :destroy_globals, {:common_parameters => [:destroy]}
+  end
+
+  map.security_block :hostgroups do |map|
+    map.permission :view_hostgroups,       {:hostgroups => [:index, :show]}
+    map.permission :create_hostgroups,     {:hostgroups => [:new, :create]}
+    map.permission :edit_hostgroups,       {:hostgroups => [:edit, :update]}
+    map.permission :destroy_hostgroups,    {:hostgroups => [:destroy]}
+  end
+
+  map.security_block :hosts do |map|
+    map.permission :view_hosts,    {:hosts => [:index, :show], :dashboard => [:OutOfSync, :errors, :active]}
+    map.permission :create_hosts,  {:hosts => [:new, :create, :clone]}
+    map.permission :edit_hosts,    {:hosts => [:edit, :update, :multiple_actions, :reset_multiple, :select_multiple_hostgroup, :select_multiple_environment, :submit_multiple_disable,
+                                                 :multiple_parameters, :multiple_disable, :multiple_enable, :update_multiple_environment, :update_multiple_hostgroup, :update_multiple_parameters]}
+    map.permission :destroy_hosts, {:hosts => [:destroy, :multiple_actions, :reset_multiple, :multiple_destroy, :submit_multiple_destroy]}
+  end 
+  
+  map.security_block :medias do |map|
+    map.permission :view_medias,    {:medias => [:index, :show]}
+    map.permission :create_medias,  {:medias => [:new, :create]}
+    map.permission :edit_medias,    {:medias => [:edit, :update]}
+    map.permission :destroy_medias, {:medias => [:destroy]}
+  end
+
+  map.security_block :models do |map|
+    map.permission :view_models,    {:models => [:index, :show]}
+    map.permission :create_models,  {:models => [:new, :create]}
+    map.permission :edit_models,    {:models => [:edit, :update]}
+    map.permission :destroy_models, {:models => [:destroy]}
+  end
+
+  map.security_block :operatingsystems do |map|
+    map.permission :view_operatingsystems,    {:operatingsystems => [:index, :show]}
+    map.permission :create_operatingsystems,  {:operatingsystems => [:new, :create]}
+    map.permission :edit_operatingsystems,    {:operatingsystems => [:edit, :update]}
+    map.permission :destroy_operatingsystems, {:operatingsystems => [:destroy]}
+  end
+
+  map.security_block :partition_tables do |map|
+    map.permission :view_ptables,    {:ptables => [:index, :show]}
+    map.permission :create_ptables,  {:ptables => [:new, :create]}
+    map.permission :edit_ptables,    {:ptables => [:edit, :update]}
+    map.permission :destroy_ptables, {:ptables => [:destroy]}
+  end
+
+  map.security_block :puppetclasses do |map|
+    map.permission :view_puppetclasses,    {:puppetclasses => [:index, :show]}
+    map.permission :create_puppetclasses,  {:puppetclasses => [:new, :create]}
+    map.permission :edit_puppetclasses,    {:puppetclasses => [:edit, :update]}
+    map.permission :destroy_puppetclasses, {:puppetclasses => [:destroy]}
+    map.permission :import_puppetclasses,  {:puppetclasses => [:import_environments]}
+  end
+
+  map.security_block :usergroups do |map|
+    map.permission :view_usergroups,    {:usergroups => [:index, :show]}
+    map.permission :create_usergroups,  {:usergroups => [:new, :create]}
+    map.permission :edit_usergroups,    {:usergroups => [:edit, :update]}
+    map.permission :destroy_usergroups, {:usergroups => [:destroy]}
+  end
+
+  map.security_block :users do |map|
+    map.permission :view_users,    {:users => [:index, :show]}
+    map.permission :create_users,  {:users => [:new, :create]}
+    map.permission :edit_users,    {:users => [:edit, :update]}
+    map.permission :destroy_users, {:users => [:destroy]}
+  end
+
+  map.security_block :settings_menu do |map|
+    map.permission :access_settings,  {:home => [:settings]}
+  end
+  
+  map.security_block :dashboard do |map|
+    map.permission :access_dashboard, {:dashboard => [:index]}
+  end
+  
+  map.security_block :reports do |map|
+    map.permission :view_reports,    {:reports     => [:index, :show]}
+    map.permission :destroy_reports, {:reports     => [:destroy]}
+  end
+  
+  map.security_block :facts do |map|
+    map.permission :view_facts,       {:fact_values => [:index, :show]}
+  end
+  
+  map.security_block :audit_logs do |map|
+    map.permission :view_audit_logs,  {:audits      => [:index, :show]}
+  end
+  map.security_block :statistics do |map|
+    map.permission :view_statistics,  {:statistics  => [:index, :show]}
+  end
+
+  #map.permission :edit_environment_hosts, {:hosts => [:new, :create, :update]}, :require => :loggedin
+  
+#  map.project_module :documents do |map|
+#    map.permission :manage_documents, {:documents => [:new, :edit, :destroy, :create_attachment]}, :require => :loggedin
+#    map.permission :view_documents, :documents => [:index, :show, :download]
+#  end
+end
diff --git a/lib/foreman/access_control.rb b/lib/foreman/access_control.rb
new file mode 100644
index 0000000..cb9d5a7
--- /dev/null
+++ b/lib/foreman/access_control.rb
@@ -0,0 +1,120 @@
+# 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 AccessControl
+
+    class << self
+      def map
+        mapper = Mapper.new
+        yield mapper
+        @permissions ||= []
+        @permissions += mapper.mapped_permissions
+      end
+
+      def permissions
+        @permissions
+      end
+
+      # Returns the permission of given name or nil if it wasn't found
+      # Argument should be a symbol
+      def permission(name)
+        permissions.detect {|p| p.name == name}
+      end
+
+      # Returns the actions that are allowed by the permission of given name
+      def allowed_actions(permission_name)
+        perm = permission(permission_name)
+        perm ? perm.actions : []
+      end
+
+      def public_permissions
+        @public_permissions ||= @permissions.select {|p| p.public?}
+      end
+
+      def members_only_permissions
+        @members_only_permissions ||= @permissions.select {|p| p.require_member?}
+      end
+
+      def loggedin_only_permissions
+        @loggedin_only_permissions ||= @permissions.select {|p| p.require_loggedin?}
+      end
+
+      def available_security_blocks
+        @available_security_blocks ||= @permissions.collect(&:security_block).uniq.compact
+      end
+
+      def blocks_permissions(modules)
+        @permissions.select {|p| p.security_block.nil? || modules.include?(p.security_block.to_s)}
+      end
+    end
+
+    class Mapper
+      def initialize
+        @security_block = nil
+      end
+
+      def permission(name, hash, options={})
+        @permissions ||= []
+        options.merge!(:security_block => @security_block)
+        @permissions << Permission.new(name, hash, options)
+      end
+
+      def security_block(name, options={})
+        @security_block = name
+        yield self
+        @security_block = nil
+      end
+
+      def mapped_permissions
+        @permissions
+      end
+    end
+
+    class Permission
+      attr_reader :name, :actions, :security_block
+
+      def initialize(name, hash, options)
+        @name = name
+        @actions = []
+        @public = options[:public] || false
+        @require = options[:require]
+        @security_block = options[:security_block]
+        hash.each do |controller, actions|
+          if actions.is_a? Array
+            @actions << actions.collect {|action| "#{controller}/#{action}"}
+          else
+            @actions << "#{controller}/#{actions}"
+          end
+        end
+        @actions.flatten!
+      end
+
+      def public?
+        @public
+      end
+
+      def require_member?
+        @require && @require == :member
+      end
+
+      def require_loggedin?
+        @require && (@require == :member || @require == :loggedin)
+      end
+    end
+  end
+end
diff --git a/lib/foreman/controller/environments.rb b/lib/foreman/controller/environments.rb
index 19268b1..7d663cb 100644
--- a/lib/foreman/controller/environments.rb
+++ b/lib/foreman/controller/environments.rb
@@ -29,17 +29,4 @@ module Foreman::Controller::Environments
     end
   end
 
-  protected
-  def no_puppetclass_documentation_handler(exception)
-    if exception.message =~ /No route matches "\/puppet\/rdoc\/([^\/]+)\/classes\/(.+?)\.html/
-      render :template => "puppetclasses/no_route", :locals => {:environment => $1, :name => $2.gsub("/","::")}, :layout => false
-    else
-      if local_request?
-        rescue_action_locally exception
-      else
-        rescue_action_in_public exception
-      end
-    end
-  end
-
 end
diff --git a/lib/foreman/default_data/loader.rb b/lib/foreman/default_data/loader.rb
new file mode 100644
index 0000000..8aa05fa
--- /dev/null
+++ b/lib/foreman/default_data/loader.rb
@@ -0,0 +1,156 @@
+# 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.find(: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.name_is("Manager").empty? ? Role.create(:name => "Manager") : Role.name_is("Manager")[0]
+            if reset or manager.permissions.empty?
+              manager.update_attribute :permissions, manager.setable_permissions.collect {|p| p.name}
+            end
+
+            ptable_editor =  Role.name_is("Edit partition tables").empty? ? Role.create(:name => "Edit partition tables") : Role.name_is("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.name_is("View hosts").empty? ? Role.create(:name => "View hosts") : Role.name_is("View hosts")[0]
+            if reset or hosts_reader.permissions.empty?
+              hosts_reader.update_attribute :permissions, [:view_hosts]
+            end
+
+            hosts_editor =  Role.name_is("Edit hosts").empty? ? Role.create(:name => "Edit hosts") : Role.name_is("Edit hosts")[0]
+            if reset or hosts_editor.permissions.empty?
+              hosts_editor.update_attribute :permissions, [:view_hosts,    :edit_hosts,    :create_hosts,    :destroy_hosts]
+            end
+
+            viewer =  Role.name_is("Viewer").empty? ? Role.create(:name => "Viewer") : Role.name_is("Viewer")[0]
+            if reset or viewer.permissions.empty?
+              viewer.update_attribute :permissions, [:view_hosts,
+                                                     :view_puppetclasses,
+                                                     :view_hostgroups,
+                                                     :view_domains,
+                                                     :view_operatingsystems,
+                                                     :view_medias,
+                                                     :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,
+                                                     :view_usergroups,
+                                                     :view_users,
+                                                     :view_audit_logs]
+            end
+
+            siteman = Role.name_is("Site manager").empty? ? Role.create(:name => "Site manager") : Role.name_is("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_hosts,
+                                                       :create_hosts,
+                                                       :edit_hosts,
+                                                       :destroy_hosts,
+                                                       :view_medias,
+                                                       :create_medias,
+                                                       :edit_medias,
+                                                       :destroy_medias,
+                                                       :view_models,
+                                                       :view_operatingsystems,
+                                                       :view_ptables,
+                                                       :view_puppetclasses,
+                                                       :import_puppetclasses,
+                                                       :view_reports,
+                                                       :destroy_reports,
+                                                       :access_settings,
+                                                       :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_medias,
+                                                                :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]
+            end
+            if reset or Role.anonymous.permissions.empty?
+              Role.anonymous.update_attribute :permissions, [:view_hosts]
+            end
+          end
+          true
+        end
+      end
+    end
+  end
+end
diff --git a/lib/tabular_form_builder.rb b/lib/tabular_form_builder.rb
new file mode 100644
index 0000000..d0433fb
--- /dev/null
+++ b/lib/tabular_form_builder.rb
@@ -0,0 +1,50 @@
+# 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.
+
+require 'action_view/helpers/form_helper'
+
+class TabularFormBuilder < ActionView::Helpers::FormBuilder
+  
+  def initialize(object_name, object, template, options, proc)
+    super
+  end      
+      
+  (field_helpers - %w(radio_button hidden_field) + %w(date_select)).each do |selector|
+    src = <<-END_SRC
+    def #{selector}(field, options = {}) 
+      label_for_field(field, options) + super
+    end
+    END_SRC
+    class_eval src, __FILE__, __LINE__
+  end
+  
+  def select(field, choices, options = {}, html_options = {}) 
+    label_for_field(field, options) + super
+  end
+  
+  # Returns a label tag for the given field
+  def label_for_field(field, options = {})
+      return '' if options.delete(:no_label)
+      text = options[:label].is_a?(Symbol) ? l(options[:label]) : options[:label]
+      text ||= l(("field_" + field.to_s.gsub(/\_id$/, "")).to_sym)
+      text += @template.content_tag("span", " *", :class => "required") if options.delete(:required)
+      @template.content_tag("label", text, 
+                                     :style => "#{options[:style]}",
+                                     :class => (@object && @object.errors[field] ? "error" : nil), 
+                                     :for => (@object_name.to_s + "_" + field.to_s))
+  end
+end
diff --git a/lib/tasks/load_default_data.rake b/lib/tasks/load_default_data.rake
new file mode 100644
index 0000000..465348e
--- /dev/null
+++ b/lib/tasks/load_default_data.rake
@@ -0,0 +1,16 @@
+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/public/images/1downarrow.png b/public/images/1downarrow.png
new file mode 100644
index 0000000..a6d07e9
Binary files /dev/null and b/public/images/1downarrow.png differ
diff --git a/public/images/1uparrow.png b/public/images/1uparrow.png
new file mode 100644
index 0000000..6c0eae4
Binary files /dev/null and b/public/images/1uparrow.png differ
diff --git a/public/images/2downarrow.png b/public/images/2downarrow.png
new file mode 100644
index 0000000..1796516
Binary files /dev/null and b/public/images/2downarrow.png differ
diff --git a/public/images/2uparrow.png b/public/images/2uparrow.png
new file mode 100644
index 0000000..352cc2b
Binary files /dev/null and b/public/images/2uparrow.png differ
diff --git a/public/images/bullet_toggle_minus.png b/public/images/bullet_toggle_minus.png
new file mode 100644
index 0000000..656fc9c
Binary files /dev/null and b/public/images/bullet_toggle_minus.png differ
diff --git a/public/images/bullet_toggle_plus.png b/public/images/bullet_toggle_plus.png
new file mode 100644
index 0000000..bbd81a9
Binary files /dev/null and b/public/images/bullet_toggle_plus.png differ
diff --git a/public/images/toggle_check.png b/public/images/toggle_check.png
new file mode 100644
index 0000000..da85f26
Binary files /dev/null and b/public/images/toggle_check.png differ
diff --git a/public/javascripts/redmine-application.js b/public/javascripts/redmine-application.js
new file mode 100644
index 0000000..3313f6a
--- /dev/null
+++ b/public/javascripts/redmine-application.js
@@ -0,0 +1,132 @@
+/* redMine - project management software
+   Copyright (C) 2006-2008  Jean-Philippe Lang */
+
+function checkAll (id, checked) {
+	var els = Element.descendants(id);
+	for (var i = 0; i < els.length; i++) {
+    if (els[i].disabled==false) {
+      els[i].checked = checked;
+    }
+	}
+}
+
+function toggleCheckboxesBySelector(selector) {
+	boxes = $$(selector);
+	var all_checked = true;
+	for (i = 0; i < boxes.length; i++) { if (boxes[i].checked == false) { all_checked = false; } }
+	for (i = 0; i < boxes.length; i++) { boxes[i].checked = !all_checked; }
+}
+
+function showAndScrollTo(id, focus) {
+	Element.show(id);
+	if (focus!=null) { Form.Element.focus(focus); }
+	Element.scrollTo(id);
+}
+
+function toggleRowGroup(el) {
+	var tr = Element.up(el, 'tr');
+	var n = Element.next(tr);
+	tr.toggleClassName('open');
+	while (n != undefined && !n.hasClassName('group')) {
+		Element.toggle(n);
+		n = Element.next(n);
+	}
+}
+
+function toggleFieldset(el) {
+	var fieldset = Element.up(el, 'fieldset');
+	fieldset.toggleClassName('collapsed');
+	Effect.toggle(fieldset.down('div'), 'slide', {duration:0.2});
+}
+
+function showTab(name) {
+    var f = $$('div#content .tab-content');
+	for(var i=0; i<f.length; i++){
+		Element.hide(f[i]);
+	}
+    var f = $$('div.tabs a');
+	for(var i=0; i<f.length; i++){
+		Element.removeClassName(f[i], "selected");
+	}
+	Element.show('tab-content-' + name);
+	Element.addClassName('tab-' + name, "selected");
+	return false;
+}
+
+function moveTabRight(el) {
+	var lis = Element.up(el, 'div.tabs').down('ul').childElements();
+	var tabsWidth = 0;
+	var i;
+	for (i=0; i<lis.length; i++) {
+		if (lis[i].visible()) {
+			tabsWidth += lis[i].getWidth() + 6;
+		}
+	}
+	if (tabsWidth < Element.up(el, 'div.tabs').getWidth() - 60) {
+		return;
+	}
+	i=0;
+	while (i<lis.length && !lis[i].visible()) {
+		i++;
+	}
+	lis[i].hide();
+}
+
+function moveTabLeft(el) {
+	var lis = Element.up(el, 'div.tabs').down('ul').childElements();
+	var i = 0;
+	while (i<lis.length && !lis[i].visible()) {
+		i++;
+	}
+	if (i>0) {
+		lis[i-1].show();
+	}
+}
+
+function displayTabsButtons() {
+	var lis;
+	var tabsWidth = 0;
+	var i;
+	$$('div.tabs').each(function(el) {
+		lis = el.down('ul').childElements();
+		for (i=0; i<lis.length; i++) {
+			if (lis[i].visible()) {
+				tabsWidth += lis[i].getWidth() + 6;
+			}
+		}
+		if ((tabsWidth < el.getWidth() - 60) && (lis[0].visible())) {
+			el.down('div.tabs-buttons').hide();
+		} else {
+			el.down('div.tabs-buttons').show();
+		}
+	});
+}
+
+function promptToRemote(text, param, url) {
+    value = prompt(text + ':');
+    if (value) {
+        new Ajax.Request(url + '?' + param + '=' + encodeURIComponent(value), {asynchronous:true, evalScripts:true});
+        return false;
+    }
+}
+
+
+/* shows and hides ajax indicator */
+Ajax.Responders.register({
+    onCreate: function(){
+        if ($('ajax-indicator') && Ajax.activeRequestCount > 0) {
+            Element.show('ajax-indicator');
+        }
+    },
+    onComplete: function(){
+        if ($('ajax-indicator') && Ajax.activeRequestCount == 0) {
+            Element.hide('ajax-indicator');
+        }
+    }
+});
+
+Event.observe(window, 'load', function() {
+  $$('.hol').each(function(el) {
+  	el.hide();
+	});
+});
diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css
index 53b6757..62f9cea 100644
--- a/public/stylesheets/style.css
+++ b/public/stylesheets/style.css
@@ -39,14 +39,13 @@ input {
 p {
   line-height: 130%;
 }
-#header {height:3.5em;background-color:#406480;color:#E6DFCF; position:relative; padding-bottom: 4px;}
+#header {height:3.5em;background-color:#406480;color:#E6DFCF; position:relative; padding-bottom: 8px;}
 
 #header a {
   float:right;
   margin-right: 0.5em;
   color: #CFDCE6;
   height:1.8em;
-  font-size: 0.8em;
 }
 
 #header .title {
@@ -66,26 +65,32 @@ p {
 #menu li {
   float:left;
   list-style-type:none;
+  font-variant: small-caps;
+  padding: 4px 10px 4px 10px;
+  height:1.2em;
+  font-size: 100%;
+}
+#menu li .entry{
+	  padding: 4px 10px 4px 10px;
 }
 #menu li a {
-  font-variant: small-caps;
+  margin-right: 0.0em;
   float:left;
   color: #fff;
   text-decoration: none;
-  padding: 4px 10px 4px 10px;
-  height:1.2em;
-  font-size: 100%;
 }
 #menu li a:hover {background:#759FCF; color:#fff;}
 #menu li a:active {background:#fff; color:#555;}
 
 #content {
   position:relative;
-  float: center;
   padding: 1%;
   margin: 0 auto;
   clear: both;
 }
+#content br{
+	clear:both;
+}
 #content h3 {
   margin-top: 15px;
 }
@@ -114,7 +119,7 @@ p {
   border: 2px solid;
 }
 
-div.flash {margin-top: 24px;}
+div.flash {margin-top: 28px;}
 
 div.flash.error, #errorExplanation {
   background: url(../images/false.png) 8px 5px no-repeat;
@@ -309,3 +314,65 @@ div#run_distribution {
   border: solid 1px black;
   margin-top: 20px;
 }
+/***** Tabular forms ******/
+.tabular p{
+margin: 0;
+padding: 5px 0 8px 0;
+padding-left: 180px; /*width of left column containing the label elements*/
+height: 1%;
+clear:left;
+}
+
+html>body .tabular p {overflow:hidden;}
+
+.tabular label{
+font-weight: bold;
+float: left;
+text-align: right;
+margin-left: -180px; /*width of left column*/
+width: 175px; /*width of labels. Should be smaller than left column to create some right 
+margin*/
+}
+
+.tabular label.floating{
+font-weight: normal;
+margin-left: 0px;
+text-align: left;
+width: 180px;
+}
+
+.tabular label.block{
+font-weight: normal;
+margin-left: 0px !important;
+text-align: left;
+float: none;
+display: block;
+width: auto;
+}
+
+.tabular fieldset { border: 1px solid #E9f1d1}
+
+.tabular.settings p{ padding-left: 300px; }
+.tabular.settings label{ margin-left: -300px; width: 295px; }
+.tabular.settings textarea { width: 99%; }
+
+fieldset.settings label { display: block; }
+
+.required {color: #bb0000;}
+.summary {font-style: italic;}
+
+#attachments_fields input[type=text] {margin-left: 8px; }
+
+div.attachments { margin-top: 12px; }
+div.attachments p { margin:4px 0 2px 0; }
+div.attachments img { vertical-align: middle; }
+div.attachments span.author { font-size: 0.9em; color: #888; }
+
+p.other-formats { text-align: right; font-size:0.9em; color: #666; }
+.other-formats span + span:before { content: "| "; }
+
+a.atom { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; }
+
+tr span.expander {background-image: url(../images/bullet_toggle_plus.png); padding-left: 8px; margin-left: 0; cursor: pointer;}
+tr.open span.expander {background-image: url(../images/bullet_toggle_minus.png);}
+
diff --git a/test/fixtures/architectures.yml b/test/fixtures/architectures.yml
index 4420dc6..0225c3d 100644
--- a/test/fixtures/architectures.yml
+++ b/test/fixtures/architectures.yml
@@ -1,6 +1,6 @@
 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
 
- one:
+x86_64:
    name: x86_64
 #
 # two:
diff --git a/test/fixtures/domains.yml b/test/fixtures/domains.yml
index b16c451..b05f489 100644
--- a/test/fixtures/domains.yml
+++ b/test/fixtures/domains.yml
@@ -7,3 +7,7 @@ mydomain:
 yourdomain:
   name: yourdomain.net
   fullname: somewhere in yourdomain
+
+unuseddomain:
+  name: unused.net
+  fullname: somewhare that is never used
\ No newline at end of file
diff --git a/test/fixtures/hosts.yml b/test/fixtures/hosts.yml
index eddbe12..6cc2eab 100644
--- a/test/fixtures/hosts.yml
+++ b/test/fixtures/hosts.yml
@@ -1,14 +1,24 @@
 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
 
 one:
-  name: myname
+  name: myname.mydomain.net
   ip: "1.2.3.4"
-  mac: aabbCCddeeff
+  mac: aabbCCddeeee
+  environment: production
+  architecture: x86_64
+  operatingsystem: one
+  ptable: one
+  domain: mydomain
 
 two:
-  name: temp.domain.com
+  name: temp.yourdomain.net
   ip: "1.2.3.5"
   mac: "aa:bb:cc:dd:ee:ff"
+  environment: production
+  architecture: x86_64
+  operatingsystem: one
+  ptable: one
+  domain: yourdomain
 
 otherfullhost:
   name: otherfullhost
diff --git a/test/fixtures/parameters.yml b/test/fixtures/parameters.yml
index 37b31a4..baa3498 100644
--- a/test/fixtures/parameters.yml
+++ b/test/fixtures/parameters.yml
@@ -15,4 +15,10 @@ host:
   name: host1
   value: host1
   type: HostParameter
-  host: one
\ No newline at end of file
+  host: one
+
+group:
+  name: group1
+  value: group1
+  type: GroupParameter
+  hostgroup: common
diff --git a/test/fixtures/roles.yml b/test/fixtures/roles.yml
new file mode 100644
index 0000000..6e88041
--- /dev/null
+++ b/test/fixtures/roles.yml
@@ -0,0 +1,181 @@
+---
+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_medias
+    - :create_medias
+    - :edit_medias
+    - :destroy_medias
+    - :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
+
+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_medias
+    - :view_models
+    - :view_operatingsystems
+    - :view_ptables
+    - :view_puppetclasses
+    - :view_reports
+    - :access_settings
+    - :view_statistics
+    - :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_medias
+    - :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
+
diff --git a/test/fixtures/subnets.yml b/test/fixtures/subnets.yml
new file mode 100644
index 0000000..27650e2
--- /dev/null
+++ b/test/fixtures/subnets.yml
@@ -0,0 +1,9 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+one:
+   name: one
+   number: 2.3.4.5
+   mask: 255.255.255.0
+#
+# two:
+#   column: value
diff --git a/test/fixtures/user_facts.yml b/test/fixtures/user_facts.yml
new file mode 100644
index 0000000..5bf0293
--- /dev/null
+++ b/test/fixtures/user_facts.yml
@@ -0,0 +1,7 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+# one:
+#   column: value
+#
+# two:
+#   column: value
diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml
index d697791..cffb761 100644
--- a/test/fixtures/users.yml
+++ b/test/fixtures/users.yml
@@ -16,13 +16,13 @@ two:
   mail: usertwo@someware.com
   admin: false
   last_login_on: 2009-10-12 21:50:04
-  auth_source_id: 1
+  auth_source: auth_sources(:one)
 
 admin:
   login: admin
   firstname: Admin
   lastname: User
-  mail: a@b.c.com
+  mail: admin@someware.com
   admin: true
   last_login_on: 2009-10-12 21:50:04
-  auth_source_id: 1
+  auth_source: auth_sources(:one)
diff --git a/test/functional/architectures_controller_test.rb b/test/functional/architectures_controller_test.rb
index fbf5682..5c5211d 100644
--- a/test/functional/architectures_controller_test.rb
+++ b/test/functional/architectures_controller_test.rb
@@ -47,4 +47,21 @@ class ArchitecturesControllerTest < ActionController::TestCase
     assert_redirected_to architectures_url
     assert !Architecture.exists?(architecture.id)
   end
+
+  def setup_user
+     @request.session[:user] = users(:one).id
+     users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+  end
+
+  def user_with_viewer_rights_should_fail_to_edit_an_architecture
+    setup_user
+    get :edit, {:id => Architecture.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  def user_with_viewer_rights_should_succeed_in_viewing_architectures
+    setup_user
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/audits_controller_test.rb b/test/functional/audits_controller_test.rb
index 4408a39..9254a8a 100644
--- a/test/functional/audits_controller_test.rb
+++ b/test/functional/audits_controller_test.rb
@@ -10,4 +10,20 @@ class AuditsControllerTest < ActionController::TestCase
     get :show, {:id => Audit.first}, set_session_user
     assert_template 'show'
   end
+
+  def setup_user
+    @request.session[:user] = users(:one).id
+    users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+  end
+  def user_with_viewer_rights_should_fail_to edit_audit
+    setup_user
+    get :edit, {:id => Audit.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  def user_with_viewer_rights_succeed_in_viewing_audits
+    setup_user
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/auth_source_ldaps_controller_test.rb b/test/functional/auth_source_ldaps_controller_test.rb
index 2833558..f066263 100644
--- a/test/functional/auth_source_ldaps_controller_test.rb
+++ b/test/functional/auth_source_ldaps_controller_test.rb
@@ -46,4 +46,21 @@ class AuthSourceLdapsControllerTest < ActionController::TestCase
     assert_redirected_to auth_source_ldaps_url
     assert !AuthSourceLdap.exists?(auth_source_ldap.id)
   end
+
+   def setup_user
+     @request.session[:user] = users(:one).id
+     users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+  end
+
+  def user_with_viewer_rights_should_fail_to_an_edit_authentication_source
+    setup_user
+    get :edit, {:id => AuthSourceLdap.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  def user_with_viewer_rights_should_succeed_in_viewing_authentication_sources
+    setup_user
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/common_parameters_controller_test.rb b/test/functional/common_parameters_controller_test.rb
index a3c9c89..d4ebd86 100644
--- a/test/functional/common_parameters_controller_test.rb
+++ b/test/functional/common_parameters_controller_test.rb
@@ -46,4 +46,24 @@ class CommonParametersControllerTest < ActionController::TestCase
     assert_redirected_to common_parameters_url
     assert !CommonParameter.exists?(common_parameter.id)
   end
+
+  def setup_user
+    @request.session[:user] = users(:one).id
+    users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+  end
+
+  context 'user with viewer rights' do
+
+    def user_with_viewer_rights_should_fail_to_edit_a_common_parameter
+      setup_user
+      get :edit, {:id => CommonParameter.first.id}
+      assert @response.status == '403 Forbidden'
+    end
+
+    def user_with_viewer_rights_should_succeed_in_viewing_common_parameters
+      setup_user
+      get :index
+      assert_response :success
+    end
+  end
 end
diff --git a/test/functional/dashboard_controller_test.rb b/test/functional/dashboard_controller_test.rb
index 2a7d689..00fb82a 100644
--- a/test/functional/dashboard_controller_test.rb
+++ b/test/functional/dashboard_controller_test.rb
@@ -6,4 +6,10 @@ class DashboardControllerTest < ActionController::TestCase
     assert_response :success
   end
 
+  def user_with_viewer_rights_should_succeed_in_viewing_the_dashboard
+    @request.session[:user] = users(:one).id
+    users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/domains_controller_test.rb b/test/functional/domains_controller_test.rb
index 6373f33..5c187b4 100644
--- a/test/functional/domains_controller_test.rb
+++ b/test/functional/domains_controller_test.rb
@@ -47,4 +47,21 @@ class DomainsControllerTest < ActionController::TestCase
     assert_redirected_to domains_url
     assert !Domain.exists?(domain.id)
   end
+
+  def setup_user
+    @request.session[:user] = users(:one).id
+    users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+  end
+
+  def user_with_viewer_rights_should_fail_to_edit_a_domain
+    setup_users
+    get :edit, {:id => Domain.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  def user_with_viewer_rights_should_succeed_in_viewing_domains
+    setup_users
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/environments_controller_test.rb b/test/functional/environments_controller_test.rb
index 6bdf51d..f85ea39 100644
--- a/test/functional/environments_controller_test.rb
+++ b/test/functional/environments_controller_test.rb
@@ -156,4 +156,21 @@ class EnvironmentsControllerTest < ActionController::TestCase
 
     assert_redirected_to environments_url
   end
+
+  def setup_user
+    @request.session[:user] = users(:one).id
+    users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+  end
+
+  test 'user with viewer rights should fail to edit an environment' do
+    setup_user
+    get :edit, {:id => Environment.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  test 'user with viewer rights should succeed in viewing environments' do
+    setup_user
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/fact_values_controller_test.rb b/test/functional/fact_values_controller_test.rb
index a1ba721..b8a7c6b 100644
--- a/test/functional/fact_values_controller_test.rb
+++ b/test/functional/fact_values_controller_test.rb
@@ -22,4 +22,10 @@ class FactValuesControllerTest < ActionController::TestCase
     assert_response :success
   end
 
+  test 'user with viewer rights should succeed in viewing facts' do
+    @request.session[:user] = users(:one).id
+    users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/hostgroups_controller_test.rb b/test/functional/hostgroups_controller_test.rb
index 4be39b1..e985936 100644
--- a/test/functional/hostgroups_controller_test.rb
+++ b/test/functional/hostgroups_controller_test.rb
@@ -47,4 +47,21 @@ class HostgroupsControllerTest < ActionController::TestCase
     assert_redirected_to hostgroups_url
     assert !Hostgroup.exists?(hostgroup.id)
   end
+
+  def setup_user
+    @request.session[:user] = users(:one).id
+    users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+  end
+
+  test 'user with viewer rights should fail to edit a hostgroup ' do
+    setup_user
+    get :edit, {:id => Hostgroup.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  test 'user with viewer rights should succeed in viewing hostgroups' do
+    setup_user
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/hosts_controller_test.rb b/test/functional/hosts_controller_test.rb
index 147406a..1baf516 100644
--- a/test/functional/hosts_controller_test.rb
+++ b/test/functional/hosts_controller_test.rb
@@ -138,35 +138,173 @@ class HostsControllerTest < ActionController::TestCase
     assert_template 'new'
   end
 
-  context "multiple assignments" do
-    setup do
-      @host1 = hosts(:otherfullhost)
-      @host2 = hosts(:anotherfullhost)
+  def setup_user_and_host operation
+    as_admin do
+      @one             = users(:one)
+      @one.domains     = []
+      @one.hostgroups  = []
+      @one.user_facts  = []
+      @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"
+    as_admin do
+      @one.domains  = [domains(:mydomain)]
+      @host1.domain = domains(:mydomain)
+      @host2.domain = domains(:yourdomain)            
+    end
+    get :index
+    assert_response :success
+    assert @response.body =~ /#{@host1.shortname}/
+  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
+    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
+    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"
+    as_admin do
+      @host1.owner = @one
+      @host2.owner = users(:two)
+      @one.filter_on_owner = true
+      @one.save!
+      @host1.save!
+      @host2.save!
+    end
+    get :index
+    assert_response :success
+    assert @response.body !~ /#{@host2.name}/
+  end
+
+  test 'user with edit host rights and hostgroup is set should succeed in viewing host1' 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
+    assert_response :success
+    assert @response.body =~ /#{@host1.name}/
+  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
+    assert_response :success
+    assert @response.body !~ /#{@host2.name}/
+  end
 
-    context "with update environments" do
-      should "change environments" do
-        assert @host1.environment == environments(:production)
-        assert @host2.environment == environments(:production)
-        post :update_multiple_environment,
-             {:environment => { :id => environments(:global_puppetmaster).id}},
-             {:selected => [@host1.id, @host2.id], :user => User.first.id}
-        assert Host.find(@host1.id).environment == environments(:global_puppetmaster)
-        assert Host.find(@host2.id).environment == environments(:global_puppetmaster)
-      end
+  test 'user with edit host rights and facts are set should succeed in viewing host1' do
+    setup_user_and_host "Edit"
+    as_admin do
+      fn_id = Puppet::Rails::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
-    context "with update parameters" do
-      should "change parameters" do
-        @host1.host_parameters = [HostParameter.create(:name => "p1", :value => "yo")]
-        @host2.host_parameters = [HostParameter.create(:name => "p1", :value => "hi")]
-        post :update_multiple_parameters,
-             {:name => { "p1" => "hello"}},
-             {:selected => [@host1.id, @host2.id], :user => User.first.id}
-        assert Host.find(@host1.id).host_parameters[0][:value] == "hello"
-        assert Host.find(@host2.id).host_parameters[0][:value] == "hello"
-      end
+    get :index
+    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 = Puppet::Rails::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
+    assert_response :success
+    assert @response.body !~ /#{@host2.name}/
+  end
+
+  test 'user with view host rights should fail to edit host' do
+    setup_user_and_host "View"
+    get :edit, {:id => @host1.id}
+    assert @response.status == '403 Forbidden'
+  end
+  
+  test 'user with view host rights should should succeed in viewing hosts' do
+    setup_user_and_host "View"
+    get :index
+    assert_response :success
+  end
+
+
+  def setup_multiple_environments
+    setup_user_and_host "edit"
+    as_admin do
+      @host1 = hosts(:otherfullhost)
+      @host2 = hosts(:anotherfullhost)
+    end
+  end
+
+  test "user with edit host rights with update environments should change environments" do
+    setup_multiple_environments
+    assert @host1.environment == environments(:production)
+    assert @host2.environment == environments(:production)
+    post :update_multiple_environment,
+         {:environment => { :id => environments(:global_puppetmaster).id}},
+         {:selected => [@host1.id, @host2.id], :user => User.first.id}
+    assert Host.find(@host1.id).environment == environments(:global_puppetmaster)
+    assert Host.find(@host2.id).environment == environments(:global_puppetmaster)
+  end
 
+  test "user with edit host rights with update parameters should change parameters" do
+    setup_multiple_environments
+    @host1.host_parameters = [HostParameter.create(:name => "p1", :value => "yo")]
+    @host2.host_parameters = [HostParameter.create(:name => "p1", :value => "hi")]
+    post :update_multiple_parameters,
+         {:name => { "p1" => "hello"}},
+         {:selected => [@host1.id, @host2.id], :user => User.first.id}
+    assert Host.find(@host1.id).host_parameters[0][:value] == "hello"
+    assert Host.find(@host2.id).host_parameters[0][:value] == "hello"
   end
   test "should get errors" do
     get :errors, {}, set_session_user
@@ -195,6 +333,7 @@ class HostsControllerTest < ActionController::TestCase
 
   private
   def initialize_host
+    User.current = users(:admin)
     @host = Host.create :name => "myfullhost",
       :mac => "aabbecddeeff",
       :ip => "123.05.02.03",
diff --git a/test/functional/lookup_keys_controller_test.rb b/test/functional/lookup_keys_controller_test.rb
index 7dc467c..a9cd045 100644
--- a/test/functional/lookup_keys_controller_test.rb
+++ b/test/functional/lookup_keys_controller_test.rb
@@ -38,4 +38,21 @@ class LookupKeysControllerTest < ActionController::TestCase
 
     assert_redirected_to lookup_keys_path
   end
+
+  def setup_user
+    @request.session[:user] = users(:one).id
+    users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+  end
+
+  test 'user with viewer rights should fail to edit an external variable' do
+    setup_user
+    get :edit, {:id => LookupKey.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  test 'user with viewer rights should succeed in viewing external variables' do
+    setup_user
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/medias_controller_test.rb b/test/functional/medias_controller_test.rb
index 04ff7ae..bfaa743 100644
--- a/test/functional/medias_controller_test.rb
+++ b/test/functional/medias_controller_test.rb
@@ -46,4 +46,21 @@ class MediasControllerTest < ActionController::TestCase
     assert_redirected_to medias_url
     assert !Media.exists?(media.id)
   end
+
+  def setup_user
+    @request.session[:user] = users(:one).id
+    users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+  end
+
+  test 'user with viewer rights should fail to edit a media' do
+    setup_user
+    get :edit, {:id => Media.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  test 'user with viewer rights should succeed in viewing medias' do
+    setup_user
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/models_controller_test.rb b/test/functional/models_controller_test.rb
index e15effd..c8bc1db 100644
--- a/test/functional/models_controller_test.rb
+++ b/test/functional/models_controller_test.rb
@@ -46,4 +46,21 @@ class ModelsControllerTest < ActionController::TestCase
     assert_redirected_to models_url
     assert !Model.exists?(model.id)
   end
+
+  def setup_user
+    @request.session[:user] = users(:one).id
+    users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+  end
+
+  test 'user with viewer rights should fail to edit a model' do
+    setup_user
+    get :edit, {:id => Model.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  test 'user with viewer rights should succeed in viewing models' do
+    setup_user
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/operatingsystems_controller_test.rb b/test/functional/operatingsystems_controller_test.rb
index 3b16ab5..02bad4b 100644
--- a/test/functional/operatingsystems_controller_test.rb
+++ b/test/functional/operatingsystems_controller_test.rb
@@ -49,4 +49,21 @@ class OperatingsystemsControllerTest < ActionController::TestCase
     assert_redirected_to operatingsystems_url
     assert !Operatingsystem.exists?(operatingsystem.id)
   end
+
+  def setup_user
+    @request.session[:user] = users(:one).id
+    users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+  end
+
+  test 'user with viewer rights should fail to edit an operating system' do
+    setup_user
+    get :edit, {:id => Operatingsystem.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  test 'user with viewer rights should succeed in viewing operatingsystems' do
+    setup_user
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/ptables_controller_test.rb b/test/functional/ptables_controller_test.rb
index 62cdccd..1fb0423 100644
--- a/test/functional/ptables_controller_test.rb
+++ b/test/functional/ptables_controller_test.rb
@@ -48,8 +48,63 @@ class PtablesControllerTest < ActionController::TestCase
 
   def test_destroy
     ptable = Ptable.first
-    delete :destroy, {:id => ptable.id}, set_session_user
+    ptable.hosts = []
+    delete :destroy, {:id => ptable}, set_session_user
     assert_redirected_to ptables_url
     assert !Ptable.exists?(ptable.id)
   end
+
+  def setup_view_user
+    @request.session[:user] = users(:one).id
+    users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+  end
+
+  test 'user with viewer rights should fail to edit a partition table' do
+    setup_view_user
+    get :edit, {:id => Ptable.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  test 'user with viewer rights should fail to delete a partition table' do
+    setup_view_user
+    delete :destroy, {:id => Ptable.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  test 'user with viewer rights should fail to create a partition table' do
+    setup_view_user
+    post :create, {:ptable => {:name => "dummy", :layout => "dummy"}}
+    assert @response.status == '403 Forbidden'
+  end
+
+  test 'user with viewer rights should succeed in viewing partition tables' do
+    setup_view_user
+    get :index
+    assert_response :success
+  end
+
+  def setup_edit_user
+    @user = User.find_by_login("one")
+    @user.roles = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer'), Role.find_by_name('Edit partition tables')]
+    @request.session[:user] = @user.id
+  end
+
+
+  test 'user with editing rights should succeed in editing a partition table' do
+    setup_edit_user
+    get :edit, {:id => Ptable.first.id}
+    assert_response :success
+  end
+
+  test 'user with editing rights should succeed in deleting a partition table' do
+    setup_edit_user
+    delete :destroy, {:id => Ptable.first.id}
+    assert flash[:foreman_notice] = "Successfully destroyed partition table."
+  end
+
+  test 'user with editing rights should succeed in creating a partition table' do
+    setup_edit_user
+    post :create, {:ptable => {:name => "dummy", :layout => "dummy"}}
+    assert flash[:foreman_notice] = "Successfully created partition table."
+  end
 end
diff --git a/test/functional/puppetclasses_controller_test.rb b/test/functional/puppetclasses_controller_test.rb
index affa372..0b059ca 100644
--- a/test/functional/puppetclasses_controller_test.rb
+++ b/test/functional/puppetclasses_controller_test.rb
@@ -46,4 +46,21 @@ class PuppetclassesControllerTest < ActionController::TestCase
     assert_redirected_to puppetclasses_url
     assert !Puppetclass.exists?(puppetclass.id)
   end
+
+  def setup_user
+    @request.session[:user] = users(:one).id
+    users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+  end
+
+  test 'user with viewer rights should fail to edit a puppetclass' do
+    setup_user
+    get :edit, {:id => Puppetclass.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  test 'user with viewer rights should succeed in viewing puppetclasses' do
+    setup_user
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/reports_controller_test.rb b/test/functional/reports_controller_test.rb
index b5da489..e50c6ad 100644
--- a/test/functional/reports_controller_test.rb
+++ b/test/functional/reports_controller_test.rb
@@ -1,6 +1,9 @@
 require 'test_helper'
 
 class ReportsControllerTest < ActionController::TestCase
+  setup do
+    User.current = User.find_by_login "admin"
+  end
   def test_index
     get :index, {}, set_session_user
     assert_response :success
@@ -62,4 +65,21 @@ class ReportsControllerTest < ActionController::TestCase
   def create_a_puppet_transaction_report
     @log = File.read(File.expand_path(File.dirname(__FILE__) + "/../fixtures/report-skipped.yaml"))
   end
+
+  def user_setup
+    @request.session[:user] = users(:one).id
+    users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+  end
+
+  test 'user with viewer rights should fail to edit a report' do
+    user_setup
+    get :edit, {:id => Report.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  test 'user with viewer rights should succeed in viewing reports' do
+    user_setup
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/roles_controller_test.rb b/test/functional/roles_controller_test.rb
new file mode 100644
index 0000000..205b033
--- /dev/null
+++ b/test/functional/roles_controller_test.rb
@@ -0,0 +1,125 @@
+# 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.
+
+require File.dirname(__FILE__) + '/../test_helper'
+require 'roles_controller'
+
+# Re-raise errors caught by the controller.
+class RolesController; def rescue_action(e) raise e end; end
+
+class RolesControllerTest < ActionController::TestCase
+  fixtures :roles, :users
+
+  def setup
+    @controller = RolesController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    User.current = nil
+    @request.session[:user] = users(:admin).id # admin
+  end
+
+  def test_get_index
+    get :index
+    assert_response :success
+    assert_template 'index'
+
+    assert_not_nil assigns(:roles)
+    assert_equal Role.find(:all).sort, [assigns(:roles)].flatten.sort
+
+    assert_tag :tag => 'a', :attributes => { :href => '/roles/1/edit' },
+      :content => 'Manager'
+  end
+
+  def test_get_new
+    get :new
+    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', '']
+    }
+
+    assert_response :success
+    assert_template 'new'
+    assert_tag :tag => 'div', :attributes => { :id => 'errorExplanation' }
+  end
+
+  def test_get_edit
+    get :edit, :id => 1
+    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']
+    }
+
+    assert_redirected_to 'roles'
+    role = Role.find(1)
+    assert_equal [:edit_hosts], role.permissions
+  end
+
+  def test_destroy
+    r = Role.new(:name => 'ToBeDestroyed', :permissions => [:view_ptables])
+    assert r.save
+
+    delete :destroy, :id => r
+    assert_redirected_to 'roles'
+    assert_nil Role.find_by_id(r.id)
+  end
+
+  def test_destroy_role_in_use
+    users(:one).roles = [roles(:manager)] # make user one a manager
+    delete :destroy, {:id => roles(:manager)}
+    assert_redirected_to roles_path
+    assert_equal 'Role is in use', flash[:foreman_error]
+    assert_not_nil Role.find_by_id(roles(:manager).id)
+  end
+
+  def test_get_report
+    get :report
+    assert_response :success
+    assert_template 'report'
+
+    assert_not_nil assigns(:roles)
+    assert_equal Role.find(:all).sort, assigns(:roles).sort
+
+  end
+
+  def test_post_report
+    post :report, :permissions => { '0' => '', '1' => ['edit_issues'], '3' => ['add_issues', 'delete_issues']}
+    assert_redirected_to 'roles'
+
+    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' => '' }
+    assert_redirected_to 'roles'
+    assert Role.find(1).permissions.empty?
+  end
+
+  viewable = %w{Architecture Audit AuthSourceLdap Dashboard Domain Environment LookupKey FactValue CommonParameter Hostgroup Host Media Model Operatingsystem Ptable Puppetclass Report Setting Statistic Usergroup User}
+  editable = %w{Architecture Audit AuthSourceLdap           Domain Environment LookupKey           CommonParameter Hostgroup Host Media Model Operatingsystem Ptable Puppetclass Report                   Usergroup User}
+end
diff --git a/test/functional/statistics_controller_test.rb b/test/functional/statistics_controller_test.rb
index a2838cc..b86df1e 100644
--- a/test/functional/statistics_controller_test.rb
+++ b/test/functional/statistics_controller_test.rb
@@ -5,4 +5,11 @@ class StatisticsControllerTest < ActionController::TestCase
   test "the truth" do
     assert true
   end
+
+  test 'user with viewer rights should succeed in viewing statistics' do
+    @request.session[:user] = users(:one).id
+    users(:one).roles = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/usergroups_controller_test.rb b/test/functional/usergroups_controller_test.rb
index b89f172..53311c2 100644
--- a/test/functional/usergroups_controller_test.rb
+++ b/test/functional/usergroups_controller_test.rb
@@ -46,4 +46,21 @@ class UsergroupsControllerTest < ActionController::TestCase
     assert_redirected_to usergroups_url
     assert !Usergroup.exists?(usergroup.id)
   end
+
+  def setup_user
+    @request.session[:user] = users(:one).id
+    users(:one).roles       = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+  end
+
+  test 'user with viewer rights should fail to edit a usergroup' do
+    setup_user
+    get :edit, {:id => Usergroup.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  test 'user with viewer rights should succeed in viewing usergroups' do
+    setup_user
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb
index b47f050..abd3df4 100644
--- a/test/functional/users_controller_test.rb
+++ b/test/functional/users_controller_test.rb
@@ -70,7 +70,7 @@ class UsersControllerTest < ActionController::TestCase
 
   test "should delete different user" do
     user = users(:one)
-    delete :destroy, {:id => user}, {:user => users(:two)}
+    delete :destroy, {:id => user}, {:user => users(:admin)}
     assert_redirected_to users_url
     assert !User.exists?(user.id)
   end
@@ -79,6 +79,7 @@ class UsersControllerTest < ActionController::TestCase
     return unless SETTINGS[:login]
     @request.env['HTTP_REFERER'] = users_path
     user = users(:one)
+    user.update_attribute :admin, true
     delete :destroy, {:id => user.id}, {:user => user.id}
     assert_redirected_to users_url
     assert User.exists?(user)
@@ -94,4 +95,23 @@ class UsersControllerTest < ActionController::TestCase
     assert !User.find_by_login("admin").nil?
   end
 
+  def setup_users
+    User.current = users :admin
+    user = User.find_by_login("one")
+    @request.session[:user] = user.id
+    user.roles = [Role.find_by_name('Anonymous'), Role.find_by_name('Viewer')]
+    user.save!
+  end
+
+  test 'user with viewer rights should fail to edit a user' do
+    setup_users
+    get :edit, {:id => User.first.id}
+    assert @response.status == '403 Forbidden'
+  end
+
+  test 'user with viewer rights should succeed in viewing users' do
+    setup_users
+    get :index
+    assert_response :success
+  end
 end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index ccb3e86..4cb031d 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -46,9 +46,17 @@ class ActiveSupport::TestCase
 
   def set_session_user
     if SETTINGS[:login]
-      {:user => User.first.id}
+      {:user => User.find_by_login("admin")}
     else
       {}
     end
   end
+  
+  def as_admin
+    saved_user   = User.current
+    User.current = users(:admin)
+    result = yield
+    User.current = saved_user
+    result
+  end
 end
diff --git a/test/unit/architecture_test.rb b/test/unit/architecture_test.rb
index b0ddc3d..9dfb53e 100644
--- a/test/unit/architecture_test.rb
+++ b/test/unit/architecture_test.rb
@@ -1,6 +1,9 @@
 require 'test_helper'
 
 class ArchitectureTest < ActiveSupport::TestCase
+  setup do
+    User.current = User.find_by_login("admin")
+  end
   test "should not save without a name" do
     architecture = Architecture.new
     assert !architecture.save
@@ -48,4 +51,60 @@ class ArchitectureTest < ActiveSupport::TestCase
 
     assert !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 = []
+    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 8d83eab..8afd0c0 100644
--- a/test/unit/auth_source_ldap_test.rb
+++ b/test/unit/auth_source_ldap_test.rb
@@ -7,6 +7,7 @@ class AuthSourceLdapTest < ActiveSupport::TestCase
                     :host= => "value",
                     :attr_login= => "value",
                     :port= => 389  }
+    User.current = users(:admin)
   end
 
   test "should exists a name" do
@@ -148,4 +149,57 @@ class AuthSourceLdapTest < ActiveSupport::TestCase
   def assigns_a_string_of_length_greater_than(length, method)
     @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
+    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/common_parameter_test.rb b/test/unit/common_parameter_test.rb
index 6e267f0..cc2367b 100644
--- a/test/unit/common_parameter_test.rb
+++ b/test/unit/common_parameter_test.rb
@@ -1,6 +1,9 @@
 require 'test_helper'
 
 class CommonParameterTest < ActiveSupport::TestCase
+  setup do
+    User.current = User.find_by_login "admin"
+  end
   test "name can't be blank" do
     parameter = CommonParameter.new :name => "  ", :value => "some_value"
     assert parameter.name.strip.empty?
@@ -52,4 +55,58 @@ 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/domain_parameter_test.rb b/test/unit/domain_parameter_test.rb
index 57ab37f..8408cb4 100644
--- a/test/unit/domain_parameter_test.rb
+++ b/test/unit/domain_parameter_test.rb
@@ -22,5 +22,82 @@ class DomainParameterTest < ActiveSupport::TestCase
     parameter2 = DomainParameter.create :name => "some parameter", :value => "value", :reference_id => Domain.last.id
     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 = []
+      @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)]
+    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 = []
+    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 f5e6158..05fac4f 100644
--- a/test/unit/domain_test.rb
+++ b/test/unit/domain_test.rb
@@ -2,6 +2,7 @@ require 'test_helper'
 
 class DomainTest < ActiveSupport::TestCase
   def setup
+    User.current = users(:admin)
     @new_domain = Domain.new
     @domain = Domain.new(:name => "myDomain")
     @domain.save
@@ -39,17 +40,22 @@ class DomainTest < ActiveSupport::TestCase
     assert_equal @domain.name, s
   end
 
-  test "should not destroy if have childrens" do
-    host = create_a_host
+  test "should not destroy if it contains hosts" do
+    host   = create_a_host
 
-    d = host.domain
-    assert !d.destroy
+    domain = host.domain
+    assert !domain.destroy
+    assert_match /is used by/, domain.errors.full_messages.join("\n")
+  end
 
-    subnet = Subnet.create  :number => "123.123.123.1", :mask => "321.321.321.1",
-                             :domain => @domain
+  test "should not destroy if it contains subnets" do
 
-    d = subnet.domain
-    assert !d.destroy
+    as_admin do
+      Subnet.create!  :number => "123.123.123.1", :mask => "321.321.321.1",
+                      :domain => @domain
+    end
+    assert !@domain.destroy
+    assert_match /is used by/, @domain.errors.full_messages.join("\n")
   end
 
 #I must find out how to create a fact_name inside of fact_value
@@ -60,12 +66,89 @@ class DomainTest < ActiveSupport::TestCase
 #  end
 
   def create_a_host
-    Host.create :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
-                 :domain => @domain,
-                 :operatingsystem => Operatingsystem.first,
-                 :architecture => Architecture.find_or_create_by_name("i386"),
-                 :environment => Environment.find_or_create_by_name("envy"),
-                 :disk => "empty partition"
+    as_admin do
+      Host.create :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
+                   :domain => @domain,
+                   :operatingsystem => Operatingsystem.first,
+                   :architecture => Architecture.find_or_create_by_name("i386"),
+                   :environment => Environment.find_or_create_by_name("envy"),
+                   :disk => "empty partition"
+    end
+  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 = []
+      @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 =  Domain.first
+    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
+    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
+
 end
 
diff --git a/test/unit/fact_value_test.rb b/test/unit/fact_value_test.rb
index 9e35368..d4be6fd 100644
--- a/test/unit/fact_value_test.rb
+++ b/test/unit/fact_value_test.rb
@@ -2,15 +2,16 @@ require 'test_helper'
 
 class FactValueTest < ActiveSupport::TestCase
   def setup
-    host = Host.create :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
-                        :domain => Domain.find_or_create_by_name("company.com"),
-                        :operatingsystem => Operatingsystem.first,
-                        :architecture => Architecture.find_or_create_by_name("i386"),
-                        :environment => Environment.find_or_create_by_name("envy"),
-                        :disk => "empty partition"
-
-    @fact_name = Puppet::Rails::FactName.create(:name => "my_facting_name")
-    @fact_value = FactValue.create(:value => "some value", :host => host, :fact_name => @fact_name)
+    as_admin do
+      @host = Host.create :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
+                          :domain => Domain.find_or_create_by_name("company.com"),
+                          :operatingsystem => Operatingsystem.first,
+                          :architecture => Architecture.find_or_create_by_name("i386"),
+                          :environment => Environment.find_or_create_by_name("envy"),
+                          :disk => "empty partition"
+    end
+    @fact_name  = Puppet::Rails::FactName.create(:name => "my_facting_name")
+    @fact_value = FactValue.create(:value => "some value", :host => @host, :fact_name => @fact_name)
   end
 
 #  test "should return the memory average" do
@@ -22,13 +23,15 @@ class FactValueTest < ActiveSupport::TestCase
     assert_equal h, FactValue.count_each("my_facting_name")
 
     #Now creating a new fact value
-    other_host = Host.create :name => "myfullhost2", :mac => "aabbccddeefa", :ip => "123.05.02.04",
-                              :domain => Domain.find_or_create_by_name("company.com"),
-                              :operatingsystem => Operatingsystem.first,
-                              :architecture => Architecture.find_or_create_by_name("i386"),
-                              :environment => Environment.find_or_create_by_name("envy"),
-                              :disk => "empty partition"
-    other_fact_value = FactValue.create(:value => "some value", :host => other_host, :fact_name => @fact_name)
+    as_admin do
+      @other_host = Host.create :name => "myfullhost2", :mac => "aabbccddeefa", :ip => "123.05.02.04",
+                                :domain => Domain.find_or_create_by_name("company.com"),
+                                :operatingsystem => Operatingsystem.first,
+                                :architecture => Architecture.find_or_create_by_name("i386"),
+                                :environment => Environment.find_or_create_by_name("envy"),
+                                :disk => "empty partition"
+    end
+    other_fact_value = FactValue.create(:value => "some value", :host => @other_host, :fact_name => @fact_name)
     h["Some value"] = 2
     assert_equal h, FactValue.count_each("my_facting_name")
   end
diff --git a/test/unit/group_parameter_test.rb b/test/unit/group_parameter_test.rb
index 32b8161..79131d2 100644
--- a/test/unit/group_parameter_test.rb
+++ b/test/unit/group_parameter_test.rb
@@ -1,6 +1,9 @@
 require 'test_helper'
 
 class GroupParameterTest < ActiveSupport::TestCase
+  setup do
+    User.current = User.find_by_login "admin"
+  end
   test "should have a reference_id" do
     group_parameter = GroupParameter.new
     assert !group_parameter.save
@@ -24,5 +27,82 @@ class GroupParameterTest < ActiveSupport::TestCase
     parameter2 = GroupParameter.create :name => "some parameter", :value => "value", :reference_id => Hostgroup.last.id
     assert parameter2.valid?
   end
+
+  def setup_user operation
+    @one = users(:one)
+    as_admin do
+      role = Role.find_or_create_by_name :name => "#{operation}_hostgroups"
+      role.permissions = ["#{operation}_hostgroups".to_sym]
+      @one.roles = [role]
+      @one.hostgroups = []
+      @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.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"
+    as_admin do
+      @one.hostgroups = []
+    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"
+    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"
+    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/host_mailer_test.rb b/test/unit/host_mailer_test.rb
index e08668e..eabc52a 100644
--- a/test/unit/host_mailer_test.rb
+++ b/test/unit/host_mailer_test.rb
@@ -2,18 +2,20 @@ require 'test_helper'
 
 class HostMailerTest < ActionMailer::TestCase
   def setup
-    @env = Environment.new :name => "testing"
-    @env.save!
-
-    @host = Host.new :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
-      :domain => Domain.find_or_create_by_name("company.com"), :operatingsystem => Operatingsystem.first,
-      :architecture => Architecture.first, :environment => @env, :disk => "empty partition",
-      :ptable => Ptable.first, :last_report => Time.at(0)
-    @host.save!
-
-    @env.hosts << @host
-    @env.save!
-
+    as_admin do
+      @env = Environment.new :name => "testing"
+      @env.save!
+  
+      @host = Host.new :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
+        :domain => Domain.find_or_create_by_name("company.com"), :operatingsystem => Operatingsystem.first,
+        :architecture => Architecture.first, :environment => @env, :disk => "empty partition",
+        :ptable => Ptable.first, :last_report => Time.at(0)
+      @host.save!
+  
+      @env.hosts << @host
+      @env.save!
+    end
+    User.current = User.find_by_login "admin"
     SETTINGS[:foreman_url] = "http://localhost:3000/hosts/:id"
 
     @options = {}
diff --git a/test/unit/host_parameter_test.rb b/test/unit/host_parameter_test.rb
index 5adcb36..9b695a8 100644
--- a/test/unit/host_parameter_test.rb
+++ b/test/unit/host_parameter_test.rb
@@ -1,6 +1,9 @@
 require 'test_helper'
 
 class HostParameterTest < ActiveSupport::TestCase
+  setup do
+    User.current = User.find_by_login "admin"
+  end
   test "should have a reference_id" do
     host_parameter = HostParameter.new
     host_parameter.name = "valid"
@@ -13,28 +16,111 @@ class HostParameterTest < ActiveSupport::TestCase
   end
 
   test "duplicate names cannot exist for a host" do
-    host = Host.create :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
-    :domain => domains(:mydomain) , :operatingsystem => Operatingsystem.first, :hostgroup => hostgroups(:common),
-    :architecture => Architecture.first, :environment => Environment.first, :disk => "empty partition"
+    as_admin do
+      @host = Host.create! :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
+                          :domain => domains(:mydomain) , :operatingsystem => Operatingsystem.first, :hostgroup => hostgroups(:common),
+                          :architecture => Architecture.first, :environment => Environment.first, :disk => "empty partition"
 
-    parameter1 = HostParameter.create :name => "some parameter", :value => "value", :reference_id => host.id
-    parameter2 = HostParameter.create :name => "some parameter", :value => "value", :reference_id => host.id
-    assert !parameter2.valid?
-    assert  parameter2.errors.full_messages[0] == "Name has already been taken"
+      @parameter1 = HostParameter.create :name => "some parameter", :value => "value", :reference_id => @host.id
+      @parameter2 = HostParameter.create :name => "some parameter", :value => "value", :reference_id => @host.id
+    end
+    assert !@parameter2.valid?
+    assert  @parameter2.errors.full_messages[0] == "Name has already been taken"
   end
 
   test "duplicate names can exist for different hosts" do
-    host1 = Host.create :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
-    :domain => domains(:mydomain) , :operatingsystem => Operatingsystem.first, :hostgroup => hostgroups(:common),
-    :architecture => Architecture.first, :environment => Environment.first, :disk => "empty partition"
-    host2 = Host.create :name => "anotherfullhost", :mac => "aabbecddee00", :ip => "123.05.02.04",
-    :domain => domains(:mydomain) , :operatingsystem => Operatingsystem.first, :hostgroup => hostgroups(:common),
-    :architecture => Architecture.first, :environment => Environment.first, :disk => "empty partition"
+    as_admin do
+      @host1 = Host.create! :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
+                            :domain => domains(:mydomain) , :operatingsystem => Operatingsystem.first, :hostgroup => hostgroups(:common),
+                            :architecture => Architecture.first, :environment => Environment.first, :disk => "empty partition"
+      @host2 = Host.create! :name => "anotherfullhost", :mac => "aabbecddee00", :ip => "123.05.02.04",
+                            :domain => domains(:mydomain) , :operatingsystem => Operatingsystem.first, :hostgroup => hostgroups(:common),
+                            :architecture => Architecture.first, :environment => Environment.first, :disk => "empty partition"
+      @parameter1 = HostParameter.create! :name => "some parameter", :value => "value", :reference_id => @host1.id
+      @parameter2 = HostParameter.create! :name => "some parameter", :value => "value", :reference_id => @host2.id
+    end
+    assert @parameter2.valid?
+  end
 
-    parameter1 = HostParameter.create :name => "some parameter", :value => "value", :reference_id => host1.id
-    parameter2 = HostParameter.create :name => "some parameter", :value => "value", :reference_id => host2.id
-    assert parameter2.valid?
+  def setup_user operation
+    @one = users(:one)
+    as_admin do
+      role = Role.find_or_create_by_name :name => "#{operation}_hosts"
+      role.permissions = ["#{operation}_hosts".to_sym]
+      @one.roles      = [role]
+      @one.domains    = []
+      @one.hostgroups = []
+      @one.user_facts = []
+      @one.save!
+    end
+    User.current = @one
   end
 
-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
+    record =  HostParameter.create :name => "dummy", :value => "value", :reference_id => hosts(:one).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)]
+      @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.new_record?
+  end
+
+  test "user with create permissions should be able to create when unconstrained" do
+    setup_user "create"
+    as_admin do
+      @one.domains = []
+    end
+    record =  HostParameter.create :name => "dummy", :value => "value", :reference_id => hosts(:one).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 =  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 0690d45..ee45d47 100644
--- a/test/unit/host_test.rb
+++ b/test/unit/host_test.rb
@@ -1,6 +1,10 @@
 require 'test_helper'
 
 class HostTest < ActiveSupport::TestCase
+  setup do
+    User.current = User.find_by_login "admin"
+  end
+
   test "should not save without a hostname" do
     host = Host.new
     assert !host.save
@@ -39,26 +43,31 @@ class HostTest < ActiveSupport::TestCase
     host = Host.create :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
       :domain => Domain.find_or_create_by_name("company.com"), :operatingsystem => Operatingsystem.first,
       :architecture => Architecture.first, :environment => Environment.first, :disk => "empty partition"
-    puts host.errors.full_messages
+    if host.new_record?
+      puts host.errors.full_messages
+    end
     assert host.valid?
+    assert !host.new_record?
   end
 
   test "should import facts from yaml stream" do
     h=Host.new(:name => "sinn1636.lan")
     h.disk = "!" # workaround for now
-    assert h.importFacts(YAML::load(File.read(File.expand_path(File.dirname(__FILE__) + "/facts.yml"))))
+    assert true == h.importFacts(YAML::load(File.read(File.expand_path(File.dirname(__FILE__) + "/facts.yml"))))
   end
 
   test "should import facts from yaml of a new host" do
     assert Host.importHostAndFacts(File.read(File.expand_path(File.dirname(__FILE__) + "/facts.yml")))
   end
-  if SETTINGS[:unattended].nil? or SETTINGS[:unattended]
-    test "should not save if both ptable and disk are not defined" do
+
+  test "should not save if both ptable and disk are not defined" do
+    if SETTINGS[:unattended].nil? or SETTINGS[:unattended] == "true"
       host = Host.create :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
         :domain => Domain.find_or_create_by_name("company.com"), :operatingsystem => Operatingsystem.first,
         :architecture => Architecture.first, :environment => Environment.first
-      assert true unless SETTINGS[:attended]
-      assert !host.valid?
+      assert host.new_record?
+    else
+      true
     end
   end
 
@@ -66,14 +75,14 @@ class HostTest < ActiveSupport::TestCase
     host = Host.create :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
       :domain => Domain.find_or_create_by_name("company.com"), :operatingsystem => Operatingsystem.first,
       :architecture => Architecture.first, :environment => Environment.first, :ptable => Ptable.first
-    assert host.valid?
+    assert !host.new_record?
   end
 
   test "should save if disk is defined" do
     host = Host.create :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
       :domain => Domain.find_or_create_by_name("company.com"), :operatingsystem => Operatingsystem.first,
       :architecture => Architecture.first, :environment => Environment.first, :disk => "aaa"
-    assert host.valid?
+    assert !host.new_record?
   end
 
   test "should import from external nodes output" do
@@ -103,4 +112,148 @@ class HostTest < ActiveSupport::TestCase
     assert host.disabled?
   end
 
+  def setup_user_and_host
+    @one            = users(:one)
+    @one.hostgroups = []
+    @one.domains    = []
+    @one.user_facts = []
+    @one.save!
+    @host           = hosts(:one)
+    @host.owner     = users(:two)
+    @host.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(:name => "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 :name => "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 :name => "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(:name => "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 => "123.05.02.09",
+                       :domain => domains(:mydomain),  :operatingsystem => operatingsystems(:centos5_3),
+                       :architecture => architectures(:x86_64), :environment => environments(:production),
+                       :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 => "123.05.02.09",
+                       :domain => domains(:mydomain),  :operatingsystem => operatingsystems(:centos5_3),
+                       :architecture => architectures(:x86_64), :environment => environments(:production),
+                       :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 => "123.05.02.09",
+                       :domain => domains(:mydomain),  :operatingsystem => operatingsystems(:centos5_3),
+                       :architecture => architectures(:x86_64), :environment => environments(:production),
+                       :disk => "empty partition", :hostgroup => Hostgroup.find_by_name("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 => "123.05.02.09",
+                       :domain => domains(:mydomain),  :operatingsystem => operatingsystems(:centos5_3),
+                       :architecture => architectures(:x86_64), :environment => environments(:production),
+                       :disk => "empty partition", :hostgroup => Hostgroup.find_by_name("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")]
+    end
+    assert @host.destroy
+    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)
+    end
+    assert @host.destroy
+    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
+
 end
diff --git a/test/unit/hostgroup_test.rb b/test/unit/hostgroup_test.rb
index 8659867..ca90921 100644
--- a/test/unit/hostgroup_test.rb
+++ b/test/unit/hostgroup_test.rb
@@ -1,26 +1,90 @@
 require 'test_helper'
 
 class HostgroupTest < ActiveSupport::TestCase
+  setup do
+    User.current = User.find_by_login "admin"
+  end
   test "name can't be blank" do
     host_group = Hostgroup.new :name => "  "
     assert host_group.name.strip.empty?
     assert !host_group.save
   end
-
+  
   test "name can't contain trailing white spaces" do
     host_group = Hostgroup.new :name => " all    hosts in the     world    "
     assert !host_group.name.strip.squeeze(" ").empty?
     assert !host_group.save
-
+    
     host_group.name.strip!.squeeze!(" ")
     assert host_group.save
   end
-
+  
   test "name must be unique" do
     host_group = Hostgroup.new :name => "some hosts"
     assert host_group.save
-
+    
     other_host_group = Hostgroup.new :name => "some hosts"
     assert !other_host_group.save
   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 =  Hostgroup.first
+    as_admin do
+      record.hosts = []
+    end
+    assert record.destroy
+    assert record.frozen?
+  end
+  
+  test "user with edit permissions should not be able to destroy" do
+    setup_user "edit"
+    record =  Hostgroup.first
+    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 = []
+    end
+    assert !record.save
+    assert record.valid?
+  end
+  
 end
diff --git a/test/unit/media_test.rb b/test/unit/media_test.rb
index 89aebf3..2622dd8 100644
--- a/test/unit/media_test.rb
+++ b/test/unit/media_test.rb
@@ -1,6 +1,10 @@
 require 'test_helper'
 
 class MediaTest < ActiveSupport::TestCase
+  setup do
+    User.current = User.find_by_login "admin"
+  end
+
   test "name can't be blank" do
     media = Media.new :name => "   ", :path => "http://www.google.com"
     assert media.name.strip.empty?
@@ -52,4 +56,65 @@ class MediaTest < ActiveSupport::TestCase
 
     assert !media.destroy
   end
+
+  def setup_user operation
+    @one = users(:one)
+    as_admin do
+      role = Role.find_or_create_by_name :name => "#{operation}_medias"
+      role.permissions = ["#{operation}_medias".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 =  Media.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 =  Media.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 =  Media.first
+    as_admin do
+      record.hosts = []
+    end
+    assert record.destroy
+    assert record.frozen?
+  end
+  
+  test "user with edit permissions should not be able to destroy" do
+    setup_user "edit"
+    record =  Media.first
+    assert !record.destroy
+    assert !record.frozen?
+  end
+  
+  test "user with edit permissions should be able to edit" do
+    setup_user "edit"
+    record      =  Media.first
+    record.name = "renamed"
+    assert record.save
+  end
+  
+  test "user with destroy permissions should not be able to edit" do
+    setup_user "destroy"
+    record      =  Media.first
+    record.name = "renamed"
+    as_admin do
+      record.hosts = []
+    end
+    assert !record.save
+    assert record.valid?
+  end
+
 end
diff --git a/test/unit/model_test.rb b/test/unit/model_test.rb
index a22279a..2331683 100644
--- a/test/unit/model_test.rb
+++ b/test/unit/model_test.rb
@@ -1,6 +1,10 @@
 require 'test_helper'
 
 class ModelTest < ActiveSupport::TestCase
+  setup do
+    User.current = User.find_by_login "admin"
+  end
+
   test "should have name" do
     m = Model.new
     assert !m.save
@@ -22,4 +26,65 @@ 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 = []
+    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 = []
+    end
+    assert !record.save
+    assert record.valid?
+  end
+
 end
diff --git a/test/unit/operatingsystem_test.rb b/test/unit/operatingsystem_test.rb
index b09450f..d486f29 100644
--- a/test/unit/operatingsystem_test.rb
+++ b/test/unit/operatingsystem_test.rb
@@ -1,6 +1,9 @@
 require 'test_helper'
 
 class OperatingsystemTest < ActiveSupport::TestCase
+  setup do
+    User.current = User.find_by_login "admin"
+  end
   test "shouldn't save with blank attributes" do
     operating_system = Operatingsystem.new
     assert !operating_system.save
@@ -76,4 +79,65 @@ class OperatingsystemTest < ActiveSupport::TestCase
     operating_system = Operatingsystem.new :name => "Ubuntu", :major => "9", :minor => "10"
     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 = []
+    end
+    assert record.destroy
+    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 = []
+    end
+    assert !record.save
+    assert record.valid?
+  end
+
 end
diff --git a/test/unit/parameter_test.rb b/test/unit/parameter_test.rb
index 0985773..89c0b92 100644
--- a/test/unit/parameter_test.rb
+++ b/test/unit/parameter_test.rb
@@ -1,4 +1,7 @@
 class ParameterTest < ActiveSupport::TestCase
+  setup do
+    User.current = User.find_by_login "admin"
+  end
   test  "names may me reused in different parameter groups" do
     p1 = HostParameter.new   :name => "param", :value => "value1", :reference_id => Host.first
     assert p1.save
diff --git a/test/unit/ptable_test.rb b/test/unit/ptable_test.rb
index 1de4867..e5c1b07 100644
--- a/test/unit/ptable_test.rb
+++ b/test/unit/ptable_test.rb
@@ -1,6 +1,10 @@
 require 'test_helper'
 
 class PtableTest < ActiveSupport::TestCase
+  setup do
+    User.current = User.find_by_login "admin"
+  end
+
   test "name can't be blank" do
     partition_table = Ptable.new :name => "   ", :layout => "any layout"
     assert partition_table.name.strip.empty?
@@ -62,4 +66,65 @@ 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 = []
+    end
+    assert record.destroy
+    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 = []
+    end
+    assert !record.save
+    assert record.valid?
+  end
+
 end
diff --git a/test/unit/puppetclass_test.rb b/test/unit/puppetclass_test.rb
index 304452e..0330e4f 100644
--- a/test/unit/puppetclass_test.rb
+++ b/test/unit/puppetclass_test.rb
@@ -1,6 +1,10 @@
 require 'test_helper'
 
 class PuppetclassTest < ActiveSupport::TestCase
+  setup do
+    User.current = User.find_by_login "admin"
+  end
+
   test "name can't be blank" do
     puppet_class = Puppetclass.new
     assert !puppet_class.save
@@ -36,4 +40,65 @@ class PuppetclassTest < ActiveSupport::TestCase
     assert klasses[1] == "other_puppet_class"
     assert klasses[2] == "yet_another_puppet_class"
   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 = []
+    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 = []
+    end
+    assert !record.save
+    assert record.valid?
+  end
+
 end
diff --git a/test/unit/report_observer_test.rb b/test/unit/report_observer_test.rb
index 5b939d9..358e8f3 100644
--- a/test/unit/report_observer_test.rb
+++ b/test/unit/report_observer_test.rb
@@ -2,6 +2,8 @@ require 'test_helper'
 
 class ReportObserverTest < ActiveSupport::TestCase
   def setup
+    User.current = User.find_by_login "admin"
+
     # Email recepient
     SETTINGS[:administrator] = "admin@example.com"
 
diff --git a/test/unit/report_test.rb b/test/unit/report_test.rb
index 909cec1..e6dee80 100644
--- a/test/unit/report_test.rb
+++ b/test/unit/report_test.rb
@@ -2,6 +2,7 @@ require 'test_helper'
 
 class ReportTest < ActiveSupport::TestCase
   def setup
+    User.current = User.find_by_login "admin"
     @r=Report.import File.read(File.expand_path(File.dirname(__FILE__) + "/../fixtures/report-skipped.yaml"))
   end
 
@@ -71,4 +72,47 @@ class ReportTest < ActiveSupport::TestCase
     @r.save
     assert Report.with("skipped").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
new file mode 100644
index 0000000..ab5b56d
--- /dev/null
+++ b/test/unit/role_test.rb
@@ -0,0 +1,104 @@
+# redMine - project management software
+# Copyright (C) 2006-2008  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.
+
+require File.dirname(__FILE__) + '/../test_helper'
+
+class RoleTest < ActiveSupport::TestCase
+  fixtures :roles
+
+    should have_many :user_roles
+    should have_many(:users).through(:user_roles)
+
+    should validate_presence_of :name
+    should validate_uniqueness_of :name
+    should ensure_length_of(:name).is_at_most(30)
+    should allow_value("a role name").for(:name)
+    should_not allow_value(";a role name").for(:name)
+    should_not allow_value(" a role name").for(:name)
+    should_not allow_value("a role name ").for(:name)
+
+  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
+      assert role.builtin?
+      assert_equal Role::BUILTIN_ANONYMOUS, role.builtin
+    end
+
+    context "with a missing anonymous role" do
+      setup do
+        Role.delete_all("builtin = #{Role::BUILTIN_ANONYMOUS}")
+      end
+
+      should "create a new anonymous role" do
+        assert_difference('Role.count') do
+          Role.anonymous
+        end
+      end
+
+      should "return the anonymous role" do
+        role = Role.anonymous
+        assert role.builtin?
+        assert_equal Role::BUILTIN_ANONYMOUS, role.builtin
+      end
+    end
+  end
+
+  context "Default_user" do
+    should "return the default_user role" do
+      role = Role.default_user
+      assert role.builtin?
+      assert_equal Role::BUILTIN_DEFAULT_USER, role.builtin
+    end
+
+    context "with a missing default_user role" do
+      setup do
+        Role.delete_all("builtin = #{Role::BUILTIN_DEFAULT_USER}")
+      end
+
+      should "create a new default_user role" do
+        assert_difference('Role.count') do
+          Role.default_user
+        end
+      end
+
+      should "return the default_user role" do
+        role = Role.default_user
+        assert role.builtin?
+        assert_equal Role::BUILTIN_DEFAULT_USER, role.builtin
+      end
+    end
+  end
+end
diff --git a/test/unit/subnet_test.rb b/test/unit/subnet_test.rb
index 53cf287..0d5fb0c 100644
--- a/test/unit/subnet_test.rb
+++ b/test/unit/subnet_test.rb
@@ -2,6 +2,7 @@ require 'test_helper'
 
 class SubnetTest < ActiveSupport::TestCase
   def setup
+    User.current = User.find_by_login "admin"
     @subnet = Subnet.new
     @attrs = {  :number= => "123.123.123.1",
                 :mask= => "321.321.321.3",
@@ -97,5 +98,66 @@ class SubnetTest < ActiveSupport::TestCase
     @subnet.domain_id = @domain.id
     @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 => "dummy", :number => "1.2.3.4", :mask => "255.255.255.0"
+    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.create :name => "dummy", :number => "1.2.3.4", :mask => "255.255.255.0"
+    assert record.valid?
+    assert record.new_record?
+  end
+  
+  test "user with destroy permissions should be able to destroy" do
+    setup_user "destroy"
+    record =  Subnet.first
+    as_admin do
+      record.domain = nil
+    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.domain = nil
+    end
+    assert !record.save
+    assert record.valid?
+  end
+
 end
 
diff --git a/test/unit/user_fact_test.rb b/test/unit/user_fact_test.rb
new file mode 100644
index 0000000..4dfb122
--- /dev/null
+++ b/test/unit/user_fact_test.rb
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class UserFactTest < ActiveSupport::TestCase
+  # Replace this with your real tests.
+  test "the truth" do
+    assert true
+  end
+end
diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb
index 919f42e..55d2bd2 100644
--- a/test/unit/user_test.rb
+++ b/test/unit/user_test.rb
@@ -2,6 +2,7 @@ require 'test_helper'
 
 class UserTest < ActiveSupport::TestCase
   def setup
+    User.current = User.find_by_login "admin"
     @user = User.create :auth_source => auth_sources(:one), :login => "foo", :mail  => "foo@bar.com"
   end
 
@@ -86,5 +87,64 @@ class UserTest < ActiveSupport::TestCase
   test  "should not be able to delete the admin account" do
     assert !User.find_by_login("admin").destroy
   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
+  end
+  
+  test "user with create permissions should be able to create" do
+    setup_user "create"
+    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 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      =  User.first
+    record.login = "renamed"
+    assert record.save
+  end
+  
+  test "user with destroy permissions should not be able to edit" do
+    setup_user "destroy"
+    record      =  User.first
+    record.login = "renamed"
+    assert !record.save
+    assert record.valid?
+  end
+
 end
 
diff --git a/test/unit/usergroup_test.rb b/test/unit/usergroup_test.rb
index e611afe..f7ad5a0 100644
--- a/test/unit/usergroup_test.rb
+++ b/test/unit/usergroup_test.rb
@@ -1,7 +1,9 @@
 require 'test_helper'
 
 class UsergroupTest < ActiveSupport::TestCase
-
+  setup do
+    User.current = User.find_by_login("admin")
+  end
   test "usergroups should be creatable" do
     assert Usergroup.create(:name => "name").valid?
   end
@@ -98,4 +100,59 @@ class UsergroupTest < ActiveSupport::TestCase
       ug1.destroy
     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 "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
+  
+  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
+  
+  test "user with destroy permissions should be able to destroy" do
+    setup_user "destroy"
+    record =  Usergroup.first
+    assert record.destroy
+    assert record.frozen?
+  end
+  
+  test "user with edit permissions should not be able to destroy" do
+    setup_user "edit"
+    record =  Usergroup.first
+    assert !record.destroy
+    assert !record.frozen?
+  end
+  
+  test "user with edit permissions should be able to edit" do
+    setup_user "edit"
+    record      =  Usergroup.first
+    record.name = "renamed"
+    assert record.save
+  end
+  
+  test "user with destroy permissions should not be able to edit" do
+    setup_user "destroy"
+    record      =  Usergroup.first
+    record.name = "renamed"
+    assert !record.save
+    assert record.valid?
+  end
+
 end
diff --git a/vendor/plugins/proxy/lib/proxy.rb b/vendor/plugins/proxy/lib/proxy.rb
index 0d4f2fe..ec79b37 100644
--- a/vendor/plugins/proxy/lib/proxy.rb
+++ b/vendor/plugins/proxy/lib/proxy.rb
@@ -97,7 +97,7 @@ module GW
         return false
       end
       begin
-        command = "/usr/bin/sudo -S #{@sbin}/puppetca --clean #{fqdn}< /dev/null > /dev/null"
+        command = "/usr/bin/sudo -S #{@sbin}/puppetca --clean #{fqdn} < /dev/null > /dev/null"
         logger.info system(command)
         return true
       rescue StandardError => e
