#!/usr/bin/perl -wT
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Terry Weissman <terry@mozilla.org>
#                 Gervase Markham <gerv@gerv.net>
#                 A. Karl Kornel <karl@kornel.name>

use strict;
use lib qw(. lib);

use Bugzilla;
use Bugzilla::Mailer;
use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Token;
use Bugzilla::User;
use Bugzilla::Util;
use Date::Format;

my $template = Bugzilla->template;
my $ARGS = Bugzilla->input_params;
my $vars = {};

my $action = $ARGS->{action} || '';
my $target;

if (!$action)
{
    # redirect to index.cgi if no action is defined.
    print Bugzilla->cgi->redirect(correct_urlbase() . 'index.cgi');
    exit;
}
# prepare-sudo: Display the sudo information & login page
elsif ($action eq 'prepare-sudo')
{
    # We must have a logged-in user to do this
    # That user must be in the 'bz_sudoers' group
    my $user = Bugzilla->login(LOGIN_REQUIRED);
    unless ($user->in_group('bz_sudoers'))
    {
        ThrowUserError('auth_failure', {
            group => 'bz_sudoers',
            action => 'begin',
            object => 'sudo_session'
        });
    }

    # Do not try to start a new session if one is already in progress!
    if (defined Bugzilla->sudoer)
    {
        ThrowUserError('sudo_in_progress', { target => $user->login });
    }

    # Keep a temporary record of the user visiting this page
    $vars->{token} = issue_session_token('sudo_prepared');

    # Show the sudo page
    $vars->{target_login_default} = $ARGS->{target_login};
    $vars->{reason_default} = $ARGS->{reason};
    $target = 'admin/sudo.html.tmpl';
}
# begin-sudo: Confirm login and start sudo session
elsif ($action eq 'begin-sudo')
{
    # We must be sure that the user is authenticating by providing a login
    # and password.
    # We only need to do this for authentication methods that involve Bugzilla
    # directly obtaining a login (i.e. normal CGI login), as opposed to other
    # methods (like Environment vars login).

    # First, record if Bugzilla_login and Bugzilla_password were provided
    my $credentials_provided;
    if (defined $ARGS->{Bugzilla_login} && defined $ARGS->{Bugzilla_password})
    {
        $credentials_provided = 1;
    }

    # Next, log in the user
    my $user = Bugzilla->login(LOGIN_REQUIRED);

    # At this point, the user is logged in.  However, if they used a method
    # where they could have provided a username/password (i.e. CGI), but they
    # did not provide a username/password, then throw an error.
    if ($user->authorizer->can_login && !$credentials_provided)
    {
        ThrowUserError('sudo_password_required', {
            target_login => $ARGS->{target_login},
            reason => $ARGS->{reason},
        });
    }

    # The user must be in the 'bz_sudoers' group
    unless ($user->in_group('bz_sudoers'))
    {
        ThrowUserError('auth_failure', {
            group => 'bz_sudoers',
            action => 'begin',
            object => 'sudo_session',
        });
    }

    # Do not try to start a new session if one is already in progress!
    if (defined Bugzilla->sudoer)
    {
        ThrowUserError('sudo_in_progress', { target => $user->login });
    }

    # Did the user actually go trough the 'sudo-prepare' action?  Do some
    # checks on the token the action should have left.
    my ($token_user, $token_timestamp, $token_data) = Bugzilla::Token::GetTokenData($ARGS->{token});
    unless (defined($token_user) && defined($token_data) &&
        $token_user == $user->id && $token_data eq 'sudo_prepared')
    {
        ThrowUserError('sudo_preparation_required', {
            target_login => $ARGS->{target_login},
            reason => $ARGS->{reason},
        });
    }
    delete_token($ARGS->{token});

    # Get & verify the target user (the user who we will be impersonating)
    my $target_user = new Bugzilla::User({ name => $ARGS->{target_login} });
    unless (defined($target_user) && $target_user->id && $user->can_see_user($target_user))
    {
        ThrowUserError('user_match_failed', { name => $ARGS->{target_login} });
    }
    if ($target_user->in_group('bz_sudo_protect'))
    {
        ThrowUserError('sudo_protected', { login => $target_user->login });
    }

    # If we have a reason passed in, keep it under 200 characters
    my $reason = $ARGS->{reason} || '';
    $reason = substr($reason, 0, 200);

    # Calculate the session expiry time (T + 6 hours)
    my $time_string = time2str('%a, %d-%b-%Y %T %Z', time + MAX_SUDO_TOKEN_AGE, 'GMT');

    # For future sessions, store the unique ID of the target user
    my $token = Bugzilla::Token::_create_token($user->id, 'sudo', $target_user->id);
    Bugzilla->cgi->send_cookie(
        -name    => 'sudo',
        -expires => $time_string,
        -value   => $token
    );

    # For the present, change the values of Bugzilla::user & Bugzilla::sudoer
    Bugzilla->sudo_request($target_user, $user);

    # NOTE: If you want to log the start of an sudo session, do it here.

    # Go ahead and send out the message now
    my $message;
    my $mail_template = Bugzilla->template_inner($target_user->settings->{lang}->{value});
    $mail_template->process('email/sudo.txt.tmpl', { reason => $reason }, \$message);
    Bugzilla->template_inner("");
    MessageToMTA($message);

    $vars->{message} = 'sudo_started';
    $vars->{target} = $target_user->login;
    $target = 'global/message.html.tmpl';
}
# end-sudo: End the current sudo session (if one is in progress)
elsif ($action eq 'end-sudo')
{
    # Regardless of our state, delete the sudo cookie if it exists
    my $token = Bugzilla->cookies->{sudo};
    Bugzilla->cgi->remove_cookie('sudo');

    # Are we in an sudo session?
    Bugzilla->login(LOGIN_OPTIONAL);
    my $sudoer = Bugzilla->sudoer;
    if (defined($sudoer))
    {
        Bugzilla->sudo_request($sudoer, undef);
    }
    # Now that the session is over, remove the token from the DB.
    delete_token($token);

    # NOTE: If you want to log the end of an sudo session, do it here.
    $vars->{message} = 'sudo_ended';
    $target = 'global/message.html.tmpl';
}
# No valid action found
else
{
    Bugzilla->login(LOGIN_OPTIONAL);
    ThrowCodeError('unknown_action', {action => $action});
}

# Display the template
$template->process($target, $vars)
    || ThrowTemplateError($template->error());
exit;
