before_update :prevent_privilege_escalation
before_update :prevent_inactive_admin
before_update :prevent_nonadmin_system_root
- before_update :verify_repositories_empty, :if => Proc.new {
- username.nil? and username_changed?
- }
after_update :setup_on_activate
before_create :check_auto_admin
- before_create :set_initial_username, :if => Proc.new {
- username.nil? and email
+ before_validation :set_initial_username, :if => Proc.new {
+ new_record? && email
}
+ before_create :active_is_not_nil
after_create :after_ownership_change
after_create :setup_on_activate
after_create :add_system_group_permission_link
before_update :before_ownership_change
after_update :after_ownership_change
after_update :send_profile_created_notification
- after_update :sync_repository_names, :if => Proc.new {
- (uuid != system_user_uuid) and
- saved_change_to_username? and
- (not username_before_last_save.nil?)
- }
before_destroy :clear_permissions
after_destroy :remove_self_from_permissions
- has_many :authorized_keys, :foreign_key => :authorized_user_uuid, :primary_key => :uuid
- has_many :repositories, foreign_key: :owner_uuid, primary_key: :uuid
+ has_many :authorized_keys, foreign_key: 'authorized_user_uuid', primary_key: 'uuid'
default_scope { where('redirect_to_user_uuid is null') }
self.groups_i_can(:read).select { |x| x.match(/-f+$/) }.first)
end
+ def self.ignored_select_attributes
+ super + ["full_name", "is_invited"]
+ end
+
def groups_i_can(verb)
my_groups = self.group_permissions(VAL_FOR_PERM[verb]).keys
if verb == :read
},
# "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]]
+ [self.uuid,
+ target_uuid,
+ VAL_FOR_PERM[action],
+ target_owner_uuid]
).any?
return false
end
# "name" arg is a query label that appears in logs:
"User.group_permissions",
# "binds" arg is an array of [col_id, value] for '$1' vars:
- [[nil, uuid]]).
+ [uuid]).
rows.each do |group_uuid, max_p_val|
@group_perms[group_uuid] = PERMS_FOR_VAL[max_p_val.to_i]
end
end
# create links
- def setup(repo_name: nil, vm_uuid: nil, send_notification_email: nil)
+ def setup(vm_uuid: nil, send_notification_email: nil)
newly_invited = Link.where(tail_uuid: self.uuid,
head_uuid: all_users_group_uuid,
- link_class: 'permission',
- name: 'can_read').empty?
+ link_class: 'permission').empty?
# Add can_read link from this user to "all users" which makes this
# user "invited", and (depending on config) a link in the opposite
# direction which makes this user visible to other users.
group_perms = add_to_all_users_group
- # Add git repo
- repo_perm = if (!repo_name.nil? || Rails.configuration.Users.AutoSetupNewUsersWithRepository) and !username.nil?
- repo_name ||= "#{username}/#{username}"
- create_user_repo_link repo_name
- end
-
# Add virtual machine
if vm_uuid.nil? and !Rails.configuration.Users.AutoSetupNewUsersWithVmUUID.empty?
vm_uuid = Rails.configuration.Users.AutoSetupNewUsersWithVmUUID
forget_cached_group_perms
- return [repo_perm, vm_login_perm, *group_perms, self].compact
+ return [vm_login_perm, *group_perms, self].compact
end
- # delete user signatures, login, repo, and vm perms, and mark as inactive
+ # delete user signatures, login, and vm perms, and mark as inactive
def unsetup
if self.uuid == system_user_uuid
raise "System root user cannot be deactivated"
end
def set_initial_username(requested: false)
- if !requested.is_a?(String) || requested.empty?
+ if new_record? and requested == false and self.username != nil and self.username != ""
+ requested = self.username
+ end
+
+ if (!requested.is_a?(String) || requested.empty?) and email
email_parts = email.partition("@")
local_parts = email_parts.first.partition("+")
if email_parts.any?(&:empty?)
requested = email_parts.first
end
end
- requested.sub!(/^[^A-Za-z]+/, "")
- requested.gsub!(/[^A-Za-z0-9]/, "")
- unless requested.empty?
+ if requested
+ requested.sub!(/^[^A-Za-z]+/, "")
+ requested.gsub!(/[^A-Za-z0-9]/, "")
+ end
+ unless !requested || requested.empty?
self.username = find_usable_username_from(requested)
end
end
+ def active_is_not_nil
+ self.is_active = false if self.is_active.nil?
+ self.is_admin = false if self.is_admin.nil?
+ end
+
# Move this user's (i.e., self's) owned items to new_owner_uuid and
# new_user_uuid (for things normally owned directly by the user).
#
klass.where(column => uuid).update_all(column => new_user.uuid)
end
- # Need to update repository names to new username
- if username
- old_repo_name_re = /^#{Regexp.escape(username)}\//
- Repository.where(:owner_uuid => uuid).each do |repo|
- repo.owner_uuid = new_user.uuid
- repo_name_sub = "#{new_user.username}/"
- name = repo.name.sub(old_repo_name_re, repo_name_sub)
- while (conflict = Repository.where(:name => name).first) != nil
- repo_name_sub += "migrated"
- name = repo.name.sub(old_repo_name_re, repo_name_sub)
- end
- repo.name = name
- repo.save!
- end
- end
-
# References to the merged user's "home project" are updated to
# point to new_owner_uuid.
ActiveRecord::Base.descendants.reject(&:abstract_class?).each do |klass|
next if [ApiClientAuthorization,
AuthorizedKey,
Link,
- Log,
- Repository].include?(klass)
+ Log].include?(klass)
next if !klass.columns.collect(&:name).include?('owner_uuid')
klass.where(owner_uuid: uuid).update_all(owner_uuid: new_owner_uuid)
end
if redirect_to_new_user
- update_attributes!(redirect_to_user_uuid: new_user.uuid, username: nil)
+ update!(redirect_to_user_uuid: new_user.uuid, username: nil)
end
skip_check_permissions_against_full_refresh do
- update_permissions self.uuid, self.uuid, CAN_MANAGE_PERM
- update_permissions new_user.uuid, new_user.uuid, CAN_MANAGE_PERM
- update_permissions new_user.owner_uuid, new_user.uuid, CAN_MANAGE_PERM
+ update_permissions self.uuid, self.uuid, CAN_MANAGE_PERM, nil, true
+ update_permissions new_user.uuid, new_user.uuid, CAN_MANAGE_PERM, nil, true
+ update_permissions new_user.owner_uuid, new_user.uuid, CAN_MANAGE_PERM, nil, true
end
- update_permissions self.owner_uuid, self.uuid, CAN_MANAGE_PERM
+ update_permissions self.owner_uuid, self.uuid, CAN_MANAGE_PERM, nil, true
end
end
primary_user
end
+ def self.update_remote_user remote_user
+ remote_user = remote_user.symbolize_keys
+ remote_user_prefix = remote_user[:uuid][0..4]
+
+ # interaction between is_invited and is_active
+ #
+ # either can flag can be nil, true or false
+ #
+ # in all cases, we create the user if they don't exist.
+ #
+ # invited nil, active nil: don't call setup or unsetup.
+ #
+ # invited nil, active false: call unsetup
+ #
+ # invited nil, active true: call setup and activate them.
+ #
+ #
+ # invited false, active nil: call unsetup
+ #
+ # invited false, active false: call unsetup
+ #
+ # invited false, active true: call unsetup
+ #
+ #
+ # invited true, active nil: call setup but don't change is_active
+ #
+ # invited true, active false: call setup but don't change is_active
+ #
+ # invited true, active true: call setup and activate them.
+
+ should_setup = (remote_user_prefix == Rails.configuration.Login.LoginCluster or
+ Rails.configuration.Users.AutoSetupNewUsers or
+ Rails.configuration.Users.NewUsersAreActive or
+ Rails.configuration.RemoteClusters[remote_user_prefix].andand["ActivateUsers"])
+
+ should_activate = (remote_user_prefix == Rails.configuration.Login.LoginCluster or
+ Rails.configuration.Users.NewUsersAreActive or
+ Rails.configuration.RemoteClusters[remote_user_prefix].andand["ActivateUsers"])
+
+ remote_should_be_unsetup = (remote_user[:is_invited] == nil && remote_user[:is_active] == false) ||
+ (remote_user[:is_invited] == false)
+
+ remote_should_be_setup = should_setup && (
+ (remote_user[:is_invited] == nil && remote_user[:is_active] == true) ||
+ (remote_user[:is_invited] == false && remote_user[:is_active] == true) ||
+ (remote_user[:is_invited] == true))
+
+ remote_should_be_active = should_activate && remote_user[:is_invited] != false && remote_user[:is_active] == true
+
+ # Make sure blank username is nil
+ remote_user[:username] = nil if remote_user[:username] == ""
+
+ begin
+ user = User.create_with(email: remote_user[:email],
+ username: remote_user[:username],
+ first_name: remote_user[:first_name],
+ last_name: remote_user[:last_name],
+ is_active: remote_should_be_active,
+ ).find_or_create_by(uuid: remote_user[:uuid])
+ rescue ActiveRecord::RecordNotUnique
+ retry
+ end
+
+ user.with_lock do
+ needupdate = {}
+ [:email, :username, :first_name, :last_name, :prefs].each do |k|
+ v = remote_user[k]
+ if !v.nil? && user.send(k) != v
+ needupdate[k] = v
+ end
+ end
+
+ user.email = needupdate[:email] if needupdate[:email]
+
+ loginCluster = Rails.configuration.Login.LoginCluster
+ if user.username.nil? || user.username == ""
+ # Don't have a username yet, try to set one
+ initial_username = user.set_initial_username(requested: remote_user[:username])
+ needupdate[:username] = initial_username if !initial_username.nil?
+ elsif remote_user_prefix != loginCluster
+ # Upstream is not login cluster, don't try to change the
+ # username once set.
+ needupdate.delete :username
+ end
+
+ if needupdate.length > 0
+ begin
+ user.update!(needupdate)
+ rescue ActiveRecord::RecordInvalid
+ if remote_user_prefix == loginCluster && !needupdate[:username].nil?
+ local_user = User.find_by_username(needupdate[:username])
+ # The username of this record conflicts with an existing,
+ # different user record. This can happen because the
+ # username changed upstream on the login cluster, or
+ # because we're federated with another cluster with a user
+ # by the same username. The login cluster is the source
+ # of truth, so change the username on the conflicting
+ # record and retry the update operation.
+ if local_user.uuid != user.uuid
+ new_username = "#{needupdate[:username]}#{rand(99999999)}"
+ Rails.logger.warn("cached username '#{needupdate[:username]}' collision with user '#{local_user.uuid}' - renaming to '#{new_username}' before retrying")
+ local_user.update!({username: new_username})
+ retry
+ end
+ end
+ raise # Not the issue we're handling above
+ end
+ elsif user.new_record?
+ begin
+ user.save!
+ rescue => e
+ Rails.logger.debug "Error saving user record: #{$!}"
+ Rails.logger.debug "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
+ raise
+ end
+ end
+
+ if remote_should_be_unsetup
+ # Remote user is not "invited" or "active" state on their home
+ # cluster, so they should be unsetup, which also makes them
+ # inactive.
+ user.unsetup
+ else
+ if !user.is_invited && remote_should_be_setup
+ user.setup
+ end
+
+ if !user.is_active && remote_should_be_active
+ # remote user is active and invited, we need to activate them
+ user.update!(is_active: true)
+ end
+
+ if remote_user_prefix == Rails.configuration.Login.LoginCluster and
+ user.is_active and
+ !remote_user[:is_admin].nil? and
+ user.is_admin != remote_user[:is_admin]
+ # Remote cluster controls our user database, including the
+ # admin flag.
+ user.update!(is_admin: remote_user[:is_admin])
+ end
+ end
+ end
+ user
+ end
+
protected
def self.attributes_required_columns
merged
end
- def create_user_repo_link(repo_name)
- # repo_name is optional
- if not repo_name
- logger.warn ("Repository name not given for #{self.uuid}.")
- return
- end
-
- repo = Repository.where(owner_uuid: uuid, name: repo_name).first_or_create!
- logger.info { "repo uuid: " + repo[:uuid] }
- repo_perm = Link.where(tail_uuid: uuid, head_uuid: repo.uuid,
- link_class: "permission",
- name: "can_manage").first_or_create!
- logger.info { "repo permission: " + repo_perm[:uuid] }
- return repo_perm
- end
-
# create login permission for the given vm_uuid, if it does not already exist
- def create_vm_login_permission_link(vm_uuid, repo_name)
+ def create_vm_login_permission_link(vm_uuid, username)
# vm uuid is optional
return if vm_uuid == ""
login_perm = Link.
where(login_attrs).
- select { |link| link.properties["username"] == repo_name }.
+ select { |link| link.properties["username"] == username }.
first
login_perm ||= Link.
- create(login_attrs.merge(properties: {"username" => repo_name}))
+ create(login_attrs.merge(properties: {"username" => username}))
logger.info { "login permission: " + login_perm[:uuid] }
login_perm
# Send admin notifications
def send_admin_notifications
- AdminNotifier.new_user(self).deliver_now
- if not self.is_active then
+ if self.is_invited then
+ AdminNotifier.new_user(self).deliver_now
+ else
AdminNotifier.new_inactive_user(self).deliver_now
end
end
end
end
- def verify_repositories_empty
- unless repositories.first.nil?
- errors.add(:username, "can't be unset when the user owns repositories")
- throw(:abort)
- end
- end
-
- def sync_repository_names
- old_name_re = /^#{Regexp.escape(username_before_last_save)}\//
- name_sub = "#{username}/"
- repositories.find_each do |repo|
- repo.name = repo.name.sub(old_name_re, name_sub)
- repo.save!
- end
- end
-
def identity_url_nil_if_empty
if identity_url == ""
self.identity_url = nil