user.username.nil? and user.username_changed?
}
before_update :setup_on_activate
+
before_create :check_auto_admin
before_create :set_initial_username, :if => Proc.new { |user|
user.username.nil? and user.email
}
- after_create :update_permissions
+ after_create :after_ownership_change
after_create :setup_on_activate
after_create :add_system_group_permission_link
after_create :auto_setup_new_user, :if => Proc.new { |user|
(user.uuid != anonymous_user_uuid)
}
after_create :send_admin_notifications
- after_update :update_permissions, :if => :owner_uuid_changed?
+
+ before_update :before_ownership_change
+ after_update :after_ownership_change
after_update :send_profile_created_notification
after_update :sync_repository_names, :if => Proc.new { |user|
(user.uuid != system_user_uuid) and
user.username_changed? and
(not user.username_was.nil?)
}
-
+ before_destroy :clear_permissions
+ after_destroy :check_permissions
has_many :authorized_keys, :foreign_key => :authorized_user_uuid, :primary_key => :uuid
has_many :repositories, foreign_key: :owner_uuid, primary_key: :uuid
{read: true, write: true},
{read: true, write: true, manage: true}]
+ VAL_FOR_PERM =
+ {:read => 1,
+ :write => 2,
+ :manage => 3}
+
+
def full_name
"#{first_name} #{last_name}".strip
end
end
def groups_i_can(verb)
- my_groups = self.group_permissions.select { |uuid, mask| mask[verb] }.keys
+ my_groups = self.group_permissions(VAL_FOR_PERM[verb]).keys
if verb == :read
my_groups << anonymous_group_uuid
end
end
end
next if target_uuid == self.uuid
- next if (group_permissions[target_uuid] and
- group_permissions[target_uuid][action])
- if target.respond_to? :owner_uuid
- next if target.owner_uuid == self.uuid
- next if (group_permissions[target.owner_uuid] and
- group_permissions[target.owner_uuid][action])
- end
- sufficient_perms = case action
- when :manage
- ['can_manage']
- when :write
- ['can_manage', 'can_write']
- when :read
- ['can_manage', 'can_write', 'can_read']
- else
- # (Skip this kind of permission opportunity
- # if action is an unknown permission type)
- end
- if sufficient_perms
- # Check permission links with head_uuid pointing directly at
- # the target object. If target is a Group, this is redundant
- # and will fail except [a] if permission caching is broken or
- # [b] during a race condition, where a permission link has
- # *just* been added.
- if Link.where(link_class: 'permission',
- name: sufficient_perms,
- tail_uuid: groups_i_can(action) + [self.uuid],
- head_uuid: target_uuid).any?
- next
- end
+
+ target_owner_uuid = target.owner_uuid if target.respond_to? :owner_uuid
+
+ unless ActiveRecord::Base.connection.
+ exec_query(%{
+SELECT 1 FROM #{PERMISSION_VIEW}
+ WHERE user_uuid = $1 and
+ ((target_uuid = $2 and perm_level >= $3)
+ or (target_uuid = $4 and perm_level >= $3 and traverse_owned))
+},
+ # "name" arg is a query label that appears in logs:
+ "user_can_query",
+ [[nil, self.uuid],
+ [nil, target_uuid],
+ [nil, VAL_FOR_PERM[action]],
+ [nil, target_owner_uuid]]
+ ).any?
+ return false
end
- return false
end
true
end
- def update_permissions
-
- puts "Update permissions for #{uuid}"
- User.printdump %{
-select * from materialized_permissions where user_uuid='#{uuid}'
-}
- puts "---"
- User.update_permissions self.owner_uuid, self.uuid, 3
-
- puts "post-update"
- User.printdump %{
-select * from materialized_permissions where user_uuid='#{uuid}'
-}
- puts "<<<"
+ def before_ownership_change
+ if owner_uuid_changed? and !self.owner_uuid_was.nil?
+ MaterializedPermission.where(user_uuid: owner_uuid_was, target_uuid: uuid).delete_all
+ update_permissions self.owner_uuid_was, self.uuid, 0
+ end
end
- def self.printdump qr
- q1 = ActiveRecord::Base.connection.exec_query qr
- q1.each do |r|
- puts r
+ def after_ownership_change
+ if owner_uuid_changed?
+ update_permissions self.owner_uuid, self.uuid, 3
end
end
- def recompute_permissions
- ActiveRecord::Base.connection.execute("DELETE FROM #{PERMISSION_VIEW} where user_uuid='#{uuid}'")
- ActiveRecord::Base.connection.execute %{
-INSERT INTO #{PERMISSION_VIEW}
-select '#{uuid}', g.target_uuid, g.val, g.traverse_owned
-from search_permission_graph('#{uuid}', 3) as g
-}
+ def clear_permissions
+ update_permissions self.owner_uuid, self.uuid, 0
+ MaterializedPermission.where("user_uuid = ? or target_uuid = ?", uuid, uuid).delete_all
end
- def self.update_permissions perm_origin_uuid, starting_uuid, perm_level
- # Update a subset of the permission graph
- # perm_level is the inherited permission
- # perm_level is a number from 0-3
- # can_read=1
- # can_write=2
- # can_manage=3
- # call with perm_level=0 to revoke permissions
- #
- # 1. Compute set (group, permission) implied by traversing
- # graph starting at this group
- # 2. Find links from outside the graph that point inside
- # 3. For each starting uuid, get the set of permissions from the
- # materialized permission table
- # 3. Delete permissions from table not in our computed subset.
- # 4. Upsert each permission in our subset (user, group, val)
-
- ## testinging
- puts "__ update_permissions __"
- puts "What's in there now for #{starting_uuid}"
- printdump %{
-select * from materialized_permissions where user_uuid='#{starting_uuid}'
-}
-
- puts "search_permission_graph #{perm_origin_uuid} #{starting_uuid}, #{perm_level}"
- printdump %{
-select '#{perm_origin_uuid}'::varchar as perm_origin_uuid, target_uuid, val, traverse_owned from search_permission_graph('#{starting_uuid}', #{perm_level})
-}
-
-# puts "Perms out"
-# printdump %{
-# with
-# perm_from_start(perm_origin_uuid, target_uuid, val, traverse_owned) as (
-# select '#{perm_origin_uuid}'::varchar, target_uuid, val, traverse_owned
-# from search_permission_graph('#{starting_uuid}', #{perm_level}))
-
-# (select materialized_permissions.user_uuid, u.target_uuid, max(least(materialized_permissions.perm_level, u.val)), bool_or(u.traverse_owned)
-# from perm_from_start as u
-# join materialized_permissions on (u.perm_origin_uuid = materialized_permissions.target_uuid)
-# where materialized_permissions.traverse_owned
-# group by materialized_permissions.user_uuid, u.target_uuid)
-# union
-# select target_uuid as user_uuid, target_uuid, 3, true
-# from perm_from_start where target_uuid like '_____-tpzed-_______________'
-# }
- ## end
-
- temptable_perms = "temp_perms_#{rand(2**64).to_s(10)}"
- ActiveRecord::Base.connection.exec_query %{
-create temporary table #{temptable_perms} on commit drop
-as select * from compute_permission_subgraph($1, $2, $3)
-},
- 'Group.search_permissions',
- [[nil, perm_origin_uuid],
- [nil, starting_uuid],
- [nil, perm_level]]
-
- q1 = ActiveRecord::Base.connection.exec_query %{
-select * from #{temptable_perms}
-}
- puts "recomputed perms was #{perm_origin_uuid} #{starting_uuid}, #{perm_level}"
- q1.each do |r|
- puts r
- end
- puts "<<<<"
-
- ActiveRecord::Base.connection.exec_query %{
-delete from materialized_permissions where
- target_uuid in (select target_uuid from #{temptable_perms}) and
- (user_uuid not in (select user_uuid from #{temptable_perms} where target_uuid=materialized_permissions.target_uuid)
- or user_uuid in (select user_uuid from #{temptable_perms} where target_uuid=materialized_permissions.target_uuid and perm_level=0))
-}
-
- ActiveRecord::Base.connection.exec_query %{
-insert into materialized_permissions (user_uuid, target_uuid, perm_level, traverse_owned)
- select user_uuid, target_uuid, val as perm_level, traverse_owned from #{temptable_perms}
-on conflict (user_uuid, target_uuid) do update set perm_level=EXCLUDED.perm_level, traverse_owned=EXCLUDED.traverse_owned;
-}
-
- # for testing only - make a copy of the table and compare it to the one generated
- # using a full permission recompute
-# temptable_compare = "compare_perms_#{rand(2**64).to_s(10)}"
-# ActiveRecord::Base.connection.exec_query %{
-# create temporary table #{temptable_compare} on commit drop as select * from materialized_permissions
-# }
-
- # Ensure a new group can be accessed by the appropriate users
- # immediately after being created.
- #User.invalidate_permissions_cache
-
-# q1 = ActiveRecord::Base.connection.exec_query %{
-# select count(*) from materialized_permissions
-# }
-# puts "correct version #{q1.first}"
-
-# q2 = ActiveRecord::Base.connection.exec_query %{
-# select count(*) from #{temptable_compare}
-# }
-# puts "incremental update #{q2.first}"
+ def check_permissions
+ check_permissions_against_full_refresh
end
# Return a hash of {user_uuid: group_perms}
FROM #{PERMISSION_VIEW}
WHERE traverse_owned",
# "name" arg is a query label that appears in logs:
- "all_group_permissions",
- ).rows.each do |user_uuid, group_uuid, max_p_val|
+ "all_group_permissions").
+ rows.each do |user_uuid, group_uuid, max_p_val|
all_perms[user_uuid] ||= {}
all_perms[user_uuid][group_uuid] = PERMS_FOR_VAL[max_p_val.to_i]
end
# Return a hash of {group_uuid: perm_hash} where perm_hash[:read]
# and perm_hash[:write] are true if this user can read and write
# objects owned by group_uuid.
- def group_permissions
- group_perms = {self.uuid => {:read => true, :write => true, :manage => true}}
+ def group_permissions(level=1)
+ group_perms = {}
ActiveRecord::Base.connection.
- exec_query("SELECT target_uuid, perm_level
- FROM #{PERMISSION_VIEW}
- WHERE user_uuid = $1
- AND traverse_owned",
+ exec_query(%{
+SELECT target_uuid, perm_level
+ FROM #{PERMISSION_VIEW}
+ WHERE user_uuid = $1 and perm_level >= $2
+},
# "name" arg is a query label that appears in logs:
"group_permissions_for_user",
# "binds" arg is an array of [col_id, value] for '$1' vars:
- [[nil, uuid]],
- ).rows.each do |group_uuid, max_p_val|
+ [[nil, uuid],
+ [nil, level]]).
+ rows.each do |group_uuid, max_p_val|
group_perms[group_uuid] = PERMS_FOR_VAL[max_p_val.to_i]
end
group_perms
self.uuid = new_uuid
save!(validate: false)
change_all_uuid_refs(old_uuid: old_uuid, new_uuid: new_uuid)
+ ActiveRecord::Base.connection.exec_update %{
+update #{PERMISSION_VIEW} set user_uuid=$1 where user_uuid = $2
+},
+ 'User.update_uuid.update_permissions_user_uuid',
+ [[nil, new_uuid],
+ [nil, old_uuid]]
+ ActiveRecord::Base.connection.exec_update %{
+update #{PERMISSION_VIEW} set target_uuid=$1 where target_uuid = $2
+},
+ 'User.update_uuid.update_permissions_target_uuid',
+ [[nil, new_uuid],
+ [nil, old_uuid]]
end
end
raise "user does not exist" if !new_user
raise "cannot merge to an already merged user" if new_user.redirect_to_user_uuid
+ self.clear_permissions
+
# If 'self' is a remote user, don't transfer authorizations
# (i.e. ability to access the account) to the new user, because
# that gives the remote site the ability to access the 'new'
if redirect_to_new_user
update_attributes!(redirect_to_user_uuid: new_user.uuid, username: nil)
end
- self.recompute_permissions
- new_user.recompute_permissions
+ update_permissions self.owner_uuid, self.uuid, 3, false
+ update_permissions new_user.owner_uuid, new_user.uuid, 3
end
end
# add the user to the 'All users' group
def create_user_group_link
- #puts "In create_user_group_link"
return (Link.where(tail_uuid: self.uuid,
head_uuid: all_users_group[:uuid],
link_class: 'permission',