aeolus security - round 2 (rev 2)
by Mo Morsi
Redmine #3288, #3404, #3405
This is the lastest revision to the latest round of security updates,
incorporating all feedback so far. The spec and feature suites still work
after these patches are applied and everything should be good to go for
approval
11 years, 10 months
Need updated Audrey Agent rpm for F16
by Justin Clift
Hi Joe,
Would you be ok to recompile the latest 0.4.x series
Audrey Agent rpm, to F16, and push it out to the official
repos? (ie through Bodhi)
The existing Audrey Agent rpm is busted, missing the
systemd startup bits:
aeolus-audrey-agent-0.4.2-15.fc16.noarch.rpm
As a rough suggestion, the "aeolus-audrey-agent-0.4.10-1"
from RHEL6 seems happy when recompiled for F16.
Haven't been able to test it fully yet, as my VMware setup
is still having issues with passing userdata... but at least
the above rpm is starting ok, and trying to grab user data
(several times according to the log file).
Regards and best wishes,
Justin Clift
--
Aeolus Community Manager
http://www.aeolusproject.org
11 years, 10 months
[PATCH conductor 0/10] RM3392, 3394, converge-ui, login layout
by Jirka Tomasek
RM3392, RM3394 This patchset updates the converge-ui used by Conductor
to latest commit, brings new stylesheets, images, javascript libs from
it. Patchset also makes Conductor to use converge-ui Login layout.
git-send-email won't allow me to send the patchset so I added the
patches as attachment.
Jirka
11 years, 10 months
Re: UI irritations wiki page
by Jirka Tomasek
On 06/22/2012 09:04 PM, Matt Wagner wrote:
> Hi,
>
> As discussed on the call today, I put together a wiki page outlining my
> "annoyances" with our UI -- places we are inconsistent or do weird
> things.
>
> https://www.aeolusproject.org/redmine/projects/aeolus/wiki/List_of_UI_Ann...
>
> I seeded it with my own list, but the intention is to gather data from
> everyone.
>
> Please feel free to contribute your own, or share the link as you deem
> appropriate. (This should probably go out to aeolus-devel, but I wasn't
> certain if that's a good idea or not, so I'm holding off for the moment.
> Please feel free to send there if you think we should.)
>
> -- Matt
Hi,
I have added my findings to the wiki and sending it here too. I
especially agree on Monitor/Administer distinction issue.
I will try to go deeper in the app and update list with another issues.
I think we should share this on aeolus-devel so I add it to the
recipients. Hope nobody is against it.
Here is the list (also available at
https://www.aeolusproject.org/redmine/projects/aeolus/wiki/List_of_UI_Ann...):
* Logs UI needs polishing and figure out proper Logs link placement
(original designs puts the link (icon) between pretty/filter togle
buttons [1]), resolve places where logs sections should be also present
(eg. related only to pool, deployment, instance, user, provider...) see
logs section design: [1]
* Launch from Catalog (dropdown) is not clear what it does (should be
something like Launch NEW DEPLOYMENT from CHOOSE CATALOG)
* Settings tab is kind of empty (is needed?) the first page just points
to another with link
* As Matt pointed out let's make the administer tabs first level and add
Overview tab at the beginning.
* Based on changed top tabs, let's make a dropdown shortcuts for the
folowing tabs if the page under the tab is tabbed (Users, Environments,
Content, providers (make a new provider link) [2], this also brings some
space to utilize changes for better workflows which Brian works on.
Display tabs and subtabs based on permissions of user.
* Breadcrumbs - enable them everywhere, original point - maintain
applied filters (viewstate), use it to help with the navigation (plays
nice with dropdown tabs), we need to figure out proper placement and design
* Notifications, the current placement of notifications is wrong and
doesn't fit the current Header style. UXD Wiki says nothing about
notifications look and placement either [3]
* The Overpass font does not align vertically to it's line height.
[1]
https://github.com/aeolusproject/aeolus-ux-designs/blob/master/admin_ux_f...
[2]
http://file.bne.redhat.com/~afitzsim/headers/SE-header-CSS-HTML/katello-h...
[3] http://design.lab.bos.redhat.com/wiki/Main_Page
11 years, 10 months
[PATCH conductor] Tasks 3387/3388 UI for user groups implementation (revised)
by Scott Seago
UI for managing local user groups and for assigning group permissions.
When testing, note that after adding/removing a user from a group
the user must log out and in again to see the effects.
---
aeolus-conductor.spec.in | 1 +
src/app/controllers/application_controller.rb | 7 +-
src/app/controllers/permissions_controller.rb | 12 +-
src/app/controllers/user_groups_controller.rb | 238 ++++++++++++++++++++
.../user_groups_helper.rb} | 23 +--
src/app/models/common_filter_methods.rb | 8 +-
src/app/models/derived_permission.rb | 23 ++
src/app/models/entity.rb | 19 ++
src/app/models/permission.rb | 23 ++
src/app/models/user_group.rb | 12 +
src/app/views/permissions/_form.html.haml | 23 ++-
src/app/views/permissions/_permissions.html.haml | 20 ++
src/app/views/user_groups/_form.html.haml | 26 +++
src/app/views/user_groups/_list.html.haml | 41 ++++
src/app/views/user_groups/add_members.html.haml | 25 ++
src/app/views/user_groups/edit.html.haml | 14 ++
src/app/views/user_groups/index.html.haml | 2 +
src/app/views/user_groups/new.html.haml | 13 +
src/app/views/user_groups/show.html.haml | 71 ++++++
src/config/locales/en.yml | 63 +++++-
src/config/navigation.rb | 2 +-
src/config/routes.rb | 15 ++
src/features/step_definitions/user_group_steps.rb | 56 +++++
src/features/support/paths.rb | 6 +
src/features/user_group.feature | 72 ++++++
.../controllers/user_groups_controller_spec.rb | 75 ++++++
src/spec/factories/permission.rb | 7 +
.../factories/user_group.rb} | 23 +-
src/spec/models/permission_spec.rb | 19 ++
src/spec/models/user_group_spec.rb | 43 ++++
30 files changed, 947 insertions(+), 35 deletions(-)
create mode 100644 src/app/controllers/user_groups_controller.rb
copy src/app/{models/common_filter_methods.rb => helpers/user_groups_helper.rb} (55%)
create mode 100644 src/app/views/user_groups/_form.html.haml
create mode 100644 src/app/views/user_groups/_list.html.haml
create mode 100644 src/app/views/user_groups/add_members.html.haml
create mode 100644 src/app/views/user_groups/edit.html.haml
create mode 100644 src/app/views/user_groups/index.html.haml
create mode 100644 src/app/views/user_groups/new.html.haml
create mode 100644 src/app/views/user_groups/show.html.haml
create mode 100644 src/features/step_definitions/user_group_steps.rb
create mode 100644 src/features/user_group.feature
create mode 100644 src/spec/controllers/user_groups_controller_spec.rb
copy src/{app/models/common_filter_methods.rb => spec/factories/user_group.rb} (56%)
create mode 100644 src/spec/models/user_group_spec.rb
diff --git a/aeolus-conductor.spec.in b/aeolus-conductor.spec.in
index 6186651..30e6f43 100644
--- a/aeolus-conductor.spec.in
+++ b/aeolus-conductor.spec.in
@@ -209,6 +209,7 @@ haml="app/views/hardware_profiles app/views/realm_mappings \
app/views/api/hooks \
app/views/api \
app/views/images \
+ app/views/user_groups \
app/views/api/builds app/views/api/provider_images \
app/views/api/target_images app/views/api/entrypoint \
app/views/api/environments \
diff --git a/src/app/controllers/application_controller.rb b/src/app/controllers/application_controller.rb
index eb5931b..b30326b 100644
--- a/src/app/controllers/application_controller.rb
+++ b/src/app/controllers/application_controller.rb
@@ -281,7 +281,7 @@ class ApplicationController < ActionController::Base
def set_admin_users_tabs(tab)
@tabs = [{:name => t('application_controller.admin_tabs.users'), :url => users_url, :id => 'users'},
- #{:name => t('application_controller.admin_tabs.groups'), :url => groups_url, :id => 'groups'},
+ {:name => t('application_controller.admin_tabs.user_groups'), :url => user_groups_url, :id => 'user_groups'},
{:name => t('application_controller.admin_tabs.permissions'), :url => permissions_url, :id => 'permissions'},
]
unless @details_tab = @tabs.find {|t| t[:id] == tab}
@@ -350,7 +350,10 @@ class ApplicationController < ActionController::Base
else
local_perms = @permission_object.permissions
end
- @permissions = paginate_collection(local_perms, params[:page])
+ @permissions = paginate_collection(local_perms.
+ apply_filters(:preset_filter_id => params[:permissions_preset_filter],
+ :search_filter => params[:permissions_search]),
+ params[:page])
@permission_list_header = []
unless (@show_inherited or @show_global)
diff --git a/src/app/controllers/permissions_controller.rb b/src/app/controllers/permissions_controller.rb
index acdbb2c..6d8ae54 100644
--- a/src/app/controllers/permissions_controller.rb
+++ b/src/app/controllers/permissions_controller.rb
@@ -165,11 +165,21 @@ class PermissionsController < ApplicationController
:permission_object_id => p.permission_object_id
end
+ def filter
+ redirect_to_original({"permissions_preset_filter" => params[:permissions_preset_filter], "permissions_search" => params[:permissions_search]})
+ end
+
+ def filter_entities
+ redirect_to_original({"entities_preset_filter" => params[:entities_preset_filter], "entities_search" => params[:entities_search]})
+ end
+
private
def load_entities
sort_order = params[:sort_by].nil? ? "name" : params[:sort_by]
- @entities = paginate_collection(Entity.all(:order => sort_order), params[:page])
+ @entities = paginate_collection(Entity.order(sort_order).
+ apply_filters(:preset_filter_id => params[:entities_preset_filter],
+ :search_filter => params[:entities_search]), params[:page])
end
def load_headers
diff --git a/src/app/controllers/user_groups_controller.rb b/src/app/controllers/user_groups_controller.rb
new file mode 100644
index 0000000..59b8665
--- /dev/null
+++ b/src/app/controllers/user_groups_controller.rb
@@ -0,0 +1,238 @@
+#
+# Copyright 2011 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class UserGroupsController < ApplicationController
+ before_filter :require_user, :except => [:new, :create]
+
+ def index
+ require_privilege(Privilege::VIEW, User)
+ @title = t'user_groups.groups'
+ clear_breadcrumbs
+ save_breadcrumb(user_groups_path)
+ set_admin_users_tabs 'user_groups'
+ @params = params
+ load_headers
+ load_user_groups
+ respond_to do |format|
+ format.html
+ format.js { render :partial => 'list' }
+ end
+ end
+
+ def show
+ @user_group = UserGroup.find(params[:id])
+ require_privilege(Privilege::VIEW, User)
+ @title = @user_group.name
+ save_breadcrumb(user_group_path(@user_group), @user_group.name)
+ @tab_captions = ['Properties']
+ @details_tab = params[:details_tab].blank? ? 'properties' : params[:details_tab]
+ @members = paginate_collection((a)user_group.members, params[:page])
+ respond_to do |format|
+ format.html
+ format.js do
+ if params.delete :details_pane
+ render :partial => 'layouts/details_pane' and return
+ end
+ render :partial => @details_tab
+ end
+ end
+ end
+
+ def new
+ require_privilege(Privilege::CREATE, User)
+ @title = t'user_groups.new.new_user_group'
+ @user_group = UserGroup.new
+ # remove when we enable ldap
+ @user_group.membership_source = UserGroup::MEMBERSHIP_SOURCE_LOCAL
+ end
+
+ def create
+ if params[:commit] == "Reset"
+ redirect_to :action => 'new' and return
+ end
+
+ require_privilege(Privilege::MODIFY, User)
+ @user_group = UserGroup.new(params[:user_group])
+ unless @user_group.save
+ render :action => 'new' and return
+ end
+
+ respond_to do |format|
+ if @user_group.save
+ flash[:notice] = t "user_groups.flash.notice.added"
+ format.html { redirect_to user_groups_path }
+ format.json { render :json => @user_group, :status => :created }
+ else
+ flash.now[:warning] = t "user_groups.flash.warning.creation_failed"
+ format.html { render :new }
+ format.js { render :partial => 'new' }
+ format.json { render :json => @user_group.errors, :status => :unprocessable_entity }
+ end
+ end
+ end
+
+ def edit
+ @user_group = UserGroup.find(params[:id])
+ require_privilege(Privilege::MODIFY, User)
+ @title = t'user_groups.edit.edit_user_group'
+ end
+
+ def update
+ @user_group = UserGroup.find(params[:id])
+ require_privilege(Privilege::MODIFY, User)
+
+ if params[:commit] == "Reset"
+ redirect_to edit_user_group_url(@user_group) and return
+ end
+
+ redirect_to root_url and return unless @user_group
+
+ unless @user_group.update_attributes(params[:user_group])
+ render :action => 'edit' and return
+ else
+ flash[:notice] = t"user_groups.flash.notice.updated"
+ redirect_to user_group_path(@user_group)
+ end
+ end
+
+ def multi_destroy
+ require_privilege(Privilege::MODIFY, User)
+ deleted_user_groups = []
+
+ begin
+ UserGroup.transaction do
+ UserGroup.find(params[:user_group_selected]).each do |user_group|
+ if user_group.destroy
+ deleted_user_groups << user_group.name
+ end
+ end
+ end
+
+ unless deleted_user_groups.empty?
+ flash[:notice] = "#{t('user_groups.flash.notice.more_deleted', :count => deleted_user_groups.length)} #{deleted_user_groups.join(', ')}"
+ end
+
+ rescue => ex
+ flash[:warning] = t('user_groups.flash.warning.not_delete', :reason => ex.message)
+ end
+
+ redirect_to user_groups_url
+ end
+
+ def destroy
+ require_privilege(Privilege::MODIFY, User)
+ user_group = UserGroup.find(params[:id])
+ if user_group.destroy
+ flash[:notice] = t"user_groups.flash.notice.deleted"
+ else
+ flash[:notice] = t"user_groups.flash.warning.delete_failed"
+ end
+
+ respond_to do |format|
+ format.html { redirect_to user_groups_path }
+ end
+ end
+
+ def add_members
+ @user_group = UserGroup.find(params[:id])
+ require_privilege(Privilege::MODIFY, User)
+
+ unless @user_group.membership_source == UserGroup::MEMBERSHIP_SOURCE_LOCAL
+ flash[:error] = t('user_groups.flash.error.not_a_local_group')
+ redirect_to user_group_path(@user_group) and return
+ end
+
+ @users = paginate_collection(User.where('users.id not in (?)',
+ @user_group.members.empty? ?
+ 0 : @user_group.members.map(&:id)),
+ params[:page])
+
+ added = []
+ not_added = []
+
+ if params[:members_selected].blank?
+ flash[:error] = t"user_groups.flash.error.select_to_add_members" if request.post?
+ else
+ User.find(params[:members_selected]).each do |member|
+ if !(a)user_group.members.include?(member) and
+ @user_group.members << member
+ added << member.name
+ else
+ not_added << member.name
+ end
+ end
+ unless added.empty?
+ flash[:notice] = "#{t('user_groups.flash.notice.members_added')}: #{added.join(', ')}"
+ end
+ unless not_added.empty?
+ flash[:error] = "#{t('user_groups.flash.error.members_not_added')}: #{not_added.join(', ')}"
+ end
+ respond_to do |format|
+ format.html { redirect_to user_group_path(@user_group) }
+ end
+ end
+ end
+
+ def remove_members
+ @user_group = UserGroup.find(params[:id])
+ require_privilege(Privilege::MODIFY, User)
+
+ unless @user_group.membership_source == UserGroup::MEMBERSHIP_SOURCE_LOCAL
+ flash[:error] = t('user_groups.flash.error.not_a_local_group')
+ redirect_to user_group_path(@user_group) and return
+ end
+
+ removed=[]
+ not_removed=[]
+
+ if params[:members_selected].blank?
+ if request.post?
+ flash[:error] = t"user_groups.flash.error.select_to_remove_members"
+ end
+ else
+ User.find(params[:members_selected]).each do |member|
+ if @user_group.members.delete member
+ removed << member.name
+ else
+ not_removed << member.name
+ end
+ end
+ unless removed.empty?
+ flash[:notice] = "#{t('user_groups.flash.notice.members_removed')}: #{removed.join(', ')}"
+ end
+ unless not_removed.empty?
+ flash[:error] = "#{t('user_groups.flash.error.members_not_removed')}: #{not_removed.join(', ')}"
+ end
+ end
+ respond_to do |format|
+ format.html { redirect_to user_group_path(@user_group) }
+ end
+ end
+
+ def load_user_groups
+ sort_order = params[:sort_by].nil? ? "name" : params[:sort_by]
+ @user_groups = UserGroup.apply_filters(:preset_filter_id => params[:users_preset_filter], :search_filter => params[:user_groups_search]).order(sort_order)
+ end
+
+ def load_headers
+ @header = [
+ { :name => 'checkbox', :class => 'checkbox', :sortable => false },
+ { :name => t('user_groups.index.name'), :sortable => false },
+ { :name => t('user_groups.index.type'), :sortable => false },
+ ]
+ end
+
+end
diff --git a/src/app/models/common_filter_methods.rb b/src/app/helpers/user_groups_helper.rb
similarity index 55%
copy from src/app/models/common_filter_methods.rb
copy to src/app/helpers/user_groups_helper.rb
index 7bb2be2..85ad81f 100644
--- a/src/app/models/common_filter_methods.rb
+++ b/src/app/helpers/user_groups_helper.rb
@@ -1,5 +1,5 @@
#
-# Copyright 2011 Red Hat, Inc.
+# Copyright 2012 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,19 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-#this module contains functions for filtering table data
-module CommonFilterMethods
- def apply_filters(options = {})
- apply_preset_filter(options[:preset_filter_id]).apply_search_filter(options[:search_filter])
- end
-
- private
-
- def apply_preset_filter(preset_filter_id)
- if preset_filter_id.present?
- self::PRESET_FILTERS_OPTIONS.select{|item| item[:id] == preset_filter_id}.first[:query]
- else
- scoped
- end
+module UserGroupsHelper
+ def members_header
+ [
+ { :name => 'checkbox', :class => 'checkbox', :sortable => false },
+ { :name => t('users.index.username'), :sortable => false },
+ { :name => t('user_groups.index.name'), :sortable => false },
+ ]
end
end
diff --git a/src/app/models/common_filter_methods.rb b/src/app/models/common_filter_methods.rb
index 7bb2be2..9491ecb 100644
--- a/src/app/models/common_filter_methods.rb
+++ b/src/app/models/common_filter_methods.rb
@@ -23,7 +23,13 @@ module CommonFilterMethods
def apply_preset_filter(preset_filter_id)
if preset_filter_id.present?
- self::PRESET_FILTERS_OPTIONS.select{|item| item[:id] == preset_filter_id}.first[:query]
+ option = self::PRESET_FILTERS_OPTIONS.select { |item|
+ item[:id] == preset_filter_id}.first
+ if option[:query]
+ option[:query]
+ else
+ includes(option[:includes]).where(option[:where])
+ end
else
scoped
end
diff --git a/src/app/models/derived_permission.rb b/src/app/models/derived_permission.rb
index b7a2530..80d055d 100644
--- a/src/app/models/derived_permission.rb
+++ b/src/app/models/derived_permission.rb
@@ -15,6 +15,9 @@
#
class DerivedPermission < ActiveRecord::Base
+ class << self
+ include CommonFilterMethods
+ end
# the source permission for the denormalized object
belongs_to :permission
validates_presence_of :permission_id
@@ -54,4 +57,24 @@ class DerivedPermission < ActiveRecord::Base
validates_uniqueness_of :permission_id, :scope => [:permission_object_id,
:permission_object_type]
+ # :query is handled differently for permission
+ PRESET_FILTERS_OPTIONS = [
+ {:title => "permissions.preset_filters.user_permissions",
+ :id => "user_permissions",
+ :includes => :entity,
+ :where => {"entities.entity_target_type" => "User"}},
+ {:title => "permissions.preset_filters.group_permissions",
+ :id => "group_permissions",
+ :includes => :entity,
+ :where => {"entities.entity_target_type" => "UserGroup"}}
+ ]
+
+ def self.apply_search_filter(search)
+ if search
+ includes("entity").where("lower(entities.name) LIKE :search", :search => "%#{search.downcase}%")
+ else
+ scoped
+ end
+ end
+
end
diff --git a/src/app/models/entity.rb b/src/app/models/entity.rb
index bbc56fe..db88a5e 100644
--- a/src/app/models/entity.rb
+++ b/src/app/models/entity.rb
@@ -15,6 +15,9 @@
#
class Entity < ActiveRecord::Base
+ class << self
+ include CommonFilterMethods
+ end
belongs_to :entity_target, :polymorphic => true
validates_presence_of :entity_target_id
has_many :session_entities, :dependent => :destroy
@@ -26,4 +29,20 @@ class Entity < ActiveRecord::Base
belongs_to :user_group, :class_name => "UserGroup",
:foreign_key => "entity_target_id"
+ PRESET_FILTERS_OPTIONS = [
+ {:title => "user",
+ :id => "users",
+ :where => {"entities.entity_target_type" => "User"}},
+ {:title => "user_group",
+ :id => "user_groups",
+ :where => {"entities.entity_target_type" => "UserGroup"}}
+ ]
+
+ def self.apply_search_filter(search)
+ if search
+ where("lower(entities.name) LIKE :search", :search => "%#{search.downcase}%")
+ else
+ scoped
+ end
+ end
end
diff --git a/src/app/models/permission.rb b/src/app/models/permission.rb
index 048f7a5..9b4c0ac 100644
--- a/src/app/models/permission.rb
+++ b/src/app/models/permission.rb
@@ -30,6 +30,9 @@
#
class Permission < ActiveRecord::Base
+ class << self
+ include CommonFilterMethods
+ end
belongs_to :role
belongs_to :entity
@@ -67,6 +70,17 @@ class Permission < ActiveRecord::Base
after_save :update_derived_permissions
+ # :query is handled differently for permission
+ PRESET_FILTERS_OPTIONS = [
+ {:title => "permissions.preset_filters.user_permissions",
+ :id => "user_permissions",
+ :includes => :entity,
+ :where => {"entities.entity_target_type" => "User"}},
+ {:title => "permissions.preset_filters.group_permissions",
+ :id => "group_permissions",
+ :includes => :entity,
+ :where => {"entities.entity_target_type" => "UserGroup"}}
+ ]
def user
entity.user
end
@@ -100,4 +114,13 @@ class Permission < ActiveRecord::Base
end
end
end
+
+ def self.apply_search_filter(search)
+ if search
+ includes("entity").where("lower(entities.name) LIKE :search", :search => "%#{search.downcase}%")
+ else
+ scoped
+ end
+ end
+
end
diff --git a/src/app/models/user_group.rb b/src/app/models/user_group.rb
index 293d0db..dcb2f63 100644
--- a/src/app/models/user_group.rb
+++ b/src/app/models/user_group.rb
@@ -15,6 +15,9 @@
#
class UserGroup < ActiveRecord::Base
+ class << self
+ include CommonFilterMethods
+ end
# name will correspond to the group name if we're using LDAP, otherwise it's
# entered by the admin creating the group
@@ -42,4 +45,13 @@ class UserGroup < ActiveRecord::Base
self.entity.name = "#{self.name} (#{self.membership_source})"
self.entity.save!
end
+
+ def self.apply_search_filter(search)
+ if search
+ where("lower(name) LIKE :search", :search => "%#{search.downcase}%")
+ else
+ scoped
+ end
+ end
+
end
diff --git a/src/app/views/permissions/_form.html.haml b/src/app/views/permissions/_form.html.haml
index 95aa05d..39b1865 100644
--- a/src/app/views/permissions/_form.html.haml
+++ b/src/app/views/permissions/_form.html.haml
@@ -1,3 +1,23 @@
+- content_for :entities_filter_controls do
+ %li
+ = label_tag :entities_preset_filter, t('filter_table.viewing')
+ = select_tag(:entities_preset_filter, preset_filters_options_for_select(Entity::PRESET_FILTERS_OPTIONS, params[:entities_preset_filter]), :include_blank => t("permissions.preset_filters.all_entities"), :disabled => false)
+ = hidden_field_tag :current_path, request.fullpath
+ = restful_submit_tag t("filter_table.apply_filters"), "filter", filter_entities_permissions_path, 'POST', :class => 'button', :id => 'apply_entities_preset_filter'
+ %span.label.badge.dark= @entities.count
+ %li.table-search-filter
+ = text_field_tag :entities_search, params[:entities_search], :placeholder => t("filter_table.search")
+ = restful_submit_tag "", "search", filter_entities_permissions_path, 'POST', :class => 'button', :id => 'apply_entities_search'
+
+ :javascript
+ $(document).ready(function(){
+ $("#apply_entities_preset_filter").hide();
+ $("#entities_preset_filter").change(function() {
+ $("#apply_entities_preset_filter").click();
+ });
+ Conductor.clickOnEnterKeypress($('#entities_search'), $('#apply_entities_search'));
+ });
+
- content_for :form_footer do
= hidden_field_tag :permission_object_type, @permission_object.class.name
= hidden_field_tag :permission_object_id, @permission_object.id
@@ -5,7 +25,8 @@
= hidden_field_tag :use_tabs, @use_tabs
= link_to t('cancel'), @return_path, :class => 'button danger'
= restful_submit_tag t('permissions.form.grant_access'), "create", permissions_path, 'POST', :id => 'save_button', :class => 'button'
-= filter_table(@header, @entities) do |entity|
+= filter_table(@header, @entities,
+ :filter_controls => :entities_filter_controls) do |entity|
%tr{:class => cycle('nostripe','stripe')}
%td
-# - selected = params[:select] == 'all'
diff --git a/src/app/views/permissions/_permissions.html.haml b/src/app/views/permissions/_permissions.html.haml
index b1e18f7..56a0c8e 100644
--- a/src/app/views/permissions/_permissions.html.haml
+++ b/src/app/views/permissions/_permissions.html.haml
@@ -24,6 +24,26 @@
%li= link_to t('permissions.inherited_access'), params.merge(:show_inherited => true, :show_global => false, :page => 1), { :class => 'button primary', :id => 'inherited_permission_button'}
%li= link_to t('permissions.global_access'), params.merge(:show_inherited => false, :show_global => true, :page => 1), { :class => 'button primary', :id => 'global_permission_button'}
+- content_for :permissions_filter_controls do
+ %li
+ = label_tag :permissions_preset_filter, t('filter_table.viewing')
+ = select_tag(:permissions_preset_filter, preset_filters_options_for_select(Permission::PRESET_FILTERS_OPTIONS, params[:permissions_preset_filter]), :include_blank => t("permissions.preset_filters.all_permissions"), :disabled => false)
+ = hidden_field_tag :current_path, request.fullpath
+ = restful_submit_tag t("filter_table.apply_filters"), "filter", filter_permissions_path, 'POST', :class => 'button', :id => 'apply_permissions_preset_filter'
+ %span.label.badge.dark= @permissions.count
+ %li.table-search-filter
+ = text_field_tag :permissions_search, params[:permissions_search], :placeholder => t("filter_table.search")
+ = restful_submit_tag "", "search", filter_permissions_path, 'POST', :class => 'button', :id => 'apply_permissions_search'
+
+ :javascript
+ $(document).ready(function(){
+ $("#apply_permissions_preset_filter").hide();
+ $("#permissions_preset_filter").change(function() {
+ $("#apply_permissions_preset_filter").click();
+ });
+ Conductor.clickOnEnterKeypress($('#permissions_search'), $('#apply_permissions_search'));
+ });
+
= filter_table(@permission_list_header,
@permissions,
:form_header => :permissions_form_header,
diff --git a/src/app/views/user_groups/_form.html.haml b/src/app/views/user_groups/_form.html.haml
new file mode 100644
index 0000000..1db6a7a
--- /dev/null
+++ b/src/app/views/user_groups/_form.html.haml
@@ -0,0 +1,26 @@
+- if @user_group.errors.any?
+ = render 'layouts/error_messages', :object => @user_group
+
+%fieldset
+ .field
+ = form.label :type
+ .input
+ - if @user_group.new_record?
+ = form.select(:membership_source, UserGroup::MEMBERSHIP_SOURCES.collect {|x| [t("user_groups.#{x.downcase}"), x]}, {}, {:disabled => true})
+ = form.hidden_field :membership_source
+ - else
+ = t("user_groups.#{(a)user_group.membership_source.downcase}")
+ .field
+ = form.label :name
+ .input
+ - if @user_group.new_record? or @user_group.membership_source == UserGroup::MEMBERSHIP_SOURCE_LOCAL
+ = form.text_field :name
+ - else
+ = @user_group.name
+ .field
+ = form.label :description
+ .input
+ = form.text_area :description, :class => 'long'
+
+%fieldset.options
+ = form.submit "#{t'user_groups.form.save_user_group'}", :class => "submit button pill"
diff --git a/src/app/views/user_groups/_list.html.haml b/src/app/views/user_groups/_list.html.haml
new file mode 100644
index 0000000..5c99b39
--- /dev/null
+++ b/src/app/views/user_groups/_list.html.haml
@@ -0,0 +1,41 @@
+%header
+ %h2#user_groups.groups= @title
+
+- content_for :form_header do
+ %li= restful_submit_tag "#{t'user_groups.list.delete_selected'}", "destroy", multi_destroy_user_groups_path, 'DELETE', :id => 'delete_button', :class => 'button danger'
+ %li= link_to t("user_groups.list.add_user_group"), new_user_group_path, :class => 'button', :id => 'add_user_group_button'
+
+- content_for :filter_controls do
+ %li
+ = label_tag :user_groups_preset_filter, t('filter_table.viewing')
+ = hidden_field_tag :current_path, request.fullpath
+ = select_tag(:user_groups_preset_filter, preset_filters_options_for_select(User::PRESET_FILTERS_OPTIONS, params[:user_groups_preset_filter]), :include_blank => t("user_groups.preset_filters.all_user_groups"), :disabled => true)
+ = restful_submit_tag t("filter_table.apply_filters"), "index", filter_user_groups_path, 'POST', :class => 'button', :id => 'apply_user_groups_preset_filter'
+ %span.label.badge.dark= @user_groups.count
+ / = link_to "Filter", "#", :class => 'button pill'
+ /%li
+ /%li.more_actions
+ / %span= image_tag "button-userdrop.png"
+ / %ul
+ / %li= link_to "Add/Remove Columns", "#"
+ / %li= link_to "Freeze Column", "#"
+ %li.table-search-filter
+ = text_field_tag :user_groups_search, params[:user_groups_search], :placeholder => t("filter_table.search")
+ = restful_submit_tag "", "index", filter_user_groups_path, 'POST', :class => 'button', :id => 'apply_user_groups_search'
+
+ :javascript
+ $(document).ready(function(){
+ $("#apply_user_groups_preset_filter").hide();
+ $("#user_groups_preset_filter").change(function() {
+ $("#apply_user_groups_preset_filter").click();
+ });
+ Conductor.clickOnEnterKeypress($('#user_groups_search'), $('#apply_user_groups_search'));
+ });
+
+= filter_table(@header, @user_groups) do |user_group|
+ %tr{:class => cycle('nostripe','stripe')}
+ %td{:class => 'checkbox'}
+ - selected = params[:select] == 'all'
+ = check_box_tag "user_group_selected[]", user_group.id, selected, :id => "user_group_checkbox_#{user_group.id}"
+ %td= link_to user_group.name, user_group_path(user_group)
+ %td= t("user_groups.#{user_group.membership_source.downcase}")
diff --git a/src/app/views/user_groups/add_members.html.haml b/src/app/views/user_groups/add_members.html.haml
new file mode 100644
index 0000000..4b3a30c
--- /dev/null
+++ b/src/app/views/user_groups/add_members.html.haml
@@ -0,0 +1,25 @@
+= render :partial => 'layouts/admin_nav'
+%header.page-header
+ .obj_actions
+ .return_to
+ = t'return_to'
+ = link_to @user_group.name, user_group_path(@user_group), :id => 'user_group_button'
+ %h1.user_groups= @user_group.name
+
+%section.content-section.user_groups
+ %header
+ .align-center
+ %strong= t("user_groups.choose_members")
+ .content
+ - content_for :form_footer do
+ = link_to t('cancel'), user_group_path(@user_group), :class => 'button danger'
+ = restful_submit_tag t('user_groups.show.add_members'), "add", add_members_user_group_path(@user_group), 'POST', :id => 'save_button', :class => 'button'
+ = filter_table(members_header, @users) do |user|
+ %tr{:class => cycle('nostripe','stripe')}
+ %td{:class => 'checkbox'}
+ - selected = params[:select] == 'all'
+ = check_box_tag "members_selected[]", user.id, selected, :id => "member_checkbox_#{user.id}"
+ %td
+ = link_to user.login, users_path(user)
+ %td
+ = user.name
diff --git a/src/app/views/user_groups/edit.html.haml b/src/app/views/user_groups/edit.html.haml
new file mode 100644
index 0000000..7350a43
--- /dev/null
+++ b/src/app/views/user_groups/edit.html.haml
@@ -0,0 +1,14 @@
+= render :partial => 'layouts/admin_nav'
+%header.page-header
+ .obj_actions
+ .return_to
+ =t'return_to'
+ = link_to "#{t'user_groups.groups'}", user_groups_path
+ %h1.users= @title
+
+%section.content-section.user_groups
+ .content
+ .align-center
+ = @user_group.name
+ = form_for @user_group, :url => user_group_path(@user_group), :html => {:class => 'generic horizontal'} do |f|
+ = render :partial => "form", :locals => { :form => f }
diff --git a/src/app/views/user_groups/index.html.haml b/src/app/views/user_groups/index.html.haml
new file mode 100644
index 0000000..7cc23db
--- /dev/null
+++ b/src/app/views/user_groups/index.html.haml
@@ -0,0 +1,2 @@
+= render :partial => 'layouts/admin_nav'
+= render :partial => 'layouts/admin_users_tabs'
diff --git a/src/app/views/user_groups/new.html.haml b/src/app/views/user_groups/new.html.haml
new file mode 100644
index 0000000..eaaf3a7
--- /dev/null
+++ b/src/app/views/user_groups/new.html.haml
@@ -0,0 +1,13 @@
+= render :partial => 'layouts/admin_nav'
+%header.page-header
+ .obj_actions
+ .return_to
+ =t'return_to'
+ = link_to "#{t'user_groups.groups'}", user_groups_path
+ %h1.users= @title
+
+%section.content-section.user_groups
+ .content
+ .align-center
+ = form_for @user_group, :url => user_groups_path, :html => {:class => 'generic horizontal'} do |f|
+ = render :partial => "form", :locals => { :form => f }
diff --git a/src/app/views/user_groups/show.html.haml b/src/app/views/user_groups/show.html.haml
new file mode 100644
index 0000000..426137f
--- /dev/null
+++ b/src/app/views/user_groups/show.html.haml
@@ -0,0 +1,71 @@
+= render :partial => 'layouts/admin_nav'
+%header.page-header
+ .obj_actions
+ .return_to
+ = t'return_to'
+ = link_to t("user_groups.groups"), user_groups_path
+ - if check_privilege(Privilege::CREATE, User)
+ = link_to t('user_groups.new.new_user_group'), new_user_group_url, :class => 'button primary', :id => 'new_user_group_button'
+ .button-group
+ - if check_privilege(Privilege::MODIFY, User)
+ = link_to t('edit'), edit_user_group_path(@user_group), :class => 'button pill', :id => 'edit_button'
+ = button_to t("delete"), user_group_path(@user_group), :method => 'delete', :confirm => "Are you sure you want to delete?", :class => 'button pill danger', :id => 'delete'
+ %h1.no-icon= @title
+
+%section.content-section.user
+ %header
+ %h2=t'properties'
+
+ .content
+ %table.properties_table
+ %tbody
+ %tr
+ %td= t('user_groups.show.type')
+ %td= @user_group.membership_source
+ %tr
+ %td= t('user_groups.show.name')
+ %td= @user_group.name
+ %tr
+ %td= t('user_groups.show.description')
+ %td= @user_group.description
+
+- if @user_group.membership_source == UserGroup::MEMBERSHIP_SOURCE_LOCAL
+ %section.content-section
+ %header
+ %h2=t('user_groups.show.members')
+
+ .content
+ - content_for :form_header do
+ - if check_privilege(Privilege::CREATE, Deployable)
+ %li= restful_submit_tag t("remove"), "destroy", remove_members_user_group_path(@user_group), 'DELETE', :id => 'delete_button', :class => 'button danger'
+ %li= link_to t("user_groups.show.add_members"), add_members_user_group_path(@user_group), :class => 'button', :id => 'add_members_button'
+
+ - content_for :filter_controls do
+ %li
+ = label_tag :members_preset_filter, t('filter_table.viewing')
+ = hidden_field_tag :current_path, request.fullpath
+ = select_tag(:members_preset_filter, preset_filters_options_for_select(Deployable::PRESET_FILTERS_OPTIONS, params[:members_preset_filter]), :include_blank => t("users.preset_filters.all_users"), :disabled => true)
+ = restful_submit_tag t("filter_table.apply_filters"), "filter", filter_members_user_group_path(@user_group), 'POST', :class => 'button', :id => 'apply_members_preset_filter'
+ %span.label.badge.dark= @user_group.members.count
+ %li.table-search-filter
+ = text_field_tag :members_search, params[:members_search], :placeholder => t("filter_table.search")
+ = restful_submit_tag "", "search", filter_members_user_group_path(@user_group), 'POST', :class => 'button', :id => 'apply_members_search'
+
+ :javascript
+ $(document).ready(function(){
+ $("#apply_members_preset_filter").hide();
+ $("#members_preset_filter").change(function() {
+ $("#apply_members_preset_filter").click();
+ });
+ Conductor.clickOnEnterKeypress($('#members_search'), $('#apply_members_search'));
+ });
+
+ = filter_table(members_header, @members) do |member|
+ %tr{:class => cycle('nostripe','stripe')}
+ %td{:class => 'checkbox'}
+ - selected = params[:select] == 'all'
+ = check_box_tag "members_selected[]", member.id, selected, :id => "member_checkbox_#{member.id}"
+ %td
+ = link_to member.login, users_path(member)
+ %td
+ = member.name
diff --git a/src/config/locales/en.yml b/src/config/locales/en.yml
index 8c2e010..9295373 100644
--- a/src/config/locales/en.yml
+++ b/src/config/locales/en.yml
@@ -102,6 +102,62 @@ en:
name_starts_with_B: "Name starts with B"
errors:
has_running_instances: '%{login} has running instances'
+ user_groups:
+ groups: "User Groups"
+ local: Local
+ ldap: LDAP
+ return_to: "Return to:"
+ confirm_delete: "Are you sure you want to delete this user group?"
+ quick_jump: "Quick Jump:"
+ choose_members: "Choose Users to add to this Group."
+ index:
+ name: Name
+ type: Type
+ show:
+ name: Name
+ type: Type
+ description: Description
+ members: Members
+ add_members: Add Members
+ edit: Edit
+ delete: Delete
+ created: CREATED
+ last_updated: LAST UPDATED
+ form:
+ name: Name
+ type: Type
+ save_user_group: Save User Group
+ new:
+ new_user_group: New User Group
+ neccessary_information: "Enter necessary information about your new user group and click 'Save User Group' when you are finished."
+ list:
+ delete_selected: Delete Selected
+ add_user_group: Add User Group
+ edit:
+ edit_user_group: Edit User Group
+ flash:
+ notice:
+ added: "User Group added"
+ updated: "User Group updated!"
+ deleted: "User Group has been successfully deleted."
+ more_deleted:
+ one: Deleted user group
+ other: Deleted user groups
+ members_added: "These users have been added"
+ members_removed: "These users have been removed"
+ warning:
+ creation_failed: 'Could not create user group'
+ delete_failed: 'Could not delete user group'
+ not_delete: 'Cannot delete: %{reason}'
+ error:
+ members_not_added: "Could not add these users"
+ members_not_removed: "Could not remove these users"
+ not_a_local_group: 'Only locally-managed groups can be managed here.'
+ select_to_add_members: 'You must select at least one user to add.'
+ preset_filters:
+ all_user_groups: "All User Groups"
+ local: "Locally-managed User Groups"
+ ldap: "LDAP-managed User Groups"
user_sessions:
new:
username: "Username:"
@@ -151,6 +207,7 @@ en:
role: Role
role_assignments: Role Assignments
user: User
+ user_group: User Group
privileges: Privileges
general_settings: General Settings
new_user: New User
@@ -1285,7 +1342,10 @@ en:
user_and_role: "%{user} (%{role})"
user_and_role_change: "%{user} (from %{old_role} to %{role})"
preset_filters:
+ all_entities: All Entities
all_permissions: All Permissions
+ user_permissions: User Permissions
+ group_permissions: Group Permissions
quotas:
quota: Quota
edit:
@@ -1333,6 +1393,7 @@ en:
administer: Administer
second_level:
users: Users
+ user_groups: User Groups
environments: Environments
content: Content
cloud_providers: Cloud Providers
@@ -1353,7 +1414,7 @@ en:
realms: Realms
hardware: Hardware
users: Users
- groups: Groups
+ user_groups: User Groups
permissions: Global Role Grants
pool_families: Pool Families
images: Images
diff --git a/src/config/navigation.rb b/src/config/navigation.rb
index c8a48a2..4c37e23 100644
--- a/src/config/navigation.rb
+++ b/src/config/navigation.rb
@@ -20,7 +20,7 @@ SimpleNavigation::Configuration.run do |navigation|
first_level.dom_class = 'container'
first_level.item :monitor, t('navigation.first_level.monitor'), pools_path, :class => 'monitor', :link => { :id => 'monitor' }, :highlights_on => /\/deployments|\/pools|\/instances|\/logs|\/\z/
first_level.item :administer, t('navigation.first_level.administer'), users_path, :class => 'administer' do |second_level|
- second_level.item :users, t('navigation.second_level.users'), users_path, :link => { :class => 'users' }, :highlights_on => /\/users|\/roles|\/permissions|\/account/
+ second_level.item :users, t('navigation.second_level.users'), users_path, :link => { :class => 'users' }, :highlights_on => /\/users|\/user_groups|\/roles|\/permissions|\/account/
second_level.item :environments, t('navigation.second_level.environments'), pool_families_path, :link => { :class => 'environments' }, :highlights_on => /\/pool_families|\/images/
second_level.item :content, t('navigation.second_level.content'), catalogs_path, :link => { :class => 'content' }, :highlights_on => /\/catalogs|\/catalog_entries|\/realms|\/hardware_profiles|\/realm_mappings|\/deployables/
second_level.item :cloud_providers, t('navigation.second_level.cloud_providers'), providers_path, :link => { :class => 'providers' }, :highlights_on => /\/providers|\/provider_realms|\/config_servers/
diff --git a/src/config/routes.rb b/src/config/routes.rb
index da762a7..68ed728 100644
--- a/src/config/routes.rb
+++ b/src/config/routes.rb
@@ -83,6 +83,8 @@ Conductor::Application.routes.draw do
get :list
delete :multi_destroy
post :multi_update
+ post :filter
+ post :filter_entities
end
end
@@ -156,6 +158,19 @@ Conductor::Application.routes.draw do
post :filter, :on => :collection
end
+ resources :user_groups do
+ collection do
+ delete 'multi_destroy'
+ post :filter
+ end
+ member do
+ get 'add_members'
+ post 'add_members'
+ delete 'remove_members'
+ post 'filter_members'
+ end
+ end
+
resources :logs do
collection do
post 'filter'
diff --git a/src/features/step_definitions/user_group_steps.rb b/src/features/step_definitions/user_group_steps.rb
new file mode 100644
index 0000000..2959a21
--- /dev/null
+++ b/src/features/step_definitions/user_group_steps.rb
@@ -0,0 +1,56 @@
+#
+# Copyright 2012 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+Given /^a user group "([^"]*)" exists$/ do |name|
+ unless UserGroup.find_by_name(name)
+ FactoryGirl.create :user_group, :name => name
+ end
+end
+
+Given /^there is a user group "([^"]*)"$/ do |name|
+ unless UserGroup.find_by_name(name)
+ FactoryGirl.create :user_group, :name => name
+ end
+end
+
+Then /^there should be (\d+) user groups?$/ do |number|
+ UserGroup.count.should == number.to_i
+end
+
+When /^I check "([^"]*)" user group$/ do |user_group_name|
+ user_group = UserGroup.find_by_name(user_group_name)
+ check("user_group_checkbox_#{user_group.id}")
+end
+
+When /^I check the "([^"]*)" member$/ do |login|
+ member = User.find_by_login(login)
+ check("member_checkbox_#{member.id}")
+end
+
+Then /^there should be (\d+) user belonging to "([^"]*)"$/ do |count, name|
+ user_group = UserGroup.find_by_name(name)
+ user_group.members.count == count
+end
+
+Given /^there is a user "([^"]*)" belonging to user group "([^"]*)"$/ do |login, group_name|
+ member = User.find_by_login(login)
+ user_group = UserGroup.find_by_name(group_name)
+ user_group.members << member
+end
+
+Then /^there should not exist a member belonging to "([^"]*)"$/ do |group_name|
+ user_group = UserGroup.find_by_name(group_name)
+ user_group.members.count == 0
+end
diff --git a/src/features/support/paths.rb b/src/features/support/paths.rb
index 38f926d..f809a8c 100644
--- a/src/features/support/paths.rb
+++ b/src/features/support/paths.rb
@@ -35,6 +35,9 @@ module NavigationHelpers
when /^(.*)'s user page$/i
user_path(User.find_by_login($1))
+ when /^(.*)'s user group page$/i
+ user_group_path(UserGroup.find_by_name($1))
+
when /^(.*)'s role page$/i
role_path(Role.find_by_name($1))
@@ -179,6 +182,9 @@ module NavigationHelpers
when /^the (.*)'s edit user page$/
edit_user_path(User.find_by_login($1))
+ when /^the (.*)'s edit user group page$/
+ edit_user_group_path(UserGroup.find_by_name($1))
+
when /^the "(.*)" catalog page/
url_for catalog_path(Catalog.find_by_name($1))
diff --git a/src/features/user_group.feature b/src/features/user_group.feature
new file mode 100644
index 0000000..08f4db7
--- /dev/null
+++ b/src/features/user_group.feature
@@ -0,0 +1,72 @@
+Feature: Manage User Groups
+ In order to manage user groups
+ As an admin
+ I want to add/edit/remove user groupss
+
+ Background:
+ Given I am an authorised user
+ And I am logged in
+ And a user group "testgroup" exists
+ And a user "testuser" exists
+
+ Scenario: Show user group detials
+ Given I am on the user groups page
+ And there is a user group "testgroup"
+ When I follow "testgroup"
+ Then I should be on testgroup's user group page
+
+ Scenario: Delete user groups
+ Given there is a user group "testgroup"
+ Given there is a user group "testgroup2"
+ And I am on the user groups page
+ Then there should be 2 user groups
+ And I should be on the user groups page
+ When I check "testgroup" user group
+ And I press "Delete"
+ Then I should see "Deleted user group"
+ And there should be 1 user group
+
+ Scenario: Create new user group
+ Given I am on the user groups page
+ When I follow "add_user_group_button"
+ Then I should be on the new user group page
+ And I should see "New User Group"
+ When I fill in the following:
+ | Name | testgroup3 |
+ And I press "Save"
+ Then I should be on the user groups page
+ And I should see "User Group added"
+
+ Scenario: Edit existing user group
+ Given I am on the user groups page
+ And I follow "testgroup"
+ Then I should be on testgroup's user group page
+ And I should see "testgroup"
+ When I follow "Edit"
+ Then I should be on the testgroup's edit user group page
+ And I fill in "user_group_name" with "newname"
+ When I press "Save"
+ Then I should be on newname's user group page
+ And I should see "User Group updated"
+ And I should see "newname"
+
+ Scenario: Add member to user group
+ Given I am on the user groups page
+ And I follow "testgroup"
+ Then I should be on testgroup's user group page
+ And I should see "testgroup"
+ When I follow "Add Member"
+ And I check the "testuser" member
+ And I press "Add Member"
+ Then there should be 1 user belonging to "testgroup"
+ And I should see "testuser"
+
+ Scenario: Remove member from user group
+ And there is a user "testuser" belonging to user group "testgroup"
+ Given I am on the user groups page
+ And I follow "testgroup"
+ Then I should be on testgroup's user group page
+ Then I should see "testuser"
+ When I check the "testuser" member
+ And I press "Remove"
+ Then there should not exist a member belonging to "testgroup"
diff --git a/src/spec/controllers/user_groups_controller_spec.rb b/src/spec/controllers/user_groups_controller_spec.rb
new file mode 100644
index 0000000..d5e68bb
--- /dev/null
+++ b/src/spec/controllers/user_groups_controller_spec.rb
@@ -0,0 +1,75 @@
+#
+# Copyright 2011 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe UserGroupsController do
+
+ fixtures :all
+ before(:each) do
+ @user_group = Factory.create(:user_group)
+ @tuser = FactoryGirl.create :tuser
+ @admin_permission = FactoryGirl.create :admin_permission
+ @admin = @admin_permission.user
+ end
+
+ describe "#create" do
+ context "user enters valid input" do
+ it "creates user group" do
+ mock_warden(@admin)
+ lambda do
+ post :create, :user_group => {
+ :name => "user_group2", :membership_source => "local" }
+ end.should change(UserGroup, :count).by(1)
+
+ response.should redirect_to(user_groups_path)
+ end
+
+ it "fails to create user" do
+ mock_warden(@admin)
+ lambda do
+ post :create, :user_group => {}
+ end.should_not change(UserGroup, :count)
+
+ returned_user_group = assigns[:user_group]
+ returned_user_group.errors.empty?.should be_false
+ returned_user_group.should have(1).errors_on(:name)
+ returned_user_group.should have(2).errors_on(:membership_source)
+
+ response.should render_template('new')
+ end
+ end
+ end
+
+ it "allows an admin to create user group" do
+ mock_warden(@admin)
+ lambda do
+ post :create, :user_group => {
+ :name => "user_group3", :membership_source => "local" }
+ end.should change(UserGroup, :count)
+
+ response.should redirect_to(user_groups_url)
+ end
+
+ it "should not allow a regular user to create user group" do
+ mock_warden(@tuser)
+ lambda do
+ post :create, :user_group => {
+ :name => "user_group4", :membership_source => "local" }
+ end.should_not change(UserGroup, :count)
+ end
+
+end
diff --git a/src/spec/factories/permission.rb b/src/spec/factories/permission.rb
index e401fe8..4b2d2b4 100644
--- a/src/spec/factories/permission.rb
+++ b/src/spec/factories/permission.rb
@@ -62,4 +62,11 @@ FactoryGirl.define do
entity { |r| FactoryGirl.create(:pool_family_user).entity }
end
+ factory :group_admin_permission, :parent => :permission do
+ role { |r| Role.first(:conditions => ['name = ?', 'base.admin']) || FactoryGirl.create(:role, :name => 'base.admin') }
+ permission_object { |r| BasePermissionObject.general_permission_scope }
+ entity { |r| FactoryGirl.create(:user_group).entity }
+ end
+
+
end
diff --git a/src/app/models/common_filter_methods.rb b/src/spec/factories/user_group.rb
similarity index 56%
copy from src/app/models/common_filter_methods.rb
copy to src/spec/factories/user_group.rb
index 7bb2be2..bf1f6fc 100644
--- a/src/app/models/common_filter_methods.rb
+++ b/src/spec/factories/user_group.rb
@@ -1,5 +1,5 @@
#
-# Copyright 2011 Red Hat, Inc.
+# Copyright 2012 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,19 +13,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-#this module contains functions for filtering table data
-module CommonFilterMethods
- def apply_filters(options = {})
- apply_preset_filter(options[:preset_filter_id]).apply_search_filter(options[:search_filter])
- end
- private
+FactoryGirl.define do
+
+ factory :user_group do |u|
+ sequence(:name) { |n| "group#{n}" }
+ membership_source 'local'
+ end
- def apply_preset_filter(preset_filter_id)
- if preset_filter_id.present?
- self::PRESET_FILTERS_OPTIONS.select{|item| item[:id] == preset_filter_id}.first[:query]
- else
- scoped
- end
+ factory :ldap_group , :parent => :user_group do
+ membership_source = 'LDAP'
end
+
end
diff --git a/src/spec/models/permission_spec.rb b/src/spec/models/permission_spec.rb
index 7f05587..ea9a20d 100644
--- a/src/spec/models/permission_spec.rb
+++ b/src/spec/models/permission_spec.rb
@@ -84,4 +84,23 @@ describe Permission do
should be_false
end
+ it "User added to Admin group should be able to create users" do
+ newuser = FactoryGirl.create(:user)
+ group_admin_permission = FactoryGirl.create(:group_admin_permission)
+ user_group = group_admin_permission.user_group
+ SessionEntity.update_session(@session_id, newuser)
+ BasePermissionObject.general_permission_scope.has_privilege(@session_id,
+ newuser,
+ Privilege::CREATE,
+ User).should be_false
+ user_group.members << newuser
+ newuser.reload
+ SessionEntity.update_session(@session_id, newuser)
+ BasePermissionObject.general_permission_scope.has_privilege(@session_id,
+ newuser,
+ Privilege::CREATE,
+ User).should be_true
+
+ end
+
end
diff --git a/src/spec/models/user_group_spec.rb b/src/spec/models/user_group_spec.rb
new file mode 100644
index 0000000..915df0a
--- /dev/null
+++ b/src/spec/models/user_group_spec.rb
@@ -0,0 +1,43 @@
+#
+# Copyright 2012 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe UserGroup do
+ before(:each) do
+ end
+
+ it "should create a new local group" do
+ user_group = Factory.create(:user_group)
+ user_group.should be_valid
+ user_group.members.size.should == 0
+ end
+
+ it "should create a new ldap group" do
+ user_group = Factory.create(:ldap_group)
+ user_group.should be_valid
+ end
+
+ it "should add members to a new local group" do
+ user_group = Factory.create(:user_group)
+ user = Factory.create(:tuser)
+ user_group.members.size.should == 0
+ user_group.members << user
+ user_group.reload
+ user_group.members.size.should == 1
+ end
+
+end
--
1.7.6.5
11 years, 10 months
[PATCH conductor] Tasks 3387/3388 UI for user groups implementation
by Scott Seago
UI for managing local user groups and for assigning group permissions.
When testing, note that after adding/removing a user from a group
the user must log out and in again to see the effects.
---
aeolus-conductor.spec.in | 1 +
src/app/controllers/application_controller.rb | 7 +-
src/app/controllers/permissions_controller.rb | 12 +-
src/app/controllers/user_groups_controller.rb | 202 ++++++++++++++++++++
.../user_groups_helper.rb} | 23 +--
src/app/models/common_filter_methods.rb | 8 +-
src/app/models/derived_permission.rb | 23 +++
src/app/models/entity.rb | 19 ++
src/app/models/permission.rb | 23 +++
src/app/models/user_group.rb | 12 ++
src/app/views/permissions/_form.html.haml | 23 ++-
src/app/views/permissions/_permissions.html.haml | 20 ++
src/app/views/user_groups/_form.html.haml | 26 +++
src/app/views/user_groups/_list.html.haml | 41 ++++
src/app/views/user_groups/add_members.html.haml | 25 +++
src/app/views/user_groups/edit.html.haml | 14 ++
src/app/views/user_groups/index.html.haml | 2 +
src/app/views/user_groups/new.html.haml | 13 ++
src/app/views/user_groups/show.html.haml | 71 +++++++
src/config/locales/en.yml | 62 ++++++-
src/config/routes.rb | 15 ++
.../step_definitions/user_group_steps.rb} | 26 ++-
src/features/support/paths.rb | 6 +
src/features/user_group.feature | 54 ++++++
.../controllers/user_groups_controller_spec.rb | 75 +++++++
src/spec/factories/permission.rb | 7 +
.../factories/user_group.rb} | 23 +--
src/spec/models/permission_spec.rb | 19 ++
src/spec/models/user_group_spec.rb | 43 ++++
29 files changed, 851 insertions(+), 44 deletions(-)
create mode 100644 src/app/controllers/user_groups_controller.rb
copy src/app/{models/common_filter_methods.rb => helpers/user_groups_helper.rb} (55%)
create mode 100644 src/app/views/user_groups/_form.html.haml
create mode 100644 src/app/views/user_groups/_list.html.haml
create mode 100644 src/app/views/user_groups/add_members.html.haml
create mode 100644 src/app/views/user_groups/edit.html.haml
create mode 100644 src/app/views/user_groups/index.html.haml
create mode 100644 src/app/views/user_groups/new.html.haml
create mode 100644 src/app/views/user_groups/show.html.haml
copy src/{app/models/entity.rb => features/step_definitions/user_group_steps.rb} (52%)
create mode 100644 src/features/user_group.feature
create mode 100644 src/spec/controllers/user_groups_controller_spec.rb
copy src/{app/models/common_filter_methods.rb => spec/factories/user_group.rb} (56%)
create mode 100644 src/spec/models/user_group_spec.rb
diff --git a/aeolus-conductor.spec.in b/aeolus-conductor.spec.in
index d2c4efd..1ed8159 100644
--- a/aeolus-conductor.spec.in
+++ b/aeolus-conductor.spec.in
@@ -199,6 +199,7 @@ haml="app/views/hardware_profiles app/views/realm_mappings \
app/views/api/hooks \
app/views/api \
app/views/images \
+ app/views/user_groups \
app/views/api/builds app/views/api/provider_images \
app/views/api/target_images app/views/api/entrypoint \
app/views/api/environments \
diff --git a/src/app/controllers/application_controller.rb b/src/app/controllers/application_controller.rb
index eb5931b..b30326b 100644
--- a/src/app/controllers/application_controller.rb
+++ b/src/app/controllers/application_controller.rb
@@ -281,7 +281,7 @@ class ApplicationController < ActionController::Base
def set_admin_users_tabs(tab)
@tabs = [{:name => t('application_controller.admin_tabs.users'), :url => users_url, :id => 'users'},
- #{:name => t('application_controller.admin_tabs.groups'), :url => groups_url, :id => 'groups'},
+ {:name => t('application_controller.admin_tabs.user_groups'), :url => user_groups_url, :id => 'user_groups'},
{:name => t('application_controller.admin_tabs.permissions'), :url => permissions_url, :id => 'permissions'},
]
unless @details_tab = @tabs.find {|t| t[:id] == tab}
@@ -350,7 +350,10 @@ class ApplicationController < ActionController::Base
else
local_perms = @permission_object.permissions
end
- @permissions = paginate_collection(local_perms, params[:page])
+ @permissions = paginate_collection(local_perms.
+ apply_filters(:preset_filter_id => params[:permissions_preset_filter],
+ :search_filter => params[:permissions_search]),
+ params[:page])
@permission_list_header = []
unless (@show_inherited or @show_global)
diff --git a/src/app/controllers/permissions_controller.rb b/src/app/controllers/permissions_controller.rb
index acdbb2c..6d8ae54 100644
--- a/src/app/controllers/permissions_controller.rb
+++ b/src/app/controllers/permissions_controller.rb
@@ -165,11 +165,21 @@ class PermissionsController < ApplicationController
:permission_object_id => p.permission_object_id
end
+ def filter
+ redirect_to_original({"permissions_preset_filter" => params[:permissions_preset_filter], "permissions_search" => params[:permissions_search]})
+ end
+
+ def filter_entities
+ redirect_to_original({"entities_preset_filter" => params[:entities_preset_filter], "entities_search" => params[:entities_search]})
+ end
+
private
def load_entities
sort_order = params[:sort_by].nil? ? "name" : params[:sort_by]
- @entities = paginate_collection(Entity.all(:order => sort_order), params[:page])
+ @entities = paginate_collection(Entity.order(sort_order).
+ apply_filters(:preset_filter_id => params[:entities_preset_filter],
+ :search_filter => params[:entities_search]), params[:page])
end
def load_headers
diff --git a/src/app/controllers/user_groups_controller.rb b/src/app/controllers/user_groups_controller.rb
new file mode 100644
index 0000000..189ec09
--- /dev/null
+++ b/src/app/controllers/user_groups_controller.rb
@@ -0,0 +1,202 @@
+#
+# Copyright 2011 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class UserGroupsController < ApplicationController
+ before_filter :require_user, :except => [:new, :create]
+
+ def index
+ require_privilege(Privilege::VIEW, User)
+ @title = t'user_groups.groups'
+ clear_breadcrumbs
+ save_breadcrumb(user_groups_path)
+ set_admin_users_tabs 'user_groups'
+ @params = params
+ load_headers
+ load_user_groups
+ respond_to do |format|
+ format.html
+ format.js { render :partial => 'list' }
+ end
+ end
+
+ def show
+ @user_group = UserGroup.find(params[:id])
+ require_privilege(Privilege::VIEW, User)
+ @title = @user_group.name
+ save_breadcrumb(user_group_path(@user_group), @user_group.name)
+ @tab_captions = ['Properties']
+ @details_tab = params[:details_tab].blank? ? 'properties' : params[:details_tab]
+ @members = paginate_collection((a)user_group.members, params[:page])
+ respond_to do |format|
+ format.html
+ format.js do
+ if params.delete :details_pane
+ render :partial => 'layouts/details_pane' and return
+ end
+ render :partial => @details_tab
+ end
+ end
+ end
+
+ def new
+ require_privilege(Privilege::CREATE, User)
+ @title = t'user_groups.new.new_user_group'
+ @user_group = UserGroup.new
+ # remove when we enable ldap
+ @user_group.membership_source = UserGroup::MEMBERSHIP_SOURCE_LOCAL
+ end
+
+ def create
+ if params[:commit] == "Reset"
+ redirect_to :action => 'new' and return
+ end
+
+ require_privilege(Privilege::MODIFY, User)
+ @user_group = UserGroup.new(params[:user_group])
+ unless @user_group.save
+ render :action => 'new' and return
+ end
+
+ respond_to do |format|
+ if @user_group.save
+ flash[:notice] = t "user_groups.flash.notice.added"
+ format.html { redirect_to user_groups_path }
+ format.json { render :json => @user_group, :status => :created }
+ else
+ flash.now[:warning] = t "user_groups.flash.warning.creation_failed"
+ format.html { render :new }
+ format.js { render :partial => 'new' }
+ format.json { render :json => @user_group.errors, :status => :unprocessable_entity }
+ end
+ end
+ end
+
+ def edit
+ @user_group = UserGroup.find(params[:id])
+ require_privilege(Privilege::MODIFY, User)
+ @title = t'user_groups.edit.edit_user_group'
+ end
+
+ def update
+ @user_group = UserGroup.find(params[:id])
+ require_privilege(Privilege::MODIFY, User)
+
+ if params[:commit] == "Reset"
+ redirect_to edit_user_group_url(@user_group) and return
+ end
+
+ redirect_to root_url and return unless @user_group
+
+ unless @user_group.update_attributes(params[:user_group])
+ render :action => 'edit' and return
+ else
+ flash[:notice] = t"user_groups.flash.notice.updated"
+ redirect_to user_group_path(@user_group)
+ end
+ end
+
+ def multi_destroy
+ require_privilege(Privilege::MODIFY, User)
+ deleted_user_groups = []
+
+ begin
+ UserGroup.transaction do
+ UserGroup.find(params[:user_group_selected]).each do |user_group|
+ if user_group.destroy
+ deleted_user_groups << user_group.name
+ end
+ end
+ end
+
+ unless deleted_user_groups.empty?
+ flash[:notice] = "#{t('user_groups.flash.notice.more_deleted', :count => deleted_user_groups.length)} #{deleted_user_groups.join(', ')}"
+ end
+
+ rescue => ex
+ flash[:warning] = t('user_groups.flash.warning.not_delete', :reason => ex.message)
+ end
+
+ redirect_to user_groups_url
+ end
+
+ def destroy
+ require_privilege(Privilege::MODIFY, User)
+ user_group = UserGroup.find(params[:id])
+ if user_group.destroy
+ flash[:notice] = t"user_groups.flash.notice.deleted"
+ else
+ flash[:notice] = t"user_groups.flash.warning.delete_failed"
+ end
+
+ respond_to do |format|
+ format.html { redirect_to user_groups_path }
+ end
+ end
+
+ def add_members
+ @user_group = UserGroup.find(params[:id])
+ require_privilege(Privilege::MODIFY, User)
+
+ unless @user_group.membership_source == UserGroup::MEMBERSHIP_SOURCE_LOCAL
+ flash[:error] = t('user_groups.flash.error.not_a_local_group')
+ redirect_to user_group_path(@user_group) and return
+ end
+
+ @users = paginate_collection(User.where('users.id not in (?)',
+ @user_group.members.empty? ?
+ 0 : @user_group.members.map(&:id)),
+ params[:page])
+
+ added = []
+ not_added = []
+
+ if params[:members_selected].blank?
+ flash[:error] = t"user_groups.flash.error.select_to_add_members" if request.post?
+ else
+ User.find(params[:members_selected]).each do |member|
+ if !(a)user_group.members.include?(member) and
+ @user_group.members << member
+ added << member.name
+ else
+ not_added << member.name
+ end
+ end
+ unless added.empty?
+ flash[:notice] = "#{t('user_groups.flash.notice.members_added')}: #{added.join(', ')}"
+ end
+ unless not_added.empty?
+ flash[:error] = "#{t('user_groups.flash.error.members_not_added')}: #{not_added.join(', ')}"
+ end
+ respond_to do |format|
+ format.html { redirect_to user_group_path(@user_group) }
+ end
+ end
+ end
+
+ def load_user_groups
+ sort_order = params[:sort_by].nil? ? "name" : params[:sort_by]
+ @user_groups = UserGroup.apply_filters(:preset_filter_id => params[:users_preset_filter], :search_filter => params[:user_groups_search]).order(sort_order)
+ end
+
+ def load_headers
+ @header = [
+ { :name => 'checkbox', :class => 'checkbox', :sortable => false },
+ { :name => t('user_groups.index.name'), :sortable => false },
+ { :name => t('user_groups.index.type'), :sortable => false },
+ ]
+ end
+
+end
diff --git a/src/app/models/common_filter_methods.rb b/src/app/helpers/user_groups_helper.rb
similarity index 55%
copy from src/app/models/common_filter_methods.rb
copy to src/app/helpers/user_groups_helper.rb
index 7bb2be2..85ad81f 100644
--- a/src/app/models/common_filter_methods.rb
+++ b/src/app/helpers/user_groups_helper.rb
@@ -1,5 +1,5 @@
#
-# Copyright 2011 Red Hat, Inc.
+# Copyright 2012 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,19 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-#this module contains functions for filtering table data
-module CommonFilterMethods
- def apply_filters(options = {})
- apply_preset_filter(options[:preset_filter_id]).apply_search_filter(options[:search_filter])
- end
-
- private
-
- def apply_preset_filter(preset_filter_id)
- if preset_filter_id.present?
- self::PRESET_FILTERS_OPTIONS.select{|item| item[:id] == preset_filter_id}.first[:query]
- else
- scoped
- end
+module UserGroupsHelper
+ def members_header
+ [
+ { :name => 'checkbox', :class => 'checkbox', :sortable => false },
+ { :name => t('users.index.username'), :sortable => false },
+ { :name => t('user_groups.index.name'), :sortable => false },
+ ]
end
end
diff --git a/src/app/models/common_filter_methods.rb b/src/app/models/common_filter_methods.rb
index 7bb2be2..9491ecb 100644
--- a/src/app/models/common_filter_methods.rb
+++ b/src/app/models/common_filter_methods.rb
@@ -23,7 +23,13 @@ module CommonFilterMethods
def apply_preset_filter(preset_filter_id)
if preset_filter_id.present?
- self::PRESET_FILTERS_OPTIONS.select{|item| item[:id] == preset_filter_id}.first[:query]
+ option = self::PRESET_FILTERS_OPTIONS.select { |item|
+ item[:id] == preset_filter_id}.first
+ if option[:query]
+ option[:query]
+ else
+ includes(option[:includes]).where(option[:where])
+ end
else
scoped
end
diff --git a/src/app/models/derived_permission.rb b/src/app/models/derived_permission.rb
index b7a2530..80d055d 100644
--- a/src/app/models/derived_permission.rb
+++ b/src/app/models/derived_permission.rb
@@ -15,6 +15,9 @@
#
class DerivedPermission < ActiveRecord::Base
+ class << self
+ include CommonFilterMethods
+ end
# the source permission for the denormalized object
belongs_to :permission
validates_presence_of :permission_id
@@ -54,4 +57,24 @@ class DerivedPermission < ActiveRecord::Base
validates_uniqueness_of :permission_id, :scope => [:permission_object_id,
:permission_object_type]
+ # :query is handled differently for permission
+ PRESET_FILTERS_OPTIONS = [
+ {:title => "permissions.preset_filters.user_permissions",
+ :id => "user_permissions",
+ :includes => :entity,
+ :where => {"entities.entity_target_type" => "User"}},
+ {:title => "permissions.preset_filters.group_permissions",
+ :id => "group_permissions",
+ :includes => :entity,
+ :where => {"entities.entity_target_type" => "UserGroup"}}
+ ]
+
+ def self.apply_search_filter(search)
+ if search
+ includes("entity").where("lower(entities.name) LIKE :search", :search => "%#{search.downcase}%")
+ else
+ scoped
+ end
+ end
+
end
diff --git a/src/app/models/entity.rb b/src/app/models/entity.rb
index bbc56fe..db88a5e 100644
--- a/src/app/models/entity.rb
+++ b/src/app/models/entity.rb
@@ -15,6 +15,9 @@
#
class Entity < ActiveRecord::Base
+ class << self
+ include CommonFilterMethods
+ end
belongs_to :entity_target, :polymorphic => true
validates_presence_of :entity_target_id
has_many :session_entities, :dependent => :destroy
@@ -26,4 +29,20 @@ class Entity < ActiveRecord::Base
belongs_to :user_group, :class_name => "UserGroup",
:foreign_key => "entity_target_id"
+ PRESET_FILTERS_OPTIONS = [
+ {:title => "user",
+ :id => "users",
+ :where => {"entities.entity_target_type" => "User"}},
+ {:title => "user_group",
+ :id => "user_groups",
+ :where => {"entities.entity_target_type" => "UserGroup"}}
+ ]
+
+ def self.apply_search_filter(search)
+ if search
+ where("lower(entities.name) LIKE :search", :search => "%#{search.downcase}%")
+ else
+ scoped
+ end
+ end
end
diff --git a/src/app/models/permission.rb b/src/app/models/permission.rb
index 048f7a5..9b4c0ac 100644
--- a/src/app/models/permission.rb
+++ b/src/app/models/permission.rb
@@ -30,6 +30,9 @@
#
class Permission < ActiveRecord::Base
+ class << self
+ include CommonFilterMethods
+ end
belongs_to :role
belongs_to :entity
@@ -67,6 +70,17 @@ class Permission < ActiveRecord::Base
after_save :update_derived_permissions
+ # :query is handled differently for permission
+ PRESET_FILTERS_OPTIONS = [
+ {:title => "permissions.preset_filters.user_permissions",
+ :id => "user_permissions",
+ :includes => :entity,
+ :where => {"entities.entity_target_type" => "User"}},
+ {:title => "permissions.preset_filters.group_permissions",
+ :id => "group_permissions",
+ :includes => :entity,
+ :where => {"entities.entity_target_type" => "UserGroup"}}
+ ]
def user
entity.user
end
@@ -100,4 +114,13 @@ class Permission < ActiveRecord::Base
end
end
end
+
+ def self.apply_search_filter(search)
+ if search
+ includes("entity").where("lower(entities.name) LIKE :search", :search => "%#{search.downcase}%")
+ else
+ scoped
+ end
+ end
+
end
diff --git a/src/app/models/user_group.rb b/src/app/models/user_group.rb
index 293d0db..dcb2f63 100644
--- a/src/app/models/user_group.rb
+++ b/src/app/models/user_group.rb
@@ -15,6 +15,9 @@
#
class UserGroup < ActiveRecord::Base
+ class << self
+ include CommonFilterMethods
+ end
# name will correspond to the group name if we're using LDAP, otherwise it's
# entered by the admin creating the group
@@ -42,4 +45,13 @@ class UserGroup < ActiveRecord::Base
self.entity.name = "#{self.name} (#{self.membership_source})"
self.entity.save!
end
+
+ def self.apply_search_filter(search)
+ if search
+ where("lower(name) LIKE :search", :search => "%#{search.downcase}%")
+ else
+ scoped
+ end
+ end
+
end
diff --git a/src/app/views/permissions/_form.html.haml b/src/app/views/permissions/_form.html.haml
index 95aa05d..39b1865 100644
--- a/src/app/views/permissions/_form.html.haml
+++ b/src/app/views/permissions/_form.html.haml
@@ -1,3 +1,23 @@
+- content_for :entities_filter_controls do
+ %li
+ = label_tag :entities_preset_filter, t('filter_table.viewing')
+ = select_tag(:entities_preset_filter, preset_filters_options_for_select(Entity::PRESET_FILTERS_OPTIONS, params[:entities_preset_filter]), :include_blank => t("permissions.preset_filters.all_entities"), :disabled => false)
+ = hidden_field_tag :current_path, request.fullpath
+ = restful_submit_tag t("filter_table.apply_filters"), "filter", filter_entities_permissions_path, 'POST', :class => 'button', :id => 'apply_entities_preset_filter'
+ %span.label.badge.dark= @entities.count
+ %li.table-search-filter
+ = text_field_tag :entities_search, params[:entities_search], :placeholder => t("filter_table.search")
+ = restful_submit_tag "", "search", filter_entities_permissions_path, 'POST', :class => 'button', :id => 'apply_entities_search'
+
+ :javascript
+ $(document).ready(function(){
+ $("#apply_entities_preset_filter").hide();
+ $("#entities_preset_filter").change(function() {
+ $("#apply_entities_preset_filter").click();
+ });
+ Conductor.clickOnEnterKeypress($('#entities_search'), $('#apply_entities_search'));
+ });
+
- content_for :form_footer do
= hidden_field_tag :permission_object_type, @permission_object.class.name
= hidden_field_tag :permission_object_id, @permission_object.id
@@ -5,7 +25,8 @@
= hidden_field_tag :use_tabs, @use_tabs
= link_to t('cancel'), @return_path, :class => 'button danger'
= restful_submit_tag t('permissions.form.grant_access'), "create", permissions_path, 'POST', :id => 'save_button', :class => 'button'
-= filter_table(@header, @entities) do |entity|
+= filter_table(@header, @entities,
+ :filter_controls => :entities_filter_controls) do |entity|
%tr{:class => cycle('nostripe','stripe')}
%td
-# - selected = params[:select] == 'all'
diff --git a/src/app/views/permissions/_permissions.html.haml b/src/app/views/permissions/_permissions.html.haml
index b1e18f7..56a0c8e 100644
--- a/src/app/views/permissions/_permissions.html.haml
+++ b/src/app/views/permissions/_permissions.html.haml
@@ -24,6 +24,26 @@
%li= link_to t('permissions.inherited_access'), params.merge(:show_inherited => true, :show_global => false, :page => 1), { :class => 'button primary', :id => 'inherited_permission_button'}
%li= link_to t('permissions.global_access'), params.merge(:show_inherited => false, :show_global => true, :page => 1), { :class => 'button primary', :id => 'global_permission_button'}
+- content_for :permissions_filter_controls do
+ %li
+ = label_tag :permissions_preset_filter, t('filter_table.viewing')
+ = select_tag(:permissions_preset_filter, preset_filters_options_for_select(Permission::PRESET_FILTERS_OPTIONS, params[:permissions_preset_filter]), :include_blank => t("permissions.preset_filters.all_permissions"), :disabled => false)
+ = hidden_field_tag :current_path, request.fullpath
+ = restful_submit_tag t("filter_table.apply_filters"), "filter", filter_permissions_path, 'POST', :class => 'button', :id => 'apply_permissions_preset_filter'
+ %span.label.badge.dark= @permissions.count
+ %li.table-search-filter
+ = text_field_tag :permissions_search, params[:permissions_search], :placeholder => t("filter_table.search")
+ = restful_submit_tag "", "search", filter_permissions_path, 'POST', :class => 'button', :id => 'apply_permissions_search'
+
+ :javascript
+ $(document).ready(function(){
+ $("#apply_permissions_preset_filter").hide();
+ $("#permissions_preset_filter").change(function() {
+ $("#apply_permissions_preset_filter").click();
+ });
+ Conductor.clickOnEnterKeypress($('#permissions_search'), $('#apply_permissions_search'));
+ });
+
= filter_table(@permission_list_header,
@permissions,
:form_header => :permissions_form_header,
diff --git a/src/app/views/user_groups/_form.html.haml b/src/app/views/user_groups/_form.html.haml
new file mode 100644
index 0000000..1db6a7a
--- /dev/null
+++ b/src/app/views/user_groups/_form.html.haml
@@ -0,0 +1,26 @@
+- if @user_group.errors.any?
+ = render 'layouts/error_messages', :object => @user_group
+
+%fieldset
+ .field
+ = form.label :type
+ .input
+ - if @user_group.new_record?
+ = form.select(:membership_source, UserGroup::MEMBERSHIP_SOURCES.collect {|x| [t("user_groups.#{x.downcase}"), x]}, {}, {:disabled => true})
+ = form.hidden_field :membership_source
+ - else
+ = t("user_groups.#{(a)user_group.membership_source.downcase}")
+ .field
+ = form.label :name
+ .input
+ - if @user_group.new_record? or @user_group.membership_source == UserGroup::MEMBERSHIP_SOURCE_LOCAL
+ = form.text_field :name
+ - else
+ = @user_group.name
+ .field
+ = form.label :description
+ .input
+ = form.text_area :description, :class => 'long'
+
+%fieldset.options
+ = form.submit "#{t'user_groups.form.save_user_group'}", :class => "submit button pill"
diff --git a/src/app/views/user_groups/_list.html.haml b/src/app/views/user_groups/_list.html.haml
new file mode 100644
index 0000000..5c99b39
--- /dev/null
+++ b/src/app/views/user_groups/_list.html.haml
@@ -0,0 +1,41 @@
+%header
+ %h2#user_groups.groups= @title
+
+- content_for :form_header do
+ %li= restful_submit_tag "#{t'user_groups.list.delete_selected'}", "destroy", multi_destroy_user_groups_path, 'DELETE', :id => 'delete_button', :class => 'button danger'
+ %li= link_to t("user_groups.list.add_user_group"), new_user_group_path, :class => 'button', :id => 'add_user_group_button'
+
+- content_for :filter_controls do
+ %li
+ = label_tag :user_groups_preset_filter, t('filter_table.viewing')
+ = hidden_field_tag :current_path, request.fullpath
+ = select_tag(:user_groups_preset_filter, preset_filters_options_for_select(User::PRESET_FILTERS_OPTIONS, params[:user_groups_preset_filter]), :include_blank => t("user_groups.preset_filters.all_user_groups"), :disabled => true)
+ = restful_submit_tag t("filter_table.apply_filters"), "index", filter_user_groups_path, 'POST', :class => 'button', :id => 'apply_user_groups_preset_filter'
+ %span.label.badge.dark= @user_groups.count
+ / = link_to "Filter", "#", :class => 'button pill'
+ /%li
+ /%li.more_actions
+ / %span= image_tag "button-userdrop.png"
+ / %ul
+ / %li= link_to "Add/Remove Columns", "#"
+ / %li= link_to "Freeze Column", "#"
+ %li.table-search-filter
+ = text_field_tag :user_groups_search, params[:user_groups_search], :placeholder => t("filter_table.search")
+ = restful_submit_tag "", "index", filter_user_groups_path, 'POST', :class => 'button', :id => 'apply_user_groups_search'
+
+ :javascript
+ $(document).ready(function(){
+ $("#apply_user_groups_preset_filter").hide();
+ $("#user_groups_preset_filter").change(function() {
+ $("#apply_user_groups_preset_filter").click();
+ });
+ Conductor.clickOnEnterKeypress($('#user_groups_search'), $('#apply_user_groups_search'));
+ });
+
+= filter_table(@header, @user_groups) do |user_group|
+ %tr{:class => cycle('nostripe','stripe')}
+ %td{:class => 'checkbox'}
+ - selected = params[:select] == 'all'
+ = check_box_tag "user_group_selected[]", user_group.id, selected, :id => "user_group_checkbox_#{user_group.id}"
+ %td= link_to user_group.name, user_group_path(user_group)
+ %td= t("user_groups.#{user_group.membership_source.downcase}")
diff --git a/src/app/views/user_groups/add_members.html.haml b/src/app/views/user_groups/add_members.html.haml
new file mode 100644
index 0000000..4b3a30c
--- /dev/null
+++ b/src/app/views/user_groups/add_members.html.haml
@@ -0,0 +1,25 @@
+= render :partial => 'layouts/admin_nav'
+%header.page-header
+ .obj_actions
+ .return_to
+ = t'return_to'
+ = link_to @user_group.name, user_group_path(@user_group), :id => 'user_group_button'
+ %h1.user_groups= @user_group.name
+
+%section.content-section.user_groups
+ %header
+ .align-center
+ %strong= t("user_groups.choose_members")
+ .content
+ - content_for :form_footer do
+ = link_to t('cancel'), user_group_path(@user_group), :class => 'button danger'
+ = restful_submit_tag t('user_groups.show.add_members'), "add", add_members_user_group_path(@user_group), 'POST', :id => 'save_button', :class => 'button'
+ = filter_table(members_header, @users) do |user|
+ %tr{:class => cycle('nostripe','stripe')}
+ %td{:class => 'checkbox'}
+ - selected = params[:select] == 'all'
+ = check_box_tag "members_selected[]", user.id, selected, :id => "member_checkbox_#{user.id}"
+ %td
+ = link_to user.login, users_path(user)
+ %td
+ = user.name
diff --git a/src/app/views/user_groups/edit.html.haml b/src/app/views/user_groups/edit.html.haml
new file mode 100644
index 0000000..7350a43
--- /dev/null
+++ b/src/app/views/user_groups/edit.html.haml
@@ -0,0 +1,14 @@
+= render :partial => 'layouts/admin_nav'
+%header.page-header
+ .obj_actions
+ .return_to
+ =t'return_to'
+ = link_to "#{t'user_groups.groups'}", user_groups_path
+ %h1.users= @title
+
+%section.content-section.user_groups
+ .content
+ .align-center
+ = @user_group.name
+ = form_for @user_group, :url => user_group_path(@user_group), :html => {:class => 'generic horizontal'} do |f|
+ = render :partial => "form", :locals => { :form => f }
diff --git a/src/app/views/user_groups/index.html.haml b/src/app/views/user_groups/index.html.haml
new file mode 100644
index 0000000..7cc23db
--- /dev/null
+++ b/src/app/views/user_groups/index.html.haml
@@ -0,0 +1,2 @@
+= render :partial => 'layouts/admin_nav'
+= render :partial => 'layouts/admin_users_tabs'
diff --git a/src/app/views/user_groups/new.html.haml b/src/app/views/user_groups/new.html.haml
new file mode 100644
index 0000000..eaaf3a7
--- /dev/null
+++ b/src/app/views/user_groups/new.html.haml
@@ -0,0 +1,13 @@
+= render :partial => 'layouts/admin_nav'
+%header.page-header
+ .obj_actions
+ .return_to
+ =t'return_to'
+ = link_to "#{t'user_groups.groups'}", user_groups_path
+ %h1.users= @title
+
+%section.content-section.user_groups
+ .content
+ .align-center
+ = form_for @user_group, :url => user_groups_path, :html => {:class => 'generic horizontal'} do |f|
+ = render :partial => "form", :locals => { :form => f }
diff --git a/src/app/views/user_groups/show.html.haml b/src/app/views/user_groups/show.html.haml
new file mode 100644
index 0000000..426137f
--- /dev/null
+++ b/src/app/views/user_groups/show.html.haml
@@ -0,0 +1,71 @@
+= render :partial => 'layouts/admin_nav'
+%header.page-header
+ .obj_actions
+ .return_to
+ = t'return_to'
+ = link_to t("user_groups.groups"), user_groups_path
+ - if check_privilege(Privilege::CREATE, User)
+ = link_to t('user_groups.new.new_user_group'), new_user_group_url, :class => 'button primary', :id => 'new_user_group_button'
+ .button-group
+ - if check_privilege(Privilege::MODIFY, User)
+ = link_to t('edit'), edit_user_group_path(@user_group), :class => 'button pill', :id => 'edit_button'
+ = button_to t("delete"), user_group_path(@user_group), :method => 'delete', :confirm => "Are you sure you want to delete?", :class => 'button pill danger', :id => 'delete'
+ %h1.no-icon= @title
+
+%section.content-section.user
+ %header
+ %h2=t'properties'
+
+ .content
+ %table.properties_table
+ %tbody
+ %tr
+ %td= t('user_groups.show.type')
+ %td= @user_group.membership_source
+ %tr
+ %td= t('user_groups.show.name')
+ %td= @user_group.name
+ %tr
+ %td= t('user_groups.show.description')
+ %td= @user_group.description
+
+- if @user_group.membership_source == UserGroup::MEMBERSHIP_SOURCE_LOCAL
+ %section.content-section
+ %header
+ %h2=t('user_groups.show.members')
+
+ .content
+ - content_for :form_header do
+ - if check_privilege(Privilege::CREATE, Deployable)
+ %li= restful_submit_tag t("remove"), "destroy", remove_members_user_group_path(@user_group), 'DELETE', :id => 'delete_button', :class => 'button danger'
+ %li= link_to t("user_groups.show.add_members"), add_members_user_group_path(@user_group), :class => 'button', :id => 'add_members_button'
+
+ - content_for :filter_controls do
+ %li
+ = label_tag :members_preset_filter, t('filter_table.viewing')
+ = hidden_field_tag :current_path, request.fullpath
+ = select_tag(:members_preset_filter, preset_filters_options_for_select(Deployable::PRESET_FILTERS_OPTIONS, params[:members_preset_filter]), :include_blank => t("users.preset_filters.all_users"), :disabled => true)
+ = restful_submit_tag t("filter_table.apply_filters"), "filter", filter_members_user_group_path(@user_group), 'POST', :class => 'button', :id => 'apply_members_preset_filter'
+ %span.label.badge.dark= @user_group.members.count
+ %li.table-search-filter
+ = text_field_tag :members_search, params[:members_search], :placeholder => t("filter_table.search")
+ = restful_submit_tag "", "search", filter_members_user_group_path(@user_group), 'POST', :class => 'button', :id => 'apply_members_search'
+
+ :javascript
+ $(document).ready(function(){
+ $("#apply_members_preset_filter").hide();
+ $("#members_preset_filter").change(function() {
+ $("#apply_members_preset_filter").click();
+ });
+ Conductor.clickOnEnterKeypress($('#members_search'), $('#apply_members_search'));
+ });
+
+ = filter_table(members_header, @members) do |member|
+ %tr{:class => cycle('nostripe','stripe')}
+ %td{:class => 'checkbox'}
+ - selected = params[:select] == 'all'
+ = check_box_tag "members_selected[]", member.id, selected, :id => "member_checkbox_#{member.id}"
+ %td
+ = link_to member.login, users_path(member)
+ %td
+ = member.name
diff --git a/src/config/locales/en.yml b/src/config/locales/en.yml
index 8c2e010..19b825a 100644
--- a/src/config/locales/en.yml
+++ b/src/config/locales/en.yml
@@ -102,6 +102,62 @@ en:
name_starts_with_B: "Name starts with B"
errors:
has_running_instances: '%{login} has running instances'
+ user_groups:
+ groups: "User Groups"
+ local: Local
+ ldap: LDAP
+ return_to: "Return to:"
+ confirm_delete: "Are you sure you want to delete this user group?"
+ quick_jump: "Quick Jump:"
+ choose_members: "Choose Users to add to this Group."
+ index:
+ name: Name
+ type: Type
+ show:
+ name: Name
+ type: Type
+ description: Description
+ members: Members
+ add_members: Add Members
+ edit: Edit
+ delete: Delete
+ created: CREATED
+ last_updated: LAST UPDATED
+ form:
+ name: Name
+ type: Type
+ save_user_group: Save User Group
+ new:
+ new_user_group: New User Group
+ neccessary_information: "Enter necessary information about your new user group and click 'Save User Group' when you are finished."
+ list:
+ delete_selected: Delete Selected
+ add_user_group: Add User Group
+ edit:
+ edit_user_group: Edit User Group
+ flash:
+ notice:
+ added: "User Group added"
+ updated: "User Group updated!"
+ deleted: "User Group has been successfully deleted."
+ more_deleted:
+ one: Deleted user group
+ other: Deleted user groups
+ members_added: "These users have been added"
+ members_removed: "These users have been removed"
+ warning:
+ creation_failed: 'Could not create user group'
+ delete_failed: 'Could not delete user group'
+ not_delete: 'Cannot delete: %{reason}'
+ error:
+ members_not_added: "Could not add these users"
+ members_not_removed: "Could not remove these users"
+ not_a_local_group: 'Only locally-managed groups can be managed here.'
+ select_to_add_members: 'You must select at least one user to add.'
+ preset_filters:
+ all_user_groups: "All User Groups"
+ local: "Locally-managed User Groups"
+ ldap: "LDAP-managed User Groups"
user_sessions:
new:
username: "Username:"
@@ -151,6 +207,7 @@ en:
role: Role
role_assignments: Role Assignments
user: User
+ user_group: User Group
privileges: Privileges
general_settings: General Settings
new_user: New User
@@ -1285,7 +1342,10 @@ en:
user_and_role: "%{user} (%{role})"
user_and_role_change: "%{user} (from %{old_role} to %{role})"
preset_filters:
+ all_entities: All Entities
all_permissions: All Permissions
+ user_permissions: User Permissions
+ group_permissions: Group Permissions
quotas:
quota: Quota
edit:
@@ -1353,7 +1413,7 @@ en:
realms: Realms
hardware: Hardware
users: Users
- groups: Groups
+ user_groups: User Groups
permissions: Global Role Grants
pool_families: Pool Families
images: Images
diff --git a/src/config/routes.rb b/src/config/routes.rb
index da762a7..38fbb53 100644
--- a/src/config/routes.rb
+++ b/src/config/routes.rb
@@ -83,6 +83,8 @@ Conductor::Application.routes.draw do
get :list
delete :multi_destroy
post :multi_update
+ post :filter
+ post :filter_entities
end
end
@@ -156,6 +158,19 @@ Conductor::Application.routes.draw do
post :filter, :on => :collection
end
+ resources :user_groups do
+ collection do
+ delete 'multi_destroy'
+ post :filter
+ end
+ member do
+ get 'add_members'
+ post 'add_members'
+ post 'remove_members'
+ post 'filter_members'
+ end
+ end
+
resources :logs do
collection do
post 'filter'
diff --git a/src/app/models/entity.rb b/src/features/step_definitions/user_group_steps.rb
similarity index 52%
copy from src/app/models/entity.rb
copy to src/features/step_definitions/user_group_steps.rb
index bbc56fe..b5426f2 100644
--- a/src/app/models/entity.rb
+++ b/src/features/step_definitions/user_group_steps.rb
@@ -13,17 +13,23 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+Given /^a user group "([^"]*)" exists$/ do |name|
+ unless UserGroup.find_by_name(name)
+ FactoryGirl.create :user_group, :name => name
+ end
+end
-class Entity < ActiveRecord::Base
- belongs_to :entity_target, :polymorphic => true
- validates_presence_of :entity_target_id
- has_many :session_entities, :dependent => :destroy
- has_many :permissions, :dependent => :destroy
- has_many :derived_permissions, :dependent => :destroy
+Given /^there is a user group "([^"]*)"$/ do |name|
+ unless UserGroup.find_by_name(name)
+ FactoryGirl.create :user_group, :name => name
+ end
+end
- # type-specific associations
- belongs_to :user, :class_name => "User", :foreign_key => "entity_target_id"
- belongs_to :user_group, :class_name => "UserGroup",
- :foreign_key => "entity_target_id"
+Then /^there should be (\d+) user groups?$/ do |number|
+ UserGroup.count.should == number.to_i
+end
+When /^I check "([^"]*)" user group$/ do |user_group_name|
+ user_group = UserGroup.find_by_name(user_group_name)
+ check("user_group_checkbox_#{user_group.id}")
end
diff --git a/src/features/support/paths.rb b/src/features/support/paths.rb
index 38f926d..f809a8c 100644
--- a/src/features/support/paths.rb
+++ b/src/features/support/paths.rb
@@ -35,6 +35,9 @@ module NavigationHelpers
when /^(.*)'s user page$/i
user_path(User.find_by_login($1))
+ when /^(.*)'s user group page$/i
+ user_group_path(UserGroup.find_by_name($1))
+
when /^(.*)'s role page$/i
role_path(Role.find_by_name($1))
@@ -179,6 +182,9 @@ module NavigationHelpers
when /^the (.*)'s edit user page$/
edit_user_path(User.find_by_login($1))
+ when /^the (.*)'s edit user group page$/
+ edit_user_group_path(UserGroup.find_by_name($1))
+
when /^the "(.*)" catalog page/
url_for catalog_path(Catalog.find_by_name($1))
diff --git a/src/features/user_group.feature b/src/features/user_group.feature
new file mode 100644
index 0000000..d858035
--- /dev/null
+++ b/src/features/user_group.feature
@@ -0,0 +1,54 @@
+Feature: Manage User Groups
+ In order to manage user groups
+ As an admin
+ I want to add/edit/remove user groupss
+
+ Background:
+ Given I am an authorised user
+ And I am logged in
+ And a user group "testgroup" exists
+
+ Scenario: Show user group detials
+ Given I am on the user groups page
+ And there is a user group "testgroup"
+ When I follow "testgroup"
+ Then I should be on testgroup's user group page
+
+ Scenario: Delete user groups
+ Given there is a user group "testgroup"
+ Given there is a user group "testgroup2"
+ And I am on the user groups page
+ Then there should be 2 user groups
+ And I should be on the user groups page
+ When I check "testgroup" user group
+ And I press "Delete"
+ Then I should see "Deleted user group"
+ And there should be 1 user group
+
+ Scenario: Create new user
+ Given I am on the user groups page
+ When I follow "add_user_group_button"
+ Then I should be on the new user group page
+ And I should see "New User Group"
+ When I fill in the following:
+ | Name | testgroup3 |
+ And I press "Save"
+ Then I should be on the user groups page
+ And I should see "User Group added"
+
+ Scenario: Edit existing user group
+ Given I am on the user groups page
+ And I follow "testgroup"
+ Then I should be on testgroup's user group page
+ And I should see "testgroup"
+ When I follow "Edit"
+ Then I should be on the testgroup's edit user group page
+ And I fill in "user_group_name" with "newname"
+ When I press "Save"
+ Then I should be on newname's user group page
+ And I should see "User Group updated"
+ And I should see "newname"
+
+
+
+
diff --git a/src/spec/controllers/user_groups_controller_spec.rb b/src/spec/controllers/user_groups_controller_spec.rb
new file mode 100644
index 0000000..d5e68bb
--- /dev/null
+++ b/src/spec/controllers/user_groups_controller_spec.rb
@@ -0,0 +1,75 @@
+#
+# Copyright 2011 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe UserGroupsController do
+
+ fixtures :all
+ before(:each) do
+ @user_group = Factory.create(:user_group)
+ @tuser = FactoryGirl.create :tuser
+ @admin_permission = FactoryGirl.create :admin_permission
+ @admin = @admin_permission.user
+ end
+
+ describe "#create" do
+ context "user enters valid input" do
+ it "creates user group" do
+ mock_warden(@admin)
+ lambda do
+ post :create, :user_group => {
+ :name => "user_group2", :membership_source => "local" }
+ end.should change(UserGroup, :count).by(1)
+
+ response.should redirect_to(user_groups_path)
+ end
+
+ it "fails to create user" do
+ mock_warden(@admin)
+ lambda do
+ post :create, :user_group => {}
+ end.should_not change(UserGroup, :count)
+
+ returned_user_group = assigns[:user_group]
+ returned_user_group.errors.empty?.should be_false
+ returned_user_group.should have(1).errors_on(:name)
+ returned_user_group.should have(2).errors_on(:membership_source)
+
+ response.should render_template('new')
+ end
+ end
+ end
+
+ it "allows an admin to create user group" do
+ mock_warden(@admin)
+ lambda do
+ post :create, :user_group => {
+ :name => "user_group3", :membership_source => "local" }
+ end.should change(UserGroup, :count)
+
+ response.should redirect_to(user_groups_url)
+ end
+
+ it "should not allow a regular user to create user group" do
+ mock_warden(@tuser)
+ lambda do
+ post :create, :user_group => {
+ :name => "user_group4", :membership_source => "local" }
+ end.should_not change(UserGroup, :count)
+ end
+
+end
diff --git a/src/spec/factories/permission.rb b/src/spec/factories/permission.rb
index e401fe8..4b2d2b4 100644
--- a/src/spec/factories/permission.rb
+++ b/src/spec/factories/permission.rb
@@ -62,4 +62,11 @@ FactoryGirl.define do
entity { |r| FactoryGirl.create(:pool_family_user).entity }
end
+ factory :group_admin_permission, :parent => :permission do
+ role { |r| Role.first(:conditions => ['name = ?', 'base.admin']) || FactoryGirl.create(:role, :name => 'base.admin') }
+ permission_object { |r| BasePermissionObject.general_permission_scope }
+ entity { |r| FactoryGirl.create(:user_group).entity }
+ end
+
+
end
diff --git a/src/app/models/common_filter_methods.rb b/src/spec/factories/user_group.rb
similarity index 56%
copy from src/app/models/common_filter_methods.rb
copy to src/spec/factories/user_group.rb
index 7bb2be2..bf1f6fc 100644
--- a/src/app/models/common_filter_methods.rb
+++ b/src/spec/factories/user_group.rb
@@ -1,5 +1,5 @@
#
-# Copyright 2011 Red Hat, Inc.
+# Copyright 2012 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,19 +13,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-#this module contains functions for filtering table data
-module CommonFilterMethods
- def apply_filters(options = {})
- apply_preset_filter(options[:preset_filter_id]).apply_search_filter(options[:search_filter])
- end
- private
+FactoryGirl.define do
+
+ factory :user_group do |u|
+ sequence(:name) { |n| "group#{n}" }
+ membership_source 'local'
+ end
- def apply_preset_filter(preset_filter_id)
- if preset_filter_id.present?
- self::PRESET_FILTERS_OPTIONS.select{|item| item[:id] == preset_filter_id}.first[:query]
- else
- scoped
- end
+ factory :ldap_group , :parent => :user_group do
+ membership_source = 'LDAP'
end
+
end
diff --git a/src/spec/models/permission_spec.rb b/src/spec/models/permission_spec.rb
index 7f05587..ea9a20d 100644
--- a/src/spec/models/permission_spec.rb
+++ b/src/spec/models/permission_spec.rb
@@ -84,4 +84,23 @@ describe Permission do
should be_false
end
+ it "User added to Admin group should be able to create users" do
+ newuser = FactoryGirl.create(:user)
+ group_admin_permission = FactoryGirl.create(:group_admin_permission)
+ user_group = group_admin_permission.user_group
+ SessionEntity.update_session(@session_id, newuser)
+ BasePermissionObject.general_permission_scope.has_privilege(@session_id,
+ newuser,
+ Privilege::CREATE,
+ User).should be_false
+ user_group.members << newuser
+ newuser.reload
+ SessionEntity.update_session(@session_id, newuser)
+ BasePermissionObject.general_permission_scope.has_privilege(@session_id,
+ newuser,
+ Privilege::CREATE,
+ User).should be_true
+
+ end
+
end
diff --git a/src/spec/models/user_group_spec.rb b/src/spec/models/user_group_spec.rb
new file mode 100644
index 0000000..915df0a
--- /dev/null
+++ b/src/spec/models/user_group_spec.rb
@@ -0,0 +1,43 @@
+#
+# Copyright 2012 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe UserGroup do
+ before(:each) do
+ end
+
+ it "should create a new local group" do
+ user_group = Factory.create(:user_group)
+ user_group.should be_valid
+ user_group.members.size.should == 0
+ end
+
+ it "should create a new ldap group" do
+ user_group = Factory.create(:ldap_group)
+ user_group.should be_valid
+ end
+
+ it "should add members to a new local group" do
+ user_group = Factory.create(:user_group)
+ user = Factory.create(:tuser)
+ user_group.members.size.should == 0
+ user_group.members << user
+ user_group.reload
+ user_group.members.size.should == 1
+ end
+
+end
--
1.7.6.5
11 years, 10 months
Audrey server -> should be ok on i386?
by Justin Clift
Hi Dan,
Should Audrey's (config) server run ok on i386 hosts?
Thinking that unlike other parts of the Aeolus stack - which we "advertise"
as 64-bit only - Audrey probably doesn't have to be.
Being able to run on i386 vs just x64 *might* help keep deployment costs
minimised.
Even though EC2 Micro instances can be either 32 or 64 bit, other cloud
providers may be different. (no idea personally :>)
+ Justin
--
Aeolus Community Manager
http://www.aeolusproject.org
11 years, 10 months
[PATCH conductor] Fix ApplicationController#ids_list
by Imre Farkas
From: Imre Farkas <ifarkas(a)redhat.com>
The String#each method does not exist in Ruby 1.9
---
src/app/controllers/application_controller.rb | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/app/controllers/application_controller.rb b/src/app/controllers/application_controller.rb
index aed6b7e..a090a17 100644
--- a/src/app/controllers/application_controller.rb
+++ b/src/app/controllers/application_controller.rb
@@ -117,10 +117,11 @@ class ApplicationController < ActionController::Base
# Returns an array of ids from params[:id], params[:ids].
def ids_list(other_attrs=[])
- other_attrs.each do |attr_key|
- return Array(params[attr_key]) if params.include?(attr_key)
- end
- if params[:id].present?
+ if other_attrs.is_a?(Enumerable)
+ other_attrs.each do |attr_key|
+ return Array(params[attr_key]) if params.include?(attr_key)
+ end
+ elsif params[:id].present?
return Array(params[:id])
elsif params[:ids].present?
return Array(params[:ids])
--
1.7.10.2
11 years, 10 months
[PATCH conductor+configure 0/2] Yet another take on admin
by John Eckersberg
These patches move creation of the default admin user from configure
into conductor via db seeds. The problem with doing it in configure
is ensuring that the admin user doesn't get recreated on multiple
invocations. If the user modifies the user (say, change username from
'admin' to 'root') then configure will note that admin does not exist
and recreate it, but this is not what the user wants. Alternatively,
it was discussed to have configure only create the user configured in
the admin profile only if no other admin user already exists. This
however would be confusing to a user who believes they can use the
admin profile to create arbitrary admin users (a not-unreasonable
belief). By doing the creation with db seeds, we ensure that the
admin user is always present (no more "oh i forgot -p admin") but is
only created once - during initial setup.
BZ#806001 - aeolus-configure will always create an admin user, need to key of a uuid not name
https://bugzilla.redhat.com/show_bug.cgi?id=806001
11 years, 10 months