diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 457b401..ed2c116 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -1,130 +1,130 @@ class Admin::UsersController < Admin::BaseController before_action :fetch_user, only: [:show, :edit, :update, :ban, :unban, :grant_admin, :revoke_admin] before_action :editable_users_only, only: [:edit, :update] # GET /admin/users def index @baculized_host_names = Hash.new { |h, k| h[k] = [] } @non_baculized_host_names = Hash.new { |h, k| h[k] = [] } @unverified_host_names = Hash.new { |h, k| h[k] = [] } @users = User.all.includes(:hosts) @users = @users.admin if params[:type] == 'admin' @users = @users.vima if params[:type] == 'vima' @users = @users.institutional if params[:type] == 'institutional' @users.each do |user| user.hosts.each do |host| if host.deployed? || host.updated? || host.dispatched? || host.for_removal? @baculized_host_names[user.id] << host.name else @non_baculized_host_names[user.id] << host.name @unverified_host_names[user.id] << host.name if !host.verified? end end end end # GET /admin/users/new def new @user = User.new(user_type: :admin) end # POST /admin/users def create @user = User.new(fetch_params) @user.user_type = :admin if @user.add_password(@user.password) flash[:success] = 'User created' redirect_to admin_users_path else flash[:error] = 'User was not created' render 'new' end end # GET /admin/users/1 def show end # GET /admin/users/1/edit def edit end # PATCH /admin/users/1/update def update - if @user.admin? && @user.update_attributes(fetch_params) + if @user.admin? && @user.update_attributes_and_password(fetch_params) flash[:success] = 'User updated' redirect_to admin_user_path(@user) elsif @user.admin? flash[:error] = 'User not updated' - redirect_to edit_admin_user_path(@user) + render :edit else flash[:error] = "User is #{@user.user_type} and thus accepts no updates" - redirect_to admin_user_path(@user) + render :edit end end # PATCH /admin/users/1/ban def ban if @user.ban flash[:success] = 'User banned' else flash[:error] = 'User NOT banned' end redirect_to admin_users_path end # PATCH /admin/users/1/unban def unban if @user.unban flash[:success] = 'User enabled' else flash[:error] = 'User NOT enabled' end redirect_to admin_users_path end # PATCH /admin/users/1/revoke_admin def revoke_admin if @user.update_attribute(:moderator, false) flash[:success] = 'User is no longer an admin' else flash[:error] = 'Admin rights were NOT revoked' end redirect_to admin_users_path end # PATCH /admin/users/1/grant_admin def grant_admin if @user.update_attribute(:moderator, true) flash[:success] = 'User is now an admin' else flash[:error] = 'Admin rights were NOT granted' end redirect_to admin_users_path end private def fetch_params params.require(:user).permit(:username, :email, :password, :retype_password) end def fetch_user @user = User.find(params[:id]) end def editable_users_only return if @user.editable? flash[:error] = "User #{@user.username} is not editable" redirect_to admin_users_path end end diff --git a/app/models/user.rb b/app/models/user.rb index 5947f71..0fdd3e2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,147 +1,161 @@ class User < ActiveRecord::Base establish_connection ARCHIVING_CONF attr_accessor :password, :retype_password serialize :temp_hosts, JSON has_many :ownerships has_many :hosts, through: :ownerships, inverse_of: :users has_many :invitations enum user_type: { institutional: 0, vima: 1, okeanos: 2, admin: 3 } validates :user_type, presence: true validates :username, presence: true, uniqueness: { scope: :user_type } validates :email, presence: true, uniqueness: { scope: :user_type } before_create :confirm_passwords, if: :admin? before_create :create_token # Returns an admin user with the given password # # @param username[String] username from user input # @param a_password[String] password from user input # # @return [User] the admin user or nil def self.fetch_admin_with_password(username, a_password) hashed_pass = Digest::SHA256.hexdigest(a_password + Rails.application.secrets.salt) admin = User.admin.find_by_username_and_password_hash(username, hashed_pass) admin end # Initializes a user token which will be used for API access def create_token(opts = {}) self.token = Digest::SHA256.hexdigest( Time.now.to_s + Rails.application.secrets.salt + email ) save if opts[:save] == true end # Composes the user's display name from the user's username and email # # @return [String] def display_name "#{username} <#{email}>" end # Determines if the user must select hosts from a list or enter their # FQDN manually # # @return [Boolean] def needs_host_list? vima? || okeanos? end # Determines if the user is editable or not. # Editable users are only admin users, all others come from 3rd party authorization # # @return [Boolean] def editable? admin? end # Marks a user as not enabled def ban self.enabled = false save hosts.each do |host| if host.client.present? host.disable_jobs_and_lock else host.block end end end # Marks a user as enabled def unban self.enabled = true save end + # Updates the user and handles the password field accordingly + # + # @param attrs[Hash] the desired attributes + def update_attributes_and_password(attrs) + self.assign_attributes(attrs) + self.valid? + if self.password.present? && confirm_passwords + self.password_hash = Digest::SHA256.hexdigest(self.password + Rails.application.secrets.salt) + end + if self.errors.empty? + self.save + end + end + # Stores a hashed password as a password_hash # # @param a_password[String] the user submitted password # # @return [Boolean] the save exit status def add_password(a_password) self.password_hash = Digest::SHA256.hexdigest(a_password + Rails.application.secrets.salt) self.save end # Fetches the user's unverified hosts # # @return [Array] of Strings containing the hosts' names def unverified_hosts hosts.unverified.pluck(:name) end # Fetches the user's hosts that are being backed up by bacula # # @return [Array] of Strings configuration the host's names def baculized_hosts hosts.in_bacula.pluck(:name) end # Fetches the user's hosts that are NOT being backed up by bacula # # @return [Array] of Strings configuration the host's names def non_baculized_hosts hosts.not_baculized.pluck(:name) end # Determines if a vima user needs to update his hosts' list # # @return [Boolean] def refetch_hosts? return false unless vima? return true if hosts_updated_at.nil? hosts_updated_at < Archiving.settings[:skip_host_fetch_time_period].ago end # Determines if a user has admin access to archiving or not # # @return [Boolean] def has_admin_access? admin? || moderator? end private def confirm_passwords if password.blank? self.errors.add(:password, 'Must give a password') return false end if password != retype_password self.errors.add(:password, 'Passwords mismatch') self.errors.add(:retype_password, 'Passwords mismatch') return false end true end end diff --git a/app/views/admin/users/_form.html.erb b/app/views/admin/users/_form.html.erb index f37c60c..b31a67f 100644 --- a/app/views/admin/users/_form.html.erb +++ b/app/views/admin/users/_form.html.erb @@ -1,15 +1,15 @@ <%= bootstrap_form_for(@user, url: url, method: method, layout: :horizontal, label_col: 'col-xs-3', control_col: 'col-xs-8') do |f| %> <%= f.text_field :username, required: true %> - <%= f.password_field :password, required: true %> - <%= f.password_field :retype_password, required: true %> + <%= f.password_field :password, required: new_user %> + <%= f.password_field :retype_password, required: new_user %> <%= f.email_field :email, required: true %>