# Copyright 2002-2007 Interchange Development Group and others
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.  See the LICENSE file for details.
# 
# $Id: su.coretag,v 1.8 2007-03-30 23:40:54 pajamian Exp $

UserTag su Description  Switch User Tag for catalog superuser
UserTag su Order        username
UserTag su attrAlias    user username
UserTag su addAttr
UserTag su Version      $Revision: 1.8 $
UserTag su Routine      <<EOR
sub {
	my ($user, $opt) = @_;
	use vars qw/$Session $Tag $ready_safe $Scratch/;

	# Note: If adding any new %$opt keys, make sure to also add them to
	# the list of options to be stripped before passing the remainder
	# to tag userdb; search below for $new_user.

	$opt->{profile} = 'ui'     if $opt->{admin} and ! $opt->{profile};

	my $u;
	if($opt->{profile}) {
		$u = $Vend::Cfg->{UserDB_repository}{$opt->{profile}};
	}
	else {
		$u = $Vend::Cfg->{UserDB};
	}

	if(! $u) {
		my $place = $opt->{profile} || 'default';
		::logError("Can't find UserDB repository, profile '%s'", $place);
		return undef;
	}
	my $table  = $u->{database} || 'userdb';
	my $ufield = $u->{user_field} || 'username';
	my $going_to_admin = $u->{admin} || $opt->{admin};
#::logDebug("user table=$table ufield=$ufield");

	if ($opt->{create_user}) {
		# these settings must be done before any access to the table
		$Vend::WriteDatabase{$table} = 1;
	}

	my $super  = $Tag->if_mm('super');
	my $former = $Vend::username;

	if($user and $going_to_admin and ! $super) {
		::logError("attempt to su to admin user %s by non-super user %s",
						$user,
						$former,
					);
		return undef;
	}
	elsif($user and ! $Vend::admin) {
		::logError("attempt to su to user %s by non-admin user %s",
						$user,
						$former,
					);
		return undef;
	}

	my $dir = "$Global::ConfDir/tmp";
	if (! -d $dir) {
		if(-e $dir) {
			logGlobal("Global tmp directory exists as file, aborting su");
			return undef;
		}
		File::Path::mkpath($dir);
	}

	if($opt->{exit}) {
		if(! $Session->{su}) {
			logError("attempt to return to superuser without saved session.");
			return;
		}
		my $string = delete $Session->{su};
		my $key = $Tag->read_cookie({ name => 'MV_SU_KEY'})
			or do {
				logError("no session key in cookie, cannot exit");
				return;
			};
		my $fn = "$dir/$Session->{id}";
		open(MDCHECK, "< $fn")
			or do {
				logError("no saved session key in %s, cannot exit", $fn);
				return;
			};
		my $rand = <MDCHECK>;
		close MDCHECK;
		if(generate_key($rand . $string) ne $key) {
			logError("mismatched session key with saved session, cannot exit");
			return;
		}

		my $former = $Session->{username};
		## Authenticated
		undef $Vend::Session;
		undef $Session;
		$Vend::Session = $ready_safe->reval($string);
		$Session = $Vend::Session;
		delete $Session->{su};
		$Vend::admin = $Vend::Session->{admin};
		$Vend::username = $Vend::Session->{username};
		$Tag->if_mm('logged_in')
			and logError(
					"Admin user %s returned from login as %s",
					$Session->{username},
					$former,
				)
			and return 1;
		return;
	}
	elsif ($user) {
		my $new_user;
		if(! $Tag->data($table, $ufield, $user) ) {
			if ($opt->{create_user}) {
				$new_user = 1;
			}
			else {
				$Scratch->{ui_error} = errmsg("attempt to su to non-existent user %s", $user);
				return undef;
			}
		}

		my $rand	= random_string();
		my $sess	= uneval_it($Session);
#::logDebug("sess is $sess");
		my $sesskey	= generate_key($rand . $sess);

		open(MDIT, "> $dir/$Session->{id}")
			or die errmsg("Can't create check file for su: %s\n", $!);
		print MDIT $rand;
		close MDIT;
		$Tag->set_cookie( { name => 'MV_SU_KEY', value => $sesskey } );
		my $former = $Session->{username};

		undef $Vend::admin;
		undef $Vend::superuser;
		undef $Vend::UI_entry;

		Vend::Session::init_session();
		$Session = $Vend::Session;

		if ($new_user) {
			# pass on any non-su options to userdb tag
			my $newopt = { %$opt };
			delete @{$newopt}{qw( admin exit create_user )};
			$newopt->{username} = $user;
			my $result = $Tag->userdb('new_account', $newopt);
			unless ($result) {
				my $error = errmsg("Failed to create new user '%s' in su tag", $user);
				logError($error);
				$Scratch->{ui_error} = $error;
				return undef;
			}
			$Session->{su} = $sess;
		}
		else {
			$Vend::username = $Session->{username} = $user;
			$Vend::admin    = $Session->{admin}    = $going_to_admin;
			$Session->{logged_in} = 1;
			$Session->{su} = $sess;
			$Tag->userdb('load');
		}

		my $dest = $Tag->if_mm('logged_in') ? 'admin user' : 'regular user';
		logError(
			"superuser %s switched user to %s %s",
			$former,
			$dest,
			$Session->{username},
			);
		return 1;
	}
	else {
		::logError("unknown su operation: " . uneval_it($opt));
		return undef;
	}
}
EOR
