#!/usr/bin/env oo-ruby

require 'rubygems'
require 'getoptlong'

def usage
  puts <<USAGE
== Synopsis

#{File.basename $0}: Control user settings.

== Notes

  *** WARNING *** WARNING *** WARNING *** WARNING ***

  DO NOT USE THIS SCRIPT TO MODIFY A LOT OF USERS AT ONCE!

  *** WARNING *** WARNING *** WARNING *** WARNING ***

== Usage

#{File.basename $0} OPTIONS

Options:
  -l|--login <login_name>
    Login with OpenShift access (required)
  --setmaxgears <number>
    Set the maximum number of gears a user is allowed to use
  --setmaxtrackedstorage <number>
    Set the maximum additional storage per gear that will be tracked for a user
  --setmaxuntrackedstorage <number>
    Set the maximum additional storage per gear that will be untracked for a user
  --setconsumedgears <number>
    Set the number of gears a user has consumed (use carefully)
  --listsubaccounts
    List the subaccounts that have been created under this parent account (login)
  --addsubaccount <subaccount login>
    The sub account to add to the login parent account
  --removesubaccount <subaccount login>
    The sub account to remove from the login parent account
  --allowsubaccounts (true|false)
    Add / Remove the capability to manage sub accounts
  --allowplanupgrade (true|false)
    Add / Remove the capability to upgrade your plan.
  --allowprivatesslcertificates (true|false)
    Add / Remove the capability to add private SSL certificates
  --addgearsize <gearsize>
    Add gearsize to the capability for this login user
  --removegearsize <gearsize>
    Remove gearsize from the capability for this login user
  --inheritgearsizes (true|false)
    Allow / Disallow inheritance of login user gearsizes capability to sub accounts
  -h|--help
    Show Usage info

Examples:
  List the current user settings with:
    #{File.basename $0} -l bob@redhat.com

  Set the maximum number of gears a user is allowed to use with:
    #{File.basename $0} -l bob@redhat.com --setmaxgears 10
USAGE
  exit 255
end

class String
    def to_b()
        return true if self.to_s.strip =~ /^(true|t|yes|y|1)$/i

        return false
    end
end

def set_max_gears(user, maxgears)
    user_capabilities = user.get_capabilities
    if user_capabilities["max_gears"] == maxgears
        puts "User already has max_gears set to #{user.max_gears}"
        return
    end

    print "Setting max_gears to #{maxgears}... "
    user_capabilities["max_gears"]=maxgears
    user.set_capabilities(user_capabilities)
    if user.save
        puts "Done."
    else
        puts "An error occurred saving the user."
        exit 6
    end
end

def set_max_tracked_storage(user, maxtrackedstorage)
    user_capabilities = user.get_capabilities
    if user_capabilities["max_tracked_addtl_storage_per_gear"] == maxtrackedstorage
        puts "User already has max_tracked_addtl_storage_per_gear set to #{user_capabilities['max_tracked_addtl_storage_per_gear']}"
        return
    end

    print "Setting max_tracked_addtl_storage_per_gear to #{maxtrackedstorage}... "
    user_capabilities["max_tracked_addtl_storage_per_gear"]=maxtrackedstorage
    user.set_capabilities(user_capabilities)
    if user.save
        puts "Done."
    else
        puts "An error occurred saving the user."
        exit 6
    end
end

def set_max_untracked_storage(user, maxuntrackedstorage)
    user_capabilities = user.get_capabilities
    if user_capabilities["max_untracked_addtl_storage_per_gear"] == maxuntrackedstorage
        puts "User already has max_untracked_addtl_storage_per_gear set to #{user_capabilities['max_untracked_addtl_storage_per_gear']}"
        return
    end

    print "Setting max_untracked_addtl_storage_per_gear to #{maxuntrackedstorage}... "
    user_capabilities["max_untracked_addtl_storage_per_gear"]=maxuntrackedstorage
    user.set_capabilities(user_capabilities)
    if user.save
        puts "Done."
    else
        puts "An error occurred saving the user."
        exit 6
    end
end

def set_consumed_gears(user, consumedgears)
    old_value = user.consumed_gears
    if user.consumed_gears == consumedgears
        puts "User already has consumed_gears set to #{user.consumed_gears}"
        return
    end

    print "Setting consumed_gears to #{consumedgears}... "
    result = CloudUser.where(login: user.login, consumed_gears: old_value).find_and_modify({ "$set" => { "consumed_gears" => consumedgears } } )
    if result.nil?
      puts "User's consumed_gear count changed between read and write, try again."
      return
    end
    puts "Done."
end

def allow_sub_accounts(user, allow)
    # user.capabilities_will_change!
    user_capabilities = user.get_capabilities
    if user_capabilities['subaccounts'] == allow
        puts "User already has allowsubaccounts set to #{allow}"
        return
    end

    print "Setting subaccounts capability to #{allow} for user #{user.login}... "
    user_capabilities['subaccounts'] = allow
    user.set_capabilities(user_capabilities)
    if user.save
        puts "Done."
    else
        puts "An error occurred modifying the user capabilities."
        exit 6
    end
end

def allow_private_ssl_certificates(user, allow)
    # user.capabilities_will_change!
    user_capabilities = user.get_capabilities
    if user_capabilities['private_ssl_certificates'] == allow
        puts "User already has allow private_ssl_certificates set to #{allow}"
        return
    end

    print "Setting private_ssl_certificates capability to #{allow} for user #{user.login}... "
    user_capabilities['private_ssl_certificates'] = allow
    user.set_capabilities(user_capabilities)
    if user.save
        puts "Done."
    else
        puts "An error occurred modifying the user capabilities."
        exit 6
    end
end

def allow_plan_upgrade(user, allow)
    user_capabilities = user.get_capabilities
    if user_capabilities['plan_upgrade_enabled'] == allow
        puts "User already has plan_upgrade_enabled set to #{allow}"
        return
    end

    print "Setting plan_upgraded_enabled capability to #{allow} for user #{user.login}... "
    user_capabilities['plan_upgrade_enabled'] = allow
    user.set_capabilities(user_capabilities)
    if user.save
        puts "Done."
    else
        puts "An error occurred modifying the user capabilities."
        exit 6
    end
end

def add_sub_account(user, subaccount_login)
    user_capabilities = user.get_capabilities
    unless user_capabilities['subaccounts']
        puts "User #{user.login} does not have the capability to manage sub accounts"
        return
    end

    begin
        child_user = CloudUser::find_by_identity(subaccount_login)
    rescue Mongoid::Errors::DocumentNotFound
        child_user = nil
    end
    
    if not child_user.nil?
        if child_user.parent_user_id == user._id
            puts "Error: Subaccount for '#{subaccount_login}' already exists under #{user.login}"
        elsif not child_user.parent_user_id.nil?
            parent_user = CloudUser.with(consistency: :eventual).find_by(_id: child_user.parent_user_id)
            puts "Error: Subaccount for '#{subaccount_login}' already exists under #{parent_user.login}"
        else
            puts "Error: User '#{subaccount_login}' already exists"
        end
        exit 5
    end
    
    print "Adding subaccount for #{subaccount_login} under #{user.login}... "
    child_user = CloudUser.new(login: subaccount_login, parent_user_id: user._id)
    if child_user.save
        puts "Done."
    else
        puts "An error occurred adding the sub account #{subaccount_login}."
        exit 6
    end
    Lock.create_lock(child_user)
end

def remove_sub_account(user, subaccount_login)
    user_capabilities = user.get_capabilities
    unless user_capabilities['subaccounts']
        puts "User #{user.login} does not have the capability to manage sub accounts"
        return
    end

	  begin
        child_user = CloudUser::find_by_identity(subaccount_login)
    rescue Mongoid::Errors::DocumentNotFound
        puts "Error: Sub Account User '#{subaccount_login}' not found"
        exit 5
    end
    
    if child_user.parent_user_id.nil? || (child_user.parent_user_id != user._id)
        puts "Error: User '#{subaccount_login}' is not a sub account of #{user.login}"
        exit 5
    end
    
    print "Removing subaccount for #{child_user.login} under #{user.login}... "
    begin
      child_user.force_delete
    rescue Exception => e
      puts "An error occurred removing the sub account for #{subaccount_login} : #{e.message}"
      exit 6
    end
    puts "Done."
end

def add_gear_size(user, gear_size)
    user_capabilities = user.get_capabilities
    print "Adding gear size #{gear_size} for user #{user.login}... "

    available_sizes = Rails.configuration.openshift[:gear_sizes]
    if ! available_sizes.include? gear_size
      puts "Size #{gear_size} is not defined. Defined sizes are: #{available_sizes.join ', '}."
      exit 1
    end

    gear_sizes = Array(user_capabilities['gear_sizes'])
    if gear_sizes.include?(gear_size)
        puts "User #{user.login} already has gear size #{gear_size} in its capabilities."
        return
    end

    user_capabilities['gear_sizes'] = (gear_sizes << gear_size)
    user.set_capabilities(user_capabilities)
    if user.save
        puts "Done."
    else
        puts "An error occurred adding the gear size #{gear_size} to user #{user.login}."
        exit 6
    end
end

def remove_gear_size(user, gear_size)
    print "Removing gear size #{gear_size} for user #{user.login}... "
    
    user_capabilities = user.get_capabilities
    unless user_capabilities["gear_sizes"].include?(gear_size)
        puts "User #{user.login} does not have gear size #{gear_size} in its capabilities."
        return
    end
    
    user_capabilities["gear_sizes"].delete(gear_size)
    user.set_capabilities(user_capabilities)
    user.save
end

def inherit_on_subaccounts(user, allow, capability, cap_name)
    user_capabilities = user.get_capabilities
    user_capabilities['inherit_on_subaccounts'] = [] unless user_capabilities.has_key?('inherit_on_subaccounts')
 
    if user_capabilities['inherit_on_subaccounts'].include?(capability) == allow
        puts "User already has #{cap_name} inheritance set to #{allow}"
        return
    end

    # user.capabilities_will_change!
    print "Setting #{cap_name} inheritance to #{allow} for user #{user.login}... "
    if allow
      user_capabilities['inherit_on_subaccounts'].push(capability)
    else
      user_capabilities['inherit_on_subaccounts'].delete(capability)
    end
    
    user.set_capabilities(user_capabilities)
    if user.save
        puts "Done."
    else
        puts "An error occurred modifying the user capabilities."
        exit 6
    end
end

opts = GetoptLong.new(
    ["--login",          "-l", GetoptLong::REQUIRED_ARGUMENT],
    ["--setmaxgears",      GetoptLong::REQUIRED_ARGUMENT],
    ["--setmaxtrackedstorage", GetoptLong::REQUIRED_ARGUMENT],
    ["--setmaxuntrackedstorage", GetoptLong::REQUIRED_ARGUMENT],
    ["--setconsumedgears", GetoptLong::REQUIRED_ARGUMENT],
    ["--listsubaccounts",  GetoptLong::NO_ARGUMENT],
    ["--addsubaccount",    GetoptLong::REQUIRED_ARGUMENT],
    ["--removesubaccount", GetoptLong::REQUIRED_ARGUMENT],
    ["--allowsubaccounts", GetoptLong::REQUIRED_ARGUMENT],
    ["--allowprivatesslcertificates", GetoptLong::REQUIRED_ARGUMENT],
    ["--allowplanupgrade", GetoptLong::REQUIRED_ARGUMENT],
    ["--addgearsize",      GetoptLong::REQUIRED_ARGUMENT],
    ["--removegearsize",   GetoptLong::REQUIRED_ARGUMENT],
    ["--inheritgearsizes", GetoptLong::REQUIRED_ARGUMENT],
    ["--help",             "-h", GetoptLong::NO_ARGUMENT]
)

args = {}
begin
  opts.each{ |k,v| args[k]=v }
rescue GetoptLong::Error => e
  usage
end

login = args["--login"]
allowsubaccounts = args["--allowsubaccounts"].to_b if args["--allowsubaccounts"]
allowprivatesslcertificates = args["--allowprivatesslcertificates"].to_b if args["--allowprivatesslcertificates"]
inheritgearsizes = args["--inheritgearsizes"].to_b if args["--inheritgearsizes"]

if args["--setmaxgears"]
  unless args["--setmaxgears"] =~ /^[0-9]+$/
    puts "ERROR: Max gears must be a positive integer"
    exit 1
  end
  maxgears = args["--setmaxgears"].to_i
end

if args["--setmaxtrackedstorage"]
  unless args["--setmaxtrackedstorage"] =~ /^[0-9]+$/
    puts "ERROR: Max tracked storage must be a positive integer"
    exit 1
  end
  maxtrackedstorage = args["--setmaxtrackedstorage"].to_i
end

if args["--setmaxuntrackedstorage"]
  unless args["--setmaxuntrackedstorage"] =~ /^[0-9]+$/
    puts "ERROR: Max untracked storage must be a positive integer"
    exit 1
  end
  maxuntrackedstorage = args["--setmaxuntrackedstorage"].to_i
end

if args["--setconsumedgears"]
  unless args["--setconsumedgears"] =~ /^[0-9]+$/
    puts "ERROR: Consumed gears must be a positive integer"
    exit 1
  end
  consumedgears = args["--setconsumedgears"].to_i
end

if login.nil? or args["--help"]
  usage
end

account_to_add = args["--addsubaccount"]
account_to_remove = args["--removesubaccount"]
gear_size_to_add = args["--addgearsize"]
gear_size_to_remove = args["--removegearsize"]

# this require is here to not load the environment simply to display help
require '/var/www/openshift/broker/config/environment'
# Disable analytics for admin scripts
Rails.configuration.analytics[:enabled] = false
puts
puts

begin
    user = CloudUser.find_by_identity(login)
rescue Mongoid::Errors::DocumentNotFound
    puts "Error: User '#{login}' not found"
    exit 5
end

changed_user = false
subaccount_list = []

unless maxgears.nil?
    set_max_gears(user, maxgears)
    changed_user = true
end

unless maxtrackedstorage.nil?
    set_max_tracked_storage(user,maxtrackedstorage)
    changed_user = true
end

unless maxuntrackedstorage.nil?
    set_max_untracked_storage(user,maxuntrackedstorage)
    changed_user = true
end

unless consumedgears.nil?
    set_consumed_gears(user, consumedgears)
    changed_user = true
end

unless allowsubaccounts.nil?
    allow_sub_accounts(user, allowsubaccounts)
    changed_user = true
end

unless args["--allowplanupgrade"].nil?
  allow_plan_upgrade(user, args["--allowplanupgrade"].to_b)
end

unless allowprivatesslcertificates.nil?
    allow_private_ssl_certificates(user, allowprivatesslcertificates)
    changed_user = true
end

unless account_to_add.nil?
    add_sub_account(user, account_to_add)
end

unless account_to_remove.nil?
    remove_sub_account(user, account_to_remove)
end

unless gear_size_to_add.nil?
    add_gear_size(user, gear_size_to_add)
    changed_user = true
end

unless gear_size_to_remove.nil?
    remove_gear_size(user, gear_size_to_remove)
    changed_user = true
end

unless inheritgearsizes.nil?
    inherit_on_subaccounts(user, inheritgearsizes, 'gear_sizes', 'gearsizes')
    changed_user = true
end

if args["--listsubaccounts"]
    subaccount_list = CloudUser.where(parent_user_id: user._id) 
end

if changed_user
    # reload user with new settings
    user = CloudUser.find_by_identity(login)
    puts
    puts
end

user_capabilities = user.get_capabilities

# print out the user's current settings
puts "User #{user.login}:"
puts "                  consumed gears: #{user.consumed_gears}"
puts "                       max gears: #{user_capabilities['max_gears']}"
puts "    max tracked storage per gear: #{user_capabilities['max_tracked_addtl_storage_per_gear'] || 0}"
puts "  max untracked storage per gear: #{user_capabilities['max_untracked_addtl_storage_per_gear'] || 0}"
puts "            plan upgrade enabled: #{user_capabilities['plan_upgrade_enabled']}"
puts "                      gear sizes: #{user_capabilities['gear_sizes'].join(', ')}" if user_capabilities.has_key?('gear_sizes')
puts "            sub accounts allowed: #{user_capabilities['subaccounts']}" if user_capabilities.has_key?('subaccounts')
puts "private SSL certificates allowed: #{user_capabilities['private_ssl_certificates']}" if user_capabilities.has_key?('private_ssl_certificates')
puts "              inherit gear sizes: #{user_capabilities['inherit_on_subaccounts'].include?('gear_sizes')}" if user_capabilities.has_key?('inherit_on_subaccounts')
puts 
if args["--listsubaccounts"] and (not subaccount_list.nil?) and (not subaccount_list.empty?)
    puts "Sub Accounts: #{subaccount_list.length}"
    subaccount_list.each do |subaccount|
        puts "     #{subaccount.login}"
    end
    puts
end
