X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/efcfd2cdc9a9ed6ab3af54e9a4e149d4bdf1f8d3..48006f58ed71ccff5de4f6aa0c1f19b7297cb0fb:/services/api/app/models/user.rb diff --git a/services/api/app/models/user.rb b/services/api/app/models/user.rb index d944474712..9209411f1e 100644 --- a/services/api/app/models/user.rb +++ b/services/api/app/models/user.rb @@ -1,4 +1,9 @@ +# Copyright (C) The Arvados Authors. All rights reserved. +# +# SPDX-License-Identifier: AGPL-3.0 + require 'can_be_an_owner' +require 'refresh_permission_view' class User < ArvadosModel include HasUuid @@ -20,11 +25,13 @@ class User < ArvadosModel before_update :verify_repositories_empty, :if => Proc.new { |user| 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 :add_system_group_permission_link + after_create :invalidate_permissions_cache after_create :auto_setup_new_user, :if => Proc.new { |user| Rails.configuration.auto_setup_new_users and (user.uuid != system_user_uuid) and @@ -136,61 +143,55 @@ class User < ArvadosModel timestamp = DbCurrentTime::db_current_time.to_i if timestamp.nil? connection.execute "NOTIFY invalidate_permissions_cache, '#{timestamp}'" else - Rails.cache.delete_matched(/^groups_for_user_/) + refresh_permission_view + end + end + + def invalidate_permissions_cache(timestamp=nil) + User.invalidate_permissions_cache + end + + # Return a hash of {user_uuid: group_perms} + def self.all_group_permissions + all_perms = {} + ActiveRecord::Base.connection. + exec_query("SELECT user_uuid, target_owner_uuid, perm_level, trashed + FROM #{PERMISSION_VIEW} + WHERE target_owner_uuid IS NOT NULL", + # "name" arg is a query label that appears in logs: + "all_group_permissions", + ).rows.each do |user_uuid, group_uuid, max_p_val, trashed| + all_perms[user_uuid] ||= {} + all_perms[user_uuid][group_uuid] = PERMS_FOR_VAL[max_p_val.to_i] end + all_perms 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 calculate_group_permissions - install_view('permission') - - group_perms = {} + def group_permissions + group_perms = {self.uuid => {:read => true, :write => true, :manage => true}} ActiveRecord::Base.connection. - exec_query('SELECT target_owner_uuid, max(perm_level) - FROM permission_view + exec_query("SELECT target_owner_uuid, perm_level, trashed + FROM #{PERMISSION_VIEW} WHERE user_uuid = $1 - AND target_owner_uuid IS NOT NULL - GROUP BY target_owner_uuid', + AND target_owner_uuid IS NOT NULL", # "name" arg is a query label that appears in logs: "group_permissions for #{uuid}", # "binds" arg is an array of [col_id, value] for '$1' vars: [[nil, uuid]], - ).rows.each do |group_uuid, max_p_val| + ).rows.each do |group_uuid, max_p_val, trashed| group_perms[group_uuid] = PERMS_FOR_VAL[max_p_val.to_i] end - Rails.cache.write "groups_for_user_#{self.uuid}", group_perms group_perms 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 - r = Rails.cache.read "groups_for_user_#{self.uuid}" - if r.nil? - if Rails.configuration.async_permissions_update - while r.nil? - sleep(0.1) - r = Rails.cache.read "groups_for_user_#{self.uuid}" - end - else - r = calculate_group_permissions - end - end - r - end - - def self.setup(user, openid_prefix, repo_name=nil, vm_uuid=nil) - return user.setup_repo_vm_links(repo_name, vm_uuid, openid_prefix) - end - # create links - def setup_repo_vm_links(repo_name, vm_uuid, openid_prefix) + def setup(openid_prefix:, repo_name: nil, vm_uuid: nil) oid_login_perm = create_oid_login_perm openid_prefix repo_perm = create_user_repo_link repo_name - vm_login_perm = create_vm_login_permission_link vm_uuid, username + vm_login_perm = create_vm_login_permission_link(vm_uuid, username) if vm_uuid group_perm = create_user_group_link return [oid_login_perm, repo_perm, vm_login_perm, group_perm, self].compact @@ -253,6 +254,32 @@ class User < ArvadosModel end end + def update_uuid(new_uuid:) + if !current_user.andand.is_admin + raise PermissionDeniedError + end + if uuid == system_user_uuid || uuid == anonymous_user_uuid + raise "update_uuid cannot update system accounts" + end + if self.class != self.class.resource_class_for_uuid(new_uuid) + raise "invalid new_uuid #{new_uuid.inspect}" + end + transaction(requires_new: true) do + reload + old_uuid = self.uuid + self.uuid = new_uuid + save!(validate: false) + ActiveRecord::Base.descendants.reject(&:abstract_class?).each do |klass| + klass.columns.each do |col| + if col.name.end_with?('_uuid') + column = col.name.to_sym + klass.where(column => old_uuid).update_all(column => new_uuid) + end + end + end + end + end + protected def ensure_ownership_path_leads_to_user @@ -364,13 +391,12 @@ class User < ArvadosModel merged end - def create_oid_login_perm (openid_prefix) - login_perm_props = { "identity_url_prefix" => openid_prefix} - + def create_oid_login_perm(openid_prefix) # Check oid_login_perm oid_login_perms = Link.where(tail_uuid: self.email, - link_class: 'permission', - name: 'can_login').where("head_uuid = ?", self.uuid) + head_uuid: self.uuid, + link_class: 'permission', + name: 'can_login') if !oid_login_perms.any? # create openid login permission @@ -378,8 +404,9 @@ class User < ArvadosModel name: 'can_login', tail_uuid: self.email, head_uuid: self.uuid, - properties: login_perm_props - ) + properties: { + "identity_url_prefix" => openid_prefix, + }) logger.info { "openid login permission: " + oid_login_perm[:uuid] } else oid_login_perm = oid_login_perms.first @@ -407,15 +434,12 @@ class User < ArvadosModel # create login permission for the given vm_uuid, if it does not already exist def create_vm_login_permission_link(vm_uuid, repo_name) # vm uuid is optional - if vm_uuid - vm = VirtualMachine.where(uuid: vm_uuid).first + return if !vm_uuid - if not vm - logger.warn "Could not find virtual machine for #{vm_uuid.inspect}" - raise "No vm found for #{vm_uuid}" - end - else - return + vm = VirtualMachine.where(uuid: vm_uuid).first + if !vm + logger.warn "Could not find virtual machine for #{vm_uuid.inspect}" + raise "No vm found for #{vm_uuid}" end logger.info { "vm uuid: " + vm[:uuid] } @@ -468,9 +492,17 @@ class User < ArvadosModel end end + # Automatically setup if is_active flag turns on + def setup_on_activate + return if [system_user_uuid, anonymous_user_uuid].include?(self.uuid) + if is_active && (new_record? || is_active_changed?) + setup(openid_prefix: Rails.configuration.default_openid_prefix) + end + end + # Automatically setup new user during creation def auto_setup_new_user - setup_repo_vm_links(nil, nil, Rails.configuration.default_openid_prefix) + setup(openid_prefix: Rails.configuration.default_openid_prefix) if username create_vm_login_permission_link(Rails.configuration.auto_setup_new_users_with_vm_uuid, username)