Scott Seago
2008-Aug-04 19:16 UTC
[Ovirt-devel] [PATCH] permission denormalization. (revised)
Permission records are now stored for all pools a user has access to, even
implied/inherited permissions. This means that if an admin is granted access to
the 'default' pool, there will be one permission record for this pool,
and an additional "inherited" record for each pool below
'default' in the hierarchy -- for the rest of these records,
inherited_from_id will point to the master permission record.
Any time a user is granted permission on a pool, the inherited records are
automatically created. When a new pool is created, inherited records are created
here too.
You need to run 'rake db:migrate' to get the updated permissions table
-- the migration script will take care of creating inherited records for
existing permissions.
In the UI, both direct and inherited grants are shown, but only direct grants
have the checkbox to allow modification/deletion.
Signed-off-by: Scott Seago <sseago at redhat.com>
---
wui/src/app/controllers/hardware_controller.rb | 2 +-
wui/src/app/controllers/permission_controller.rb | 14 +++---
wui/src/app/controllers/resources_controller.rb | 2 +-
wui/src/app/models/permission.rb | 42 ++++++++++++++++-
wui/src/app/models/pool.rb | 35 ++++++++++-----
wui/src/app/views/user/_grid.rhtml | 51 +++++++++++---------
wui/src/db/migrate/012_denormalize_permissions.rb | 32 +++++++++++++
7 files changed, 133 insertions(+), 45 deletions(-)
create mode 100644 wui/src/db/migrate/012_denormalize_permissions.rb
diff --git a/wui/src/app/controllers/hardware_controller.rb
b/wui/src/app/controllers/hardware_controller.rb
index e105f7c..5f473db 100644
--- a/wui/src/app/controllers/hardware_controller.rb
+++ b/wui/src/app/controllers/hardware_controller.rb
@@ -151,7 +151,7 @@ class HardwareController < ApplicationController
def users_json
json_list(@pool.permissions,
- [:id, :uid, :user_role])
+ [:grid_id, :uid, :user_role, :source])
end
def storage_pools_json
diff --git a/wui/src/app/controllers/permission_controller.rb
b/wui/src/app/controllers/permission_controller.rb
index 14358de..4367275 100644
--- a/wui/src/app/controllers/permission_controller.rb
+++ b/wui/src/app/controllers/permission_controller.rb
@@ -59,10 +59,11 @@ class PermissionController < ApplicationController
flash[:notice] = 'You do not have permission to create this
permission record'
redirect_to_parent
else
- if @permission.save
+ begin
+ @permission.save_with_new_children
render :json => "created User Permissions for
#{@permission.uid}".to_json
- else
- # FIXME: need to handle proper error messages w/ ajax
+ rescue
+ # FIXME: need to handle proper error messages w/ ajax
render :action => 'new'
end
end
@@ -79,13 +80,12 @@ class PermissionController < ApplicationController
Permission.transaction do
permissions = Permission.find(:all, :conditions => "id in
(#{permission_ids.join(', ')})")
permissions.each do |permission|
- permission.user_role = role
- permission.save!
+ permission.update_role(role) if permission.is_primary?
end
end
render :json => { :object => "permission", :success =>
true,
:alert => "User roles were successfully updated." }
- rescue
+ rescue
render :json => { :object => "permission", :success =>
false,
:alert => "Error updating user roles: #{$!}" }
end
@@ -101,7 +101,7 @@ class PermissionController < ApplicationController
Permission.transaction do
permissions = Permission.find(:all, :conditions => "id in
(#{permission_ids.join(', ')})")
permissions.each do |permission|
- permission.destroy
+ permission.destroy if permission.is_primary?
end
end
render :json => { :object => "permission", :success =>
true,
diff --git a/wui/src/app/controllers/resources_controller.rb
b/wui/src/app/controllers/resources_controller.rb
index dbf9ad7..20defca 100644
--- a/wui/src/app/controllers/resources_controller.rb
+++ b/wui/src/app/controllers/resources_controller.rb
@@ -97,7 +97,7 @@ class ResourcesController < ApplicationController
def users_json
json_list(@vm_resource_pool.permissions,
- [:id, :uid, :user_role])
+ [:grid_id, :uid, :user_role, :source])
end
def new
diff --git a/wui/src/app/models/permission.rb b/wui/src/app/models/permission.rb
index 74c6c26..ece3da5 100644
--- a/wui/src/app/models/permission.rb
+++ b/wui/src/app/models/permission.rb
@@ -19,8 +19,13 @@
class Permission < ActiveRecord::Base
belongs_to :pool
+ belongs_to :parent_permission, :class_name => "Permission",
+ :foreign_key => "inherited_from_id"
+ has_many :child_permissions, :dependent => :destroy,
+ :class_name => "Permission", :foreign_key =>
"inherited_from_id"
- validates_uniqueness_of :uid, :scope => "pool_id"
+
+ validates_uniqueness_of :uid, :scope => [:pool_id, :inherited_from_id]
ROLE_SUPER_ADMIN = "Super Admin"
ROLE_ADMIN = "Administrator"
@@ -68,5 +73,38 @@ class Permission < ActiveRecord::Base
PRIVILEGES[privilege]
end
-
+ def is_primary?
+ inherited_from_id.nil?
+ end
+ def is_inherited?
+ !is_primary?
+ end
+ def source
+ is_primary? ? "Direct" : "Inherited"
+ end
+ def grid_id
+ id.to_s + "_" + (is_primary? ? "1" : "0")
+ end
+ def update_role(new_role)
+ self.transaction do
+ self.user_role = new_role
+ self.save!
+ child_permissions.each do |permission|
+ permission.user_role = new_role
+ permission.save!
+ end
+ end
+ end
+ def save_with_new_children
+ self.transaction do
+ self.save!
+ pool.all_children.each do |subpool|
+ new_permission = Permission.new({:pool_id => subpool.id,
+ :uid => uid,
+ :user_role => user_role,
+ :inherited_from_id => id})
+ new_permission.save!
+ end
+ end
+ end
end
diff --git a/wui/src/app/models/pool.rb b/wui/src/app/models/pool.rb
index 4aa240b..1870027 100644
--- a/wui/src/app/models/pool.rb
+++ b/wui/src/app/models/pool.rb
@@ -71,18 +71,33 @@ class Pool < ActiveRecord::Base
transaction do
save!
move_to_child_of(parent)
+ parent.permissions.each do |permission|
+ new_permission = Permission.new({:pool_id => id,
+ :uid => permission.uid,
+ :user_role =>
permission.user_role,
+ :inherited_from_id =>
+ permission.inherited_from_id.nil? ?
+ permission.id :
+ permission.inherited_from_id})
+ new_permission.save!
+ end
yield other_actions if other_actions
end
end
acts_as_xapian :texts => [ :name ]
- # this method lists pools with direct permission grants, but does not
- # include implied permissions (i.e. subtrees)
- def self.list_for_user(user, privilege)
- pools = find(:all, :include => "permissions",
- :conditions => "permissions.uid='#{user}' and
- permissions.user_role in
+ # this method lists pools with direct permission grants, but by default does
+ # not include implied permissions (i.e. subtrees)
+ def self.list_for_user(user, privilege, include_indirect = false)
+ if include_indirect
+ inherited_clause = ""
+ else
+ inherited_clause = "and permissions.inherited_from_id is null"
+ end
+ pools = find(:all, :include => "permissions",
+ :conditions => "permissions.uid='#{user}'
#{inherited_clause} and
+ permissions.user_role in
('#{Permission.roles_for_privilege(privilege).join("',
'")}')")
end
@@ -126,12 +141,10 @@ class Pool < ActiveRecord::Base
end
def has_privilege(user, privilege)
- traverse_parents do |pool|
- pool.permissions.find(:first,
- :conditions =>
"permissions.uid='#{user}' and
- permissions.user_role in
+ permissions.find(:first,
+ :conditions =>
"permissions.uid='#{user}' and
+ permissions.user_role in
('#{Permission.roles_for_privilege(privilege).join("',
'")}')")
- end
end
def total_resources
diff --git a/wui/src/app/views/user/_grid.rhtml
b/wui/src/app/views/user/_grid.rhtml
index d9ad795..cabf2af 100644
--- a/wui/src/app/views/user/_grid.rhtml
+++ b/wui/src/app/views/user/_grid.rhtml
@@ -5,27 +5,32 @@
</form>
</div>
<script type="text/javascript">
- $("#<%= table_id %>").flexigrid
- (
- {
- url: '<%= url_for :controller => parent_controller, :action =>
"users_json", :id => pool.id %>',
- dataType: 'json',
- colModel : [
- {display: '', name : 'id', width : 20, sortable : false,
align: 'left', process: <%= table_id %>checkbox},
- {display: 'Name', name : 'uid', width : 180, sortable : true,
align: 'left'},
- {display: 'Role', name : 'user_role', width : 80,
sortable : true, align: 'left'}
- ],
- sortname: "user",
- sortorder: "asc",
- usepager: <%= pool.permissions.size > users_per_page ? 'true' :
'false' %>,
- useRp: true,
- rp: <%= users_per_page %>,
- showTableToggleBtn: true
- }
- );
- function <%= table_id %>checkbox(celDiv)
- {
- $(celDiv).html('<input type="checkbox"
name="grid_checkbox'+$(celDiv).html()+'"
class="grid_checkbox"
value="'+$(celDiv).html()+'"/>');
- }
-
+ $("#<%= table_id %>").flexigrid
+ (
+ {
+ url: '<%= url_for :controller => parent_controller, :action
=> "users_json", :id => pool.id %>',
+ dataType: 'json',
+ colModel : [
+ {display: '', name : 'id', width : 20, sortable :
false, align: 'left', process: <%= table_id %>checkbox},
+ {display: 'Name', name : 'uid', width : 180, sortable :
true, align: 'left'},
+ {display: 'Role', name : 'user_role', width : 80,
sortable : true, align: 'left'},
+ {display: '', width : 80, sortable : true, align:
'left'}
+ ],
+ sortname: "user",
+ sortorder: "asc",
+ usepager: <%= pool.permissions.size > users_per_page ? 'true'
: 'false' %>,
+ useRp: true,
+ rp: <%= users_per_page %>,
+ showTableToggleBtn: true
+ }
+ );
+ function <%= table_id %>checkbox(celDiv)
+ {
+ var grid_id = $(celDiv).html().split("_");
+ if (grid_id[1] == 1) {
+ $(celDiv).html('<input type="checkbox"
name="grid_checkbox'+grid_id[0]+'"
class="grid_checkbox"
value="'+grid_id[0]+'"/>');
+ } else {
+ $(celDiv).html('')
+ }
+ }
</script>
diff --git a/wui/src/db/migrate/012_denormalize_permissions.rb
b/wui/src/db/migrate/012_denormalize_permissions.rb
new file mode 100644
index 0000000..f68f555
--- /dev/null
+++ b/wui/src/db/migrate/012_denormalize_permissions.rb
@@ -0,0 +1,32 @@
+class DenormalizePermissions < ActiveRecord::Migration
+ def self.up
+ add_column :permissions, :inherited_from_id, :integer
+ execute "alter table permissions add constraint fk_perm_parent
+ foreign key (inherited_from_id) references permissions(id)"
+
+ Permission.transaction do
+ Permission.find(:all,
+ :conditions => "inherited_from_id is null"
+ ).each do |permission|
+ permission.pool.all_children.each do |subpool|
+ new_permission = Permission.new({:pool_id => subpool.id,
+ :uid => permission.uid,
+ :user_role =>
permission.user_role,
+ :inherited_from_id =>
permission.id})
+ new_permission.save!
+ end
+ end
+ end
+ end
+
+ def self.down
+ Permission.transaction do
+ Permission.find(:all,
+ :conditions => "inherited_from_id is not
null"
+ ).each do |permission|
+ permission.destroy
+ end
+ end
+ remove_column :permissions, :inherited_from_id
+ end
+end
--
1.5.5.1
Jason Guiditta
2008-Aug-07 17:04 UTC
[Ovirt-devel] [PATCH] permission denormalization. (revised)
This partially works for me, but noticed a couple of things. * when viewing the users grid, the column with 'inherited/direct' has no header. This may be ok, but when you mouse over it, you get a clickable arrow that drops down a box that can't be fully seen (attaching screenshot, hard to describe) * Not sure where this breaks exactly, but when I log in as a user with no permissions, I still get all nodes returned in the tree (so fetch_json must not be filtering right). However, when I click on one of them, I see rails attempting to reroute me to /login. Similarly, get the full tree for a valid user with a small set of permissions, and can go only to approve pools (though I still see them in the tree). * Search results return items my user does not have permission for. Again, I cannot click on them, so that part of security is there. (I think this is covered in the next patch, which I have not tested yet). * I have not been able to test granting a new permission as my developer appliance has not yet successfully reinstalled. I kept getting the error: 'ERROR:root:Unable to create appliance : Failed to find package 'curl' : No package(s) available to install' Trying a full reinstall but am pretty much done for the day/week (it has been running a 1/2 hour is is maybe halfway through). I'll check in on it later this after and give feedback if it is successful. -j -------------- next part -------------- A non-text attachment was scrubbed... Name: user_access.png Type: image/png Size: 106672 bytes Desc: not available URL: <http://listman.redhat.com/archives/ovirt-devel/attachments/20080807/663790e4/attachment.png>
Jason Guiditta
2008-Aug-07 17:20 UTC
[Ovirt-devel] [PATCH] permission denormalization. (revised)
Sent a reply with an attachment which seems to have not gone through, so trying again without: This partially works for me, but noticed a couple of things. * when viewing the users grid, the column with 'inherited/direct' has no header. This may be ok, but when you mouse over it, you get a clickable arrow that drops down a box that can't be fully seen (attaching screenshot, hard to describe) * Not sure where this breaks exactly, but when I log in as a user with no permissions, I still get all nodes returned in the tree (so fetch_json must not be filtering right). However, when I click on one of them, I see rails attempting to reroute me to /login. Similarly, get the full tree for a valid user with a small set of permissions, and can go only to approve pools (though I still see them in the tree). * Search results return items my user does not have permission for. Again, I cannot click on them, so that part of security is there. (I think this is covered in the next patch, which I have not tested yet). * I have not been able to test granting a new permission as my developer appliance has not yet successfully reinstalled. I kept getting the error: 'ERROR:root:Unable to create appliance : Failed to find package 'curl' : No package(s) available to install' Trying a full reinstall but am pretty much done for the day/week (it has been running a 1/2 hour is is maybe halfway through). I'll check in on it later this after and give feedback if it is successful. -j