#!/usr/bin/env ruby

# -------------------------------------------------------------------------- #
# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org)             #
#                                                                            #
# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
# not use this file except in compliance with the License. You may obtain    #
# a copy of the License at                                                   #
#                                                                            #
# http://www.apache.org/licenses/LICENSE-2.0                                 #
#                                                                            #
# Unless required by applicable law or agreed to in writing, software        #
# distributed under the License is distributed on an "AS IS" BASIS,          #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
# See the License for the specific language governing permissions and        #
# limitations under the License.                                             #
#--------------------------------------------------------------------------- #

ONE_LOCATION=ENV["ONE_LOCATION"]

if !ONE_LOCATION
    RUBY_LIB_LOCATION="/usr/lib/one/ruby"
else
    RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
end

$: << RUBY_LIB_LOCATION
$: << RUBY_LIB_LOCATION+"/cli"

require 'command_parser'
require 'one_helper/oneuser_helper'

cmd=CommandParser::CmdParser.new(ARGV) do
    usage "`oneuser` <command> [<args>] [<options>]"
    version OpenNebulaHelper::ONE_VERSION

    ########################################################################
    # Global Options
    ########################################################################
    set :option, CommandParser::OPTIONS

    list_options = CLIHelper::OPTIONS
    list_options << OpenNebulaHelper::XML
    list_options << OpenNebulaHelper::NUMERIC

    READ_FILE={
        :name => "read_file",
        :short => "-r",
        :large => "--read-file",
        :description => "Read password from file"
    }

    SHA1={
        :name => "sha1",
        :large => "--sha1",
        :description => "The password will be hashed using the sha1 algorithm"
    }

    SSH={
        :name => "ssh",
        :large => "--ssh",
        :description => "SSH Auth system",
        :proc => lambda { |o, options|
            options[:driver] = OpenNebula::User::SSH_AUTH
        }
    }

    X509={
        :name => "x509",
        :large => "--x509",
        :description => "x509 Auth system for x509 certificates",
        :proc => lambda { |o, options|
            options[:driver] = OpenNebula::User::X509_AUTH
        }
    }

    X509_PROXY={
        :name => "x509_proxy",
        :large => "--x509_proxy",
        :description => "x509 Auth system based on x509 proxy certificates",
        :proc => lambda { |o, options|
            options[:driver] = OpenNebula::User::X509_PROXY_AUTH
        }
    }

    KEY={
        :name => "key",
        :short => "-k path_to_private_key_pem",
        :large => "--key path_to_private_key_pem",
        :format => String,
        :description => "Path to the Private Key of the User"
    }

    CERT={
        :name => "cert",
        :short => "-c path_to_user_cert_pem",
        :large => "--cert path_to_user_cert_pem",
        :format => String,
        :description => "Path to the Certificate of the User"
    }

    PROXY={
        :name => "proxy",
        :large => "--proxy path_to_user_proxy_pem",
        :format => String,
        :description => "Path to the user proxy certificate"
    }

    TIME={
        :name  => "time",
        :large => "--time x",
        :format => Integer,
        :description => "Token duration in seconds, defaults to 3600 (1 h)"
    }

    DRIVER={
        :name => "driver",
        :large => "--driver driver",
        :format => String,
        :description => "Driver to autehnticate this user"
    }

    create_options = [READ_FILE, SHA1, SSH, X509, KEY, CERT, DRIVER]
    login_options  = [SSH, X509, X509_PROXY, KEY, CERT, PROXY, TIME]

    ########################################################################
    # Formatters for arguments
    ########################################################################
    set :format, :groupid, OpenNebulaHelper.rname_to_id_desc("GROUP") do |arg|
        OpenNebulaHelper.rname_to_id(arg, "GROUP")
    end

    set :format, :userid, OneUserHelper.to_id_desc do |arg|
        helper = OneUserHelper.new
        helper.to_id(arg)
    end

    set :format, :userid_list, OneUserHelper.list_to_id_desc do |arg|
        helper = OneUserHelper.new
        helper.list_to_id(arg)
    end

    set :format, :password, OneUserHelper.password_to_str_desc do |arg|
        OneUserHelper.password_to_str(arg, options)
    end

    ########################################################################
    # Commands
    ########################################################################

    create_desc = <<-EOT.unindent
        Creates a new User
        Examples:
          oneuser create my_user my_password
          oneuser create my_user -r /tmp/mypass
          oneuser create my_user --ssh --key /tmp/id_rsa
          oneuser create my_user --ssh -r /tmp/public_key
          oneuser create my_user --x509 --cert /tmp/my_cert.pem
    EOT

    command :create, create_desc, :username, [:password, nil],
            :options=>create_options do
        helper = OneUserHelper.new

        if args[1]
            pass = args[1]
        else
            rc = helper.password(options)
            if rc.first == 0
                pass = rc[1]
            else
                exit_with_code *rc
            end
        end

        driver = options[:driver] || OpenNebula::User::CORE_AUTH

        helper.create_resource(options) do |user|
            user.allocate(args[0], pass, driver)
        end
    end

    update_desc = <<-EOT.unindent
        Launches the system editor to modify and update the template contents
    EOT

    command :update, update_desc, :userid do
        helper = OneUserHelper.new

        helper.perform_action(args[0],options,"modified") do |user|
            str = OpenNebulaHelper.update_template(args[0], user)
            user.update(str)
        end
    end

    login_desc = <<-EOT.unindent
        Creates the Login token for authentication
        Examples:
          oneuser login my_user --ssh --key /tmp/id_rsa --time 72000
          oneuser login my_user --x509 --cert /tmp/my_cert.pem \
                                --key /tmp/my_key.pk --time 72000
          oneuser login my_user --x509_proxy --proxy /tmp/my_cert.pem \
                                --time 72000
    EOT

    command :login, login_desc, :username, :options=>login_options do
        OneUserHelper.login(args[0], options)
    end

    key_desc = <<-EOT.unindent
        Shows a public key from a private SSH key. Use it as password
        for the SSH authentication mechanism.
    EOT

    command :key, key_desc, :options=>[KEY] do
        require 'ssh_auth'

        options[:key] ||= ENV['HOME']+'/.ssh/id_rsa'

        begin
            sshauth = SshAuth.new(:private_key=>options[:key])
        rescue Exception => e
            exit_with_code -1, e.message
        end

        puts sshauth.public_key
        exit_with_code 0
    end


    delete_desc = <<-EOT.unindent
        Deletes the given User
    EOT

    command :delete, delete_desc, [:range, :userid_list] do
        helper = OneUserHelper.new
        helper.perform_actions(args[0],options,"deleted") do |user|
            user.delete
        end
    end

    passwd_desc = <<-EOT.unindent
        Changes the given User's password
    EOT

    command :passwd, passwd_desc, :userid, [:password, nil],
            :options=>create_options do
        helper = OneUserHelper.new

        if args[1]
            pass = args[1]
        else
            rc = helper.password(options)
            if rc.first == 0
                pass = rc[1]
            else
                exit_with_code *rc
            end
        end

        helper.perform_action(args[0],options,"Password changed") do |user|
            user.passwd(pass)
        end
    end

    chgrp_desc = <<-EOT.unindent
        Changes the User's main group
    EOT

    command :chgrp, chgrp_desc, [:range, :userid_list], :groupid do
        helper = OneUserHelper.new
        helper.perform_actions(args[0],options,"Group changed") do |user|
            user.chgrp(args[1].to_i)
        end
    end

    chauth_desc = <<-EOT.unindent
        Changes the User's auth driver and its password (optional)
        Examples:
          oneuser chauth my_user core
          oneuser chauth my_user core new_password
          oneuser chauth my_user core -r /tmp/mypass
          oneuser chauth my_user --ssh --key /home/oneadmin/.ssh/id_rsa
          oneuser chauth my_user --ssh -r /tmp/public_key
          oneuser chauth my_user --x509 --cert /tmp/my_cert.pem
    EOT

    command :chauth, chauth_desc, :userid, [:auth, nil], [:password, nil],
            :options=>create_options do
        if options[:driver]
            driver = options[:driver]
        elsif args[1]
            driver = args[1]
        else
            exit_with_code 0, "An Auth driver should be specified"
        end

        helper = OneUserHelper.new

        if args[2]
            pass = args[2]
        else
            rc = helper.password(options)
            if rc.first == 0
                pass = rc[1]
            else
                pass = ""
            end
        end

        helper.perform_action(args[0],
                            options,
                            "Auth driver and password changed") do |user|
            user.chauth(driver, pass)
        end
    end

    list_desc = <<-EOT.unindent
        Lists Users in the pool
    EOT

    command :list, list_desc, :options=>list_options do
        helper = OneUserHelper.new
        helper.list_pool(options)
    end

    show_desc = <<-EOT.unindent
        Shows information for the given User
    EOT

    command :show, show_desc, [:userid, nil],
            :options=>OpenNebulaHelper::XML do
        user=args[0] || OpenNebula::User::SELF
        helper = OneUserHelper.new
        helper.show_resource(user,options)
    end

end
