From da45fee0077b7b27a0554cffa153ef048ca5f13a Mon Sep 17 00:00:00 2001
From: "Leigh B. Stoller" <stoller@flux.utah.edu>
Date: Wed, 10 Jan 2007 19:11:56 +0000
Subject: [PATCH] Remove a bunch of queries on the users table, switching to
 user object methods.

Change a bunch of queries that join on the users table to use the idx
instead of the uid. This will need to be completed before we can really
archive users away, but close at this point.
---
 bugdb/addbugdbuser.in             |  18 ++--
 bugdb/setbugdbgroups.in           |  19 ++--
 collab/jabber/addjabberuser.in    |  20 ++---
 collab/mailman/addmmuser.in       |  30 +++----
 collab/mailman/delmmuser.in       |  27 ++----
 collab/mailman/mmmodifymember.in  |  29 +++----
 collab/mailman/mmsetup.in         |   6 +-
 db/User.pm.in                     |  34 ++++++--
 db/elabinelab_bossinit.in         |   4 +-
 db/genelists.in                   |  15 ++--
 db/libdb.pm.in                    | 138 +++++++++++-------------------
 db/libdb.py.in                    |   2 +-
 db/webcontrol.in                  |  13 +--
 tbsetup/checkup/checkup_daemon.in |  40 +++++----
 tbsetup/elabinelab.in             |  10 +--
 tbsetup/exports_setup.in          |   4 +-
 tbsetup/libtestbed.pm.in          |  28 +++---
 tbsetup/plab/libplab.py.in        |   2 +-
 tbsetup/repos_daemon.in           |  30 +++----
 tbsetup/setgroups.in              |  48 +++++------
 tbsetup/sfskey_update.in          |   4 +-
 tmcd/tmcd.c                       |  16 ++--
 utils/firstuser.in                |   9 +-
 utils/opsdb_control.in            |  24 +++---
 wiki/addwikiuser.in               |  38 ++++----
 wiki/delwikiuser.in               |  14 ++-
 wiki/setwikigroups.in             |  24 ++++--
 www/dbdefs.php3.in                |   1 +
 www/news-rss.php3                 |   4 +-
 www/plabstats.php3                |   4 +-
 www/tbauth.php3                   |  16 +++-
 www/user_defs.php                 |  15 +++-
 32 files changed, 329 insertions(+), 357 deletions(-)

diff --git a/bugdb/addbugdbuser.in b/bugdb/addbugdbuser.in
index d3ae26402f..309786ba08 100644
--- a/bugdb/addbugdbuser.in
+++ b/bugdb/addbugdbuser.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2005, 2006 University of Utah and the Flux Group.
+# Copyright (c) 2005, 2006, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use English;
@@ -50,6 +50,7 @@ sub fatal($);
 use lib "@prefix@/lib";
 use libdb;
 use libtestbed;
+use User;
 
 #
 # We don't want to run this script unless its the real version.
@@ -105,15 +106,14 @@ else {
     die("Bad data in uid: $uid");
 }
 
-# Need the password hash ...
-$query_result =
-    DBQueryFatal("select u.usr_pswd from users as u ".
-		 "where u.uid='$uid'");
-
-if ($query_result->numrows == 0) {
-    fatal("No such user $uid!");
+# Map target user to object.
+my $target_user = User->Lookup($uid);
+if (! defined($target_user)) {
+    fatal("$uid does not exist!");
 }
-my ($passhash) = $query_result->fetchrow_array();
+
+# Need the password hash ...
+my $passhash = $target_user->pswd();
 
 # shell escape.
 $passhash =~ s/\$/\\\$/g;
diff --git a/bugdb/setbugdbgroups.in b/bugdb/setbugdbgroups.in
index ffced4d952..32707f4eeb 100644
--- a/bugdb/setbugdbgroups.in
+++ b/bugdb/setbugdbgroups.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2005, 2006 University of Utah and the Flux Group.
+# Copyright (c) 2005, 2006, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use English;
@@ -50,6 +50,7 @@ sub fatal($);
 use lib "@prefix@/lib";
 use libdb;
 use libtestbed;
+use User;
 
 #
 # We don't want to run this script unless its the real version.
@@ -102,6 +103,13 @@ else {
     die("Bad data in user: $user.");
 }
 
+# Map target user to object.
+my $target_user = User->Lookup($user);
+if (! defined($target_user)) {
+    fatal("$user does not exist!");
+}
+my $user_dbid = $target_user->dbid();
+
 #
 # This script always does the right thing, so no permission checks.
 # In fact, all it does is call over to ops to run a script over there.
@@ -113,7 +121,8 @@ else {
 my $query_result =
     DBQueryFatal("select p.pid,p.trust from group_membership as p ".
 		 "left join groups as g on g.pid=p.pid and g.gid=p.gid ".
-		 "where uid='$user' and p.pid=g.gid and trust!='none'");
+		 "where uid_idx='$user_dbid' and p.pid=g.gid and ".
+		 "      trust!='none'");
 
 while (my ($pid,$trust) = $query_result->fetchrow_array()) {
     #
@@ -136,10 +145,8 @@ exit(0)
 # report bugs about Emulab!
 #
 # Admin users ... TBAdmin() test does not work for this test ...
-$query_result =
-    DBQueryFatal("select admin from users where uid='$user'");
-my ($isadmin) = $query_result->fetchrow_array();
-if ($isadmin) {
+#
+if ($target_user->admin()) {
     push(@glist, "Emulab/admin");
 }
 else {
diff --git a/collab/jabber/addjabberuser.in b/collab/jabber/addjabberuser.in
index 614725e884..d054aa6270 100644
--- a/collab/jabber/addjabberuser.in
+++ b/collab/jabber/addjabberuser.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2005 University of Utah and the Flux Group.
+# Copyright (c) 2005, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use English;
@@ -47,6 +47,7 @@ $| = 1;
 use lib "@prefix@/lib";
 use libdb;
 use libtestbed;
+use User;
 
 #
 # We don't want to run this script unless its the real version.
@@ -105,20 +106,11 @@ else {
 # Note that adduser will just update the password if the user already
 # exist in the wiki. 
 #
-
-#
-# Look in the DB to see if there is already a wikiname defined. If
-# we use that. Otherwise have to form one from the user name. Ick.
-#
-my $query_result =
-    DBQueryFatal("select mailman_password ".
-		 "from users where uid='$user'");
-
-if (!$query_result->numrows) {
-    fatal("No such user $user in the DB!");
+my $target_user = User->Lookup($user);
+if (! defined($target_user)) {
+    fatal("$user does not exist!");
 }
-my ($password) = $query_result->fetchrow_array();
-
+my $password = $target_user->mailman_password();
 if (!defined($password)) {
     fatal("No password defined for $user!");
 }
diff --git a/collab/mailman/addmmuser.in b/collab/mailman/addmmuser.in
index ef5b3013e6..7b1e6b585c 100644
--- a/collab/mailman/addmmuser.in
+++ b/collab/mailman/addmmuser.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2005 University of Utah and the Flux Group.
+# Copyright (c) 2005, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use English;
@@ -54,6 +54,7 @@ $| = 1;
 use lib "@prefix@/lib";
 use libdb;
 use libtestbed;
+use User;
 
 #
 # We don't want to run this script unless its the real version.
@@ -80,14 +81,6 @@ if (! $MAILMANSUPPORT) {
     exit(0);
 }
 
-#
-# Get user DB uid.
-#
-if (! UNIX2DBUID($UID, \$dbuid)) {
-    die("*** $0:\n".
-        "    You do not exist in the Emulab Database!\n");
-}
-
 #
 # Parse command arguments. Once we return from getopts, all that should be
 # left are the required arguments.
@@ -114,15 +107,13 @@ else {
     die("Bad data in uid: $target_uid");
 }
 
-my $query_result =
-    DBQueryFatal("select usr_email, mailman_password, usr_name ".
-		 "from users where uid='$target_uid'");
-
-fatal("No such user in DB: $target_uid!")
-    if (!$query_result->numrows);
-
-my ($email, $password, $fullname) = $query_result->fetchrow_array();
-
+my $target_user = User->Lookup($target_uid);
+if (! defined($target_user)) {
+    fatal("$target_uid does not exist!");
+}
+my $email    = $target_user->email();
+my $password = $target_user->mailman_password();
+my $fullname = $target_user->name();
 
 #
 # Note that since we are sending cleartext passwords over, pipe the info
@@ -141,6 +132,9 @@ if ($CONTROL ne $BOSSNODE) {
     TBScriptLock("mailman_update") == 0 or
 	fatal("Could not get the lock!");
 
+    # Watch for embedded quotes.
+    $fullname =~ s/(\')/\'\\'\'/g;
+
     system("echo \"$password \'$fullname\'\" | ".
            "$SSH -host $CONTROL $MMPROXY $optarg adduser $target_uid $email");
 
diff --git a/collab/mailman/delmmuser.in b/collab/mailman/delmmuser.in
index acd0ac8da8..418e0a05df 100644
--- a/collab/mailman/delmmuser.in
+++ b/collab/mailman/delmmuser.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2005 University of Utah and the Flux Group.
+# Copyright (c) 2005, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use English;
@@ -54,6 +54,7 @@ $| = 1;
 use lib "@prefix@/lib";
 use libdb;
 use libtestbed;
+use User;
 
 #
 # We don't want to run this script unless its the real version.
@@ -80,14 +81,6 @@ if (! $MAILMANSUPPORT) {
     exit(0);
 }
 
-#
-# Get user DB uid.
-#
-if (! UNIX2DBUID($UID, \$dbuid)) {
-    die("*** $0:\n".
-        "    You do not exist in the Emulab Database!\n");
-}
-
 #
 # Parse command arguments. Once we return from getopts, all that should be
 # left are the required arguments.
@@ -114,18 +107,12 @@ else {
     die("Bad data in uid: $target_uid");
 }
 
-my $query_result =
-    DBQueryFatal("select usr_email, mailman_password, usr_name ".
-		 "from users where uid='$target_uid'");
-
-fatal("No such user in DB: $target_uid!")
-    if (!$query_result->numrows);
-
-my ($email, $password, $fullname) = $query_result->fetchrow_array();
+my $target_user = User->Lookup($target_uid);
+if (! defined($target_user)) {
+    fatal("$target_uid does not exist!");
+}
+my $email = $target_user->email();
 
-#
-# Note that since we are sending cleartext passwords over, pipe the info
-# into its STDIN so that the passwords are not visible in a ps listing.
 #
 # For ssh.
 #
diff --git a/collab/mailman/mmmodifymember.in b/collab/mailman/mmmodifymember.in
index 9b6da6921e..83d4b3141b 100644
--- a/collab/mailman/mmmodifymember.in
+++ b/collab/mailman/mmmodifymember.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2005 University of Utah and the Flux Group.
+# Copyright (c) 2005, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use English;
@@ -53,6 +53,7 @@ $| = 1;
 use lib "@prefix@/lib";
 use libdb;
 use libtestbed;
+use User;
 
 #
 # We don't want to run this script unless its the real version.
@@ -79,14 +80,6 @@ if (! $MAILMANSUPPORT) {
     exit(0);
 }
 
-#
-# Get user DB uid.
-#
-if (! UNIX2DBUID($UID, \$dbuid)) {
-    die("*** $0:\n".
-        "    You do not exist in the Emulab Database!\n");
-}
-
 #
 # Parse command arguments. Once we return from getopts, all that should be
 # left are the required arguments.
@@ -113,14 +106,13 @@ else {
     die("Bad data in uid: $target_uid");
 }
 
-my $query_result =
-    DBQueryFatal("select usr_email, mailman_password, usr_name ".
-		 "from users where uid='$target_uid'");
-
-fatal("No such user in DB: $target_uid!")
-    if (!$query_result->numrows);
-
-my ($email, $password, $fullname) = $query_result->fetchrow_array();
+my $target_user = User->Lookup($target_uid);
+if (! defined($target_user)) {
+    fatal("$target_uid does not exist!");
+}
+my $email    = $target_user->email();
+my $password = $target_user->mailman_password();
+my $fullname = $target_user->name();
 
 #
 # Note that since we are sending cleartext passwords over, pipe the info
@@ -139,6 +131,9 @@ if ($CONTROL ne $BOSSNODE) {
     TBScriptLock("mailman_update") == 0 or
 	fatal("Could not get the lock!");
 
+    # Watch for embedded quotes.
+    $fullname =~ s/(\')/\'\\'\'/g;
+
     system("echo \"$password \'$fullname\'\" | ".
            "  $SSH -host $CONTROL $MMPROXY ".
 	   "  $optarg modifymember $target_uid $email");
diff --git a/collab/mailman/mmsetup.in b/collab/mailman/mmsetup.in
index b56c7a468e..c660341d84 100644
--- a/collab/mailman/mmsetup.in
+++ b/collab/mailman/mmsetup.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -w
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2005, 2006 University of Utah and the Flux Group.
+# Copyright (c) 2005, 2006, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use English;
@@ -52,6 +52,7 @@ $| = 1;
 use lib "@prefix@/lib";
 use libdb;
 use libtestbed;
+use User;
 
 #
 # If no bugdb support, just exit. 
@@ -89,7 +90,8 @@ my $optarg = ($debug ? "-d" : "");
 # Initialize a mailman password for all users. 
 #
 my $query_result =
-    DBQueryFatal("select uid from users where mailman_password is NULL");
+    DBQueryFatal("select uid from users ".
+		 "where mailman_password is NULL and status!='archived'");
 
 while (my ($uid) = $query_result->fetchrow_array()) {
     print "Setting initial mailman password for $uid\n"
diff --git a/db/User.pm.in b/db/User.pm.in
index 5b701ba518..edf41da2c1 100644
--- a/db/User.pm.in
+++ b/db/User.pm.in
@@ -78,6 +78,7 @@ sub mysystem($)
 sub Lookup($$)
 {
     my ($class, $token) = @_;
+    my $status_archived = $USERSTATUS_ARCHIVED;
     my $query_result;
 
     # Look in cache first
@@ -88,13 +89,18 @@ sub Lookup($$)
     # For backwards compatability, look to see if the token is numeric
     # or alphanumeric. If numeric, assumes its an idx, otherwise a name.
     #
-    if ($token =~ /^\d*$/) {
+    if ($token =~ /^\d+$/) {
 	$query_result =
 	    DBQueryWarn("select * from users where uid_idx='$token'");
     }
-    elsif ($token =~ /^\w*$/) {
+    elsif ($token =~ /^\w+$/) {
+	# When looking up by uid, only look for non-archived users;
+	# Must use an idx if you really want an archived user. This
+	# will prevent problems with code that has not yet been
+	# changed to use the idx field.
 	$query_result =
-	    DBQueryWarn("select * from users where uid='$token'");
+	    DBQueryWarn("select * from users ".
+			"where uid='$token' and status!='$status_archived'");
     }
     else {
 	return undef;
@@ -208,10 +214,12 @@ sub LookupByUnixId($$)
 sub LookupByWikiName($$)
 {
     my ($class, $wikiname) = @_;
-
+    my $status_archived = $USERSTATUS_ARCHIVED;
+    
     my $query_result =
 	DBQueryFatal("select uid_idx from users ".
-		     "where wikiname='$wikiname'");
+		     "where wikiname='$wikiname' and ".
+		     "      status!='$status_archived'");
 
     return undef
 	if (! $query_result || !$query_result->numrows);
@@ -228,10 +236,12 @@ sub LookupByWikiName($$)
 sub LookupByEmail($$)
 {
     my ($class, $email) = @_;
+    my $status_archived = $USERSTATUS_ARCHIVED;
 
     my $query_result =
 	DBQueryFatal("select uid_idx from users ".
-		     "where LCASE(usr_email)=LCASE('$email')");
+		     "where LCASE(usr_email)=LCASE('$email') and ".
+		     "      status!='$status_archived'");
 
     return undef
 	if (! $query_result || !$query_result->numrows);
@@ -440,6 +450,18 @@ sub ThisUser($)
     return User->LookupByUnixId($UID);
 }
 
+#
+# The "implied" user is the user the web interface says we are running as.
+#
+sub ImpliedUser($)
+{
+    return undef
+	if (! exists($ENV{'HTTP_INVOKING_USER'}));
+    
+    # The lookup routine checks it argument, so no need to taint check.
+    return User->Lookup($ENV{'HTTP_INVOKING_USER'});
+}
+
 #
 # Refresh a class instance by reloading from the DB.
 #
diff --git a/db/elabinelab_bossinit.in b/db/elabinelab_bossinit.in
index 2f3a136075..0c1c1d543e 100755
--- a/db/elabinelab_bossinit.in
+++ b/db/elabinelab_bossinit.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2004 University of Utah and the Flux Group.
+# Copyright (c) 2000-2004, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use English;
@@ -99,7 +99,7 @@ mysystem("$TB/sbin/mkproj $pid");
 #
 my $users_result =
     DBQueryFatal("select distinct u.uid,u.admin from group_membership as m ".
-		 "left join users as u on u.uid=m.uid ".
+		 "left join users as u on u.uid_idx=m.uid_idx ".
 		 "where u.status='" . USERSTATUS_ACTIVE() . "'");
 while (my ($uid,$admin) = $users_result->fetchrow_array()) {
     next
diff --git a/db/genelists.in b/db/genelists.in
index 8342889b17..fa3b3829fa 100644
--- a/db/genelists.in
+++ b/db/genelists.in
@@ -2,7 +2,7 @@
 
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2006 University of Utah and the Flux Group.
+# Copyright (c) 2000-2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use Fcntl ':flock';
@@ -73,6 +73,7 @@ use lib "@prefix@/lib";
 use libdb;
 use libtestbed;
 use libtblog;
+use User;
 
 #
 # We don't want to run this script unless its the real version.
@@ -300,7 +301,7 @@ sub ActiveUsers()
 	   DBQuery("SELECT DISTINCT u.usr_email from experiments as e ".
 		   "left join group_membership as p ".
 		   "     on e.pid=p.pid and p.pid=p.gid ".
-		   "left join users as u on u.uid=p.uid ".
+		   "left join users as u on u.uid_idx=p.uid_idx ".
 		   "where u.status='active' and ".
 		   "      e.state='active' ".
 		   "order by u.usr_email"))) {
@@ -325,7 +326,7 @@ sub RecentUsers()
 
     if (! ($query_result =
 	   DBQuery("select distinct u.usr_email from user_stats as s ".
-		   "left join users as u on u.uid=s.uid ".
+		   "left join users as u on u.uid_idx=s.uid_idx ".
 		   "where ((UNIX_TIMESTAMP(now()) - ".
 		   "       UNIX_TIMESTAMP(s.last_activity)) <= $limit) ".
 		   "order by u.usr_email"))) {
@@ -352,7 +353,7 @@ sub RecentProjects()
 	   DBQuery("select distinct u.usr_email from project_stats as s ".
 		   "left join group_membership as g on ".
 		   "  g.pid=s.pid and g.gid=g.pid ".
-		   "left join users as u on u.uid=g.uid ".
+		   "left join users as u on u.uid_idx=g.uid_idx ".
 		   "where u.status='active' and ".
 		   "      ((UNIX_TIMESTAMP(now()) - ".
 		   "       UNIX_TIMESTAMP(s.last_activity)) <= $limit) ".
@@ -379,7 +380,7 @@ sub RecentProjectLeaders()
 	   DBQuery("select distinct u.usr_email from project_stats as s ".
 		   "left join group_membership as g on ".
 		   "  g.pid=s.pid and g.gid=g.pid ".
-		   "left join users as u on u.uid=g.uid ".
+		   "left join users as u on u.uid_idx=g.uid_idx ".
                    "left join projects as p on u.uid=p.head_uid ".
 		   "where u.status='active' and ".
 		   "      ((UNIX_TIMESTAMP(now()) - ".
@@ -424,7 +425,7 @@ sub WideAreaPeople()
     my $query_result =
 	DBQueryFatal("SELECT DISTINCT u.usr_email from projects as p ".
 		     "left join group_membership as m on m.pid=p.pid ".
-		     "left join users as u on u.uid=m.uid ".
+		     "left join users as u on u.uid_idx=m.uid_idx ".
 		     "where p.approved!=0 and p.pcremote_ok is not null ".
 		     "      and m.trust!='none' and u.status='active' ".
 		     "order by usr_email");
@@ -442,7 +443,7 @@ sub ProjectLeaders()
 		     ($MAILMANSUPPORT ?
 		      ", u.uid ,u.usr_name, u.mailman_password " : "") .
 		     "  from projects as p ".
-		     "left join users as u on u.uid=p.head_uid ".
+		     "left join users as u on u.uid_idx=p.head_idx ".
 		     "where p.approved!=0 ".
 		     "order by usr_email");
 
diff --git a/db/libdb.pm.in b/db/libdb.pm.in
index 938cfccfce..b82cea3005 100644
--- a/db/libdb.pm.in
+++ b/db/libdb.pm.in
@@ -499,6 +499,7 @@ sub USERSTATUS_FROZEN()		{ "frozen"; }
 sub USERSTATUS_UNAPPROVED()	{ "unapproved"; }
 sub USERSTATUS_UNVERIFIED()	{ "unverified"; }
 sub USERSTATUS_NEWUSER()	{ "newuser"; }
+sub USERSTATUS_ARCHIVED()	{ "archived"; }
 
 #
 # We want valid project membership to be non-zero for easy membership
@@ -911,19 +912,26 @@ sub TBGrpTrust($$$)
 	$gid = $pid;
     }
 
+    #
+    # Must map to an existing user to be trusted, obviously
+    #
+    my $target_user = User->Lookup($uid);
+    return PROJMEMBERTRUST_NONE
+	if (! defined($target_user));
+    my $uid_idx = $target_user->uid_idx();
+
     #
     # User must be active to be trusted.
     #
-    my $query_result =
-	DBQueryFatal("select status from users ".
-		     "where uid='$uid' and status='" . USERSTATUS_ACTIVE() . "'");
-    if ($query_result->numrows == 0) {
-	return PROJMEMBERTRUST_NONE;
-    }
+    return PROJMEMBERTRUST_NONE
+	if ($target_user->status() ne USERSTATUS_ACTIVE());
 
+    #
+    # Must be a member of the group.
+    #
     $query_result =
 	DBQueryFatal("select trust from group_membership ".
-		     "where uid='$uid' and pid='$pid' and gid='$gid'");
+		     "where uid_idx='$uid_idx' and pid='$pid' and gid='$gid'");
 
     #
     # No membership is the same as no trust. True? Maybe an error instead?
@@ -954,20 +962,15 @@ sub TBProjTrust($$)
 }
 
 #
-# Test admin status. Optional argument is the UID or Name to test. If not
-# provided, then test the current UID.
-#
-# XXX Argument is *either* a numeric UID, or a string name.
+# Test admin status. Ignore argument; we only care if the current user
+# has admin privs turned on.
 #
-# usage: TBAdmin([int or char* uid]);
+# usage: TBAdmin();
 #        returns 1 if an admin type.
 #        returns 0 if a mere user.
 #
 sub TBAdmin(;$)
 {
-    my($uid) = @_;
-    my($name);
-
     #
     # No one is considered an admin unless they have the magic environment
     # variable set (so that you have to be a bit more explict about wanting
@@ -979,54 +982,27 @@ sub TBAdmin(;$)
 	return 0;
     }
 
-    if (!defined($uid)) {
-	$uid = $UID;
-    }
-
-    #
-    # Test if numeric. Map to name if it is.
-    #
-    if ($uid =~ /^[0-9]+$/) {
-	($name) = getpwuid($uid)
-	    or die "$uid not in passwd file\n";
-    }
-    else {
-	$name = $uid;
-    }
-
-    my $query_result =
-	DBQueryFatal("select admin from users where uid='$name'");
+    # Map current user to object and confirm admin status from db.
+    my $this_user = User->ThisUser();
+    return 0
+	if (! defined($this_user));
 
-    my @row = $query_result->fetchrow_array();
-    if ($row[0] == 1) {
-	return 1;
-    }
-    return 0;
+    return $this_user->admin();
 }
 
 #
 # Test whether current user is a member of the emulab-ops project.
+# We ignore the argument; always test the current user. 
 # 
 sub TBOpsGuy(;$)
 {
-    my($uid) = @_;
-    my($name);
-
-    if (!defined($uid)) {
-	$uid = $UID;
-    }
+    # Map current user to object and confirm admin status from db.
+    my $this_user = User->ThisUser();
+    return 0
+	if (! defined($this_user));
 
-    #
-    # Test if numeric. Map to name if it is.
-    #
-    if ($uid =~ /^[0-9]+$/) {
-	($name) = getpwuid($uid)
-	    or die "$uid not in passwd file\n";
-    }
-    else {
-	$name = $uid;
-    }
-    return TBMinTrust(TBProjTrust($name, $TBOPSPID), PROJMEMBERTRUST_USER());
+    return TBMinTrust(TBProjTrust($this_user->uid_idx(), $TBOPSPID),
+		      PROJMEMBERTRUST_USER());
 }
 
 #
@@ -1495,7 +1471,7 @@ sub TBLeaderMailList($;$) {
     # have the strings in variables...
     my $query_result =
     DBQueryFatal("select distinct usr_name,u.uid,usr_email from users as u ".
-                 "left join group_membership as gm on gm.uid=u.uid ".
+                 "left join group_membership as gm on gm.uid_idx=u.uid_idx ".
                  "where (trust='project_root' and pid='$pid') or ".
 		 "(trust='group_root' and pid='$pid' and gid='$gid') ".
                  "order by trust DESC, usr_name");
@@ -2477,20 +2453,16 @@ sub TBImageLoadMaxOkay($$;@)
 #        returns 1 if the UID is okay.
 #        returns 0 if the UID is bogus.
 #
-sub UserDBInfo ($$$) {
-    my($dbuid, $username, $useremail) = @_;
-
-    my $query_result =
-	DBQueryWarn("select usr_name,usr_email from users ".
-		    "where uid='$dbuid'");
-
-    if (!$query_result || $query_result->num_rows < 1) {
-	return 0;
-    }
+sub UserDBInfo($$$)
+{
+    my ($dbuid, $username, $useremail) = @_;
 
-    my @row = $query_result->fetchrow_array();
-    $$username  = $row[0];
-    $$useremail = $row[1];
+    my $target_user = User->Lookup($dbuid);
+    return 0
+	if (! defined($target_user));
+    
+    $$username  = $target_user->name();
+    $$useremail = $target_user->email();
     return 1;
 }
 
@@ -2544,10 +2516,7 @@ sub TBUnixGroupList ($) {
 }
 
 #
-# Map UID to DB UID (login). Does a DB check to make sure user is known to
-# the DB (user obviously has a regular account), and that account will
-# always match what the DB says. Redundant, I know. But consider it a
-# sanity (or consistency) check.
+# Map UID to DB UID (login). This function will eventually be tossed.
 #
 # usage: UNIX2DBUID(int uid, \$login)
 #        returns 1 if the UID is okay.
@@ -2556,23 +2525,11 @@ sub TBUnixGroupList ($) {
 sub UNIX2DBUID ($$) {
     my($unix_uid, $userlogin) = @_;
 
-    my $query_result =
-	DBQueryFatal("select uid from users where unix_uid='$unix_uid'");
-
-    if ($query_result->num_rows < 1) {
-	return 0;
-    }
-    my @row = $query_result->fetchrow_array();
-
-    my ($pwname) = getpwuid($unix_uid) or
-	die("*** $unix_uid is not in the password file!");
-
-    if ($row[0] ne $pwname) {
-	warn("*** WARNING: $pwname does not match $row[0]\n");
-	return 0;
-    }
+    my $target_user = User->LookupByUnixId($unix_uid);
+    return 0
+	if (! defined($target_user));
 
-    $$userlogin = $row[0];
+    $$userlogin = $target_user->uid();
     return 1;
 }
 
@@ -4160,9 +4117,10 @@ sub TBNodeUpdateAccountsByUID($)
     my $query_result =
 	DBQueryFatal("select p.pid,pcremote_ok from users as u ".
 		     "left join group_membership as g on ".
-		     "  u.uid=g.uid and g.pid=g.gid ".
+		     "  u.uid_idx=g.uid_idx and g.pid=g.gid ".
 		     "left join projects as p on p.pid=g.pid ".
-		     "where u.uid='$uid' and p.pid is not null");
+		     "where u.uid='$uid' and u.status='active' and ".
+		     "      p.pid is not null");
 
     while (my %row = $query_result->fetchhash()) {
 	my $pid      = $row{'pid'};
diff --git a/db/libdb.py.in b/db/libdb.py.in
index c1a4b8a7bd..80eed5dad6 100644
--- a/db/libdb.py.in
+++ b/db/libdb.py.in
@@ -318,7 +318,7 @@ def TBMapUIDtoIDX(uid):
     uid = DBQuoteSpecial(uid)
     
     qres = DBQueryFatal("select uid_idx from users "
-                        "where uid=%s", (uid,))
+                        "where uid=%s and status!='archived'", (uid,))
 
     if len(qres) == 0:
         return 0
diff --git a/db/webcontrol.in b/db/webcontrol.in
index 52d5c22fdf..b935927c8c 100644
--- a/db/webcontrol.in
+++ b/db/webcontrol.in
@@ -2,7 +2,7 @@
 
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2003 University of Utah and the Flux Group.
+# Copyright (c) 2000-2003, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use English;
@@ -103,16 +103,7 @@ if ($setlogin) {
 	#
 	# Find all non admins and log them out.
 	# 
-	my $query_result =
-	    DBQueryFatal("select users.uid from login ".
-			 "left join users on login.uid=users.uid ".
-			 "where users.admin=0");
-	
-	while (my @row = $query_result->fetchrow_array()) {
-	    my $uid = $row[0];
-
-	    DBQueryFatal("delete from login where uid='$uid'");
-	}
+	DBQueryFatal("delete from login where adminon=0'");
     }
 }
 
diff --git a/tbsetup/checkup/checkup_daemon.in b/tbsetup/checkup/checkup_daemon.in
index d4f40753f3..9c5bda0393 100644
--- a/tbsetup/checkup/checkup_daemon.in
+++ b/tbsetup/checkup/checkup_daemon.in
@@ -2,7 +2,7 @@
 
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2005, 2006 University of Utah and the Flux Group.
+# Copyright (c) 2005, 2006, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 
@@ -40,9 +40,8 @@ my $TBOPS    = "@TBOPSEMAIL@";
 use lib "@prefix@/lib";
 use libdb;
 use libtestbed;
-
-# Be careful not to exit on transient error
-$libdb::DBQUERY_MAXTRIES = 30;
+use User;
+use Project;
 
 #
 # These come from the library.
@@ -53,7 +52,6 @@ my $TBOPSPID= TBOPSPID;
 my $HOME= USERROOT() . "/elabckup";
 
 sub fatal($);
-sub notify($);
 sub daemonize();
 sub misconfig($$);
 sub IsNodeFree($);
@@ -98,25 +96,22 @@ if (! $debug) {
 
 print "Checkup daemon starting... pid $$, at ".`date`;
 
-# Switch to the elabckup user.
-my $query_result = 
-    DBQueryFatal("select unix_uid from users where uid='elabckup'");
-
-if (! $query_result || $query_result->numrows == 0) {
-    fatal("Cannot get elabckup uid\n");
-}
-
-my ($ev_uid) = $query_result->fetchrow_array;
+# Need the unix uid for the backup user.
+my $user = User->Lookup('elabckup');
+fatal("Could not get object for backup user")
+    if (!defined($user));
+my $ev_uid   = $user->unix_uid();
 
-my ($unix_gid, $unix_gidname);
-if (! TBGroupUnixInfo($TBOPSPID, $TBOPSPID, \$unix_gid, \$unix_gidname)) {
-    die("*** $0:\n".
-	"    Could not get unix group info for $TBOPSPID!\n");
-}
+# and need the unix gid for the group.
+my $project = Project->Lookup($TBOPSPID);
+fatal("Could not get object for $TBOPSPID project")
+    if (!defined($project));
+my $unix_gid = $project->unix_gid();
 
 print "Experiment head: $ev_uid\n"
     if ($debug);
 
+# Switch to the elabckup user.
 $GID = $unix_gid;
 $EGID = "$unix_gid";
 $EUID = $UID = $ev_uid;
@@ -454,3 +449,10 @@ sub daemonize()
 
     return 0;
 }
+
+sub fatal($) {
+    my($mesg) = $_[0];
+
+    die("*** $0:\n".
+	"    $mesg\n");
+}
diff --git a/tbsetup/elabinelab.in b/tbsetup/elabinelab.in
index 67670d7881..2cb31d5671 100644
--- a/tbsetup/elabinelab.in
+++ b/tbsetup/elabinelab.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2004-2006 University of Utah and the Flux Group.
+# Copyright (c) 2004-2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 # TODO: ntpinfo table.
@@ -697,10 +697,10 @@ sub DumpDBGoo()
 
 	DBQueryWarn("create temporary table temp_$table ".
 		    "select t.* from group_membership as gm ".
-		    "left join users as u on u.uid=gm.uid ".
-		    "left join $table as t on t.uid=u.uid ".
+		    "left join users as u on u.uid_idx=gm.uid_idx ".
+		    "left join $table as t on t.uid_idx=u.uid_idx ".
 		    "where gm.pid='$pid' and gm.gid=gm.pid ".
-		    " and t.uid is not NULL and ".
+		    " and t.uid_idx is not NULL and ".
 		    " u.status='" . USERSTATUS_ACTIVE() . "'")
 	    or die("*** $0:\n".
 		   "    Could not create table temp_$table\n");
@@ -721,7 +721,7 @@ sub DumpDBGoo()
 
     # The group_membership is also special.
     DBQueryWarn("select gm.* from group_membership as gm ".
-		"left join users as u on u.uid=gm.uid ".
+		"left join users as u on u.uid_idx=gm.uid_idx ".
 		"where (gm.pid='$pid' or ".
 		"       gm.pid='" . TBOPSPID() . "') and ".
 		" u.status='" . USERSTATUS_ACTIVE() . "' ".
diff --git a/tbsetup/exports_setup.in b/tbsetup/exports_setup.in
index 25cb185345..248b298059 100644
--- a/tbsetup/exports_setup.in
+++ b/tbsetup/exports_setup.in
@@ -2,7 +2,7 @@
 
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2006 University of Utah and the Flux Group.
+# Copyright (c) 2000-2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 
@@ -233,7 +233,7 @@ while (@row = $nodes_result->fetchrow_array) {
 	# XXX needs to be fixed for shared experiments?
 	$users_result =
 	    DBQueryFatal("select distinct g.uid from group_membership as g ".
-			 "left join users as u on u.uid=g.uid ".
+			 "left join users as u on u.uid_idx=g.uid_idx ".
 			 "where g.pid='$pid' and g.gid='$gid' and ".
 			 "      (g.trust!='none' and ".
 			 "       u.webonly=0 and ".
diff --git a/tbsetup/libtestbed.pm.in b/tbsetup/libtestbed.pm.in
index 5d362a8a28..75d9f3f5b6 100644
--- a/tbsetup/libtestbed.pm.in
+++ b/tbsetup/libtestbed.pm.in
@@ -2,7 +2,7 @@
 
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2006 University of Utah and the Flux Group.
+# Copyright (c) 2000-2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 
@@ -18,7 +18,7 @@ use Exporter;
 	 TBSCRIPTLOCK_OKAY TBSCRIPTLOCK_TIMEDOUT
 	 TBSCRIPTLOCK_IGNORE TBSCRIPTLOCK_FAILED
 	 PROJROOT GROUPROOT USERROOT SCRATCHROOT SHAREROOT
-	 TBValidUserDir TBValidUserDirList);
+	 TBValidUserDir TBValidUserDirList TBMakeTempFile);
 
 # After package decl.
 use English;
@@ -291,23 +291,31 @@ sub TBBackGround($)
 }
 
 #
-# Create a logname and untaint it!
+# Create a temporary file, untaint the name, return it. 
 #
-sub TBMakeLogname($)
+sub TBMakeTempFile($)
 {
     my($prefix) = @_;
-    my $logname;
+    my $fname;
     
-    $logname = `mktemp /tmp/${prefix}.XXXXXX`;
+    $fname = `mktemp /tmp/${prefix}.XXXXXX`;
 
-    if ($logname =~ /^([-\@\w\.\/]+)$/) {
-	$logname = $1;
+    if ($fname =~ /^([-\@\w\.\/]+)$/) {
+	$fname = $1;
     }
     else {
-	die("Bad data in logfile name: $logname");
+	die("Bad data in filename: $fname");
     }
 
-    return $logname;
+    return $fname;
+}
+
+# Ditto for a temporary file.
+sub TBMakeLogname($)
+{
+    my ($prefix) = @_;
+
+    return TBMakeTempFile($prefix);
 }
 
 #
diff --git a/tbsetup/plab/libplab.py.in b/tbsetup/plab/libplab.py.in
index eb9f37b43a..d4729b24e2 100644
--- a/tbsetup/plab/libplab.py.in
+++ b/tbsetup/plab/libplab.py.in
@@ -1087,7 +1087,7 @@ class Slice:
         try:
             qres = DBQueryFatal("select u.uid, u.usr_email from users as u "
                                 "left join experiments as e "
-                                "on u.uid = e.expt_swap_uid "
+                                "on u.uid_idx = e.swapper_idx "
                                 "where e.pid=%s and e.eid=%s",
                                 (self.pid, self.eid))
             if not len(qres):
diff --git a/tbsetup/repos_daemon.in b/tbsetup/repos_daemon.in
index 165064354d..9aee0ff8fe 100644
--- a/tbsetup/repos_daemon.in
+++ b/tbsetup/repos_daemon.in
@@ -2,7 +2,7 @@
 
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2005, 2006 University of Utah and the Flux Group.
+# Copyright (c) 2005, 2006, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 
@@ -49,6 +49,8 @@ my $FLOOR = 4;
 use lib "@prefix@/lib";
 use libdb;
 use libtestbed;
+use User;
+use Project;
 
 # Be careful not to exit on transient error
 $libdb::DBQUERY_MAXTRIES = 30;
@@ -128,21 +130,17 @@ if (! $debug) {
 
 print "Repositioning Daemon starting... pid $$, at ".`date`;
 
-# XXX Need to get an emulab-ops user to run the event system as.
-$query_result = 
-    DBQueryFatal("select unix_uid from users where uid='elabman'");
-
-if (! $query_result || $query_result->numrows == 0) {
-    fatal("Cannot get elabman uid\n");
-}
-
-my ($ev_uid) = $query_result->fetchrow;
-
-my ($unix_gid, $unix_gidname);
-if (! TBGroupUnixInfo($REPOSPID, $REPOSPID, \$unix_gid, \$unix_gidname)) {
-    die("*** $0:\n".
-	"    Could not get unix group info for $REPOSPID!\n");
-}
+# Need the unix uid for the emulab-ops user.
+my $user = User->Lookup('elabman');
+fatal("Could not get object for emulab-ops user")
+    if (!defined($user));
+my $ev_uid   = $user->unix_uid();
+
+# and need the unix gid for the group.
+my $project = Project->Lookup($REPOSEID);
+fatal("Could not get object for $REPOSEID project")
+    if (!defined($project));
+my $unix_gid = $project->unix_gid();
 
 print "Experiment head: $ev_uid\n"
     if ($debug);
diff --git a/tbsetup/setgroups.in b/tbsetup/setgroups.in
index 86da3db839..865d6ca645 100755
--- a/tbsetup/setgroups.in
+++ b/tbsetup/setgroups.in
@@ -2,7 +2,7 @@
 
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2006 University of Utah and the Flux Group.
+# Copyright (c) 2000-2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 
@@ -88,6 +88,7 @@ use lib "@prefix@/lib";
 use libaudit;
 use libdb;
 use libtestbed;
+use User;
 
 #
 # We do not want to run this script unless its the real version.
@@ -155,21 +156,13 @@ if (!defined($pid) && !scalar(@userlist)) {
     usage();
 }
 
-#
-# Get user DB uid.
-#
-if (! UNIX2DBUID($UID, \$dbuid)) {
-    die("*** $0:\n".
-        "    You do not exist in the Emulab Database!\n");
-}
-
-#
-# Get email info.
-#
-if (! UserDBInfo($dbuid, \$user_name, \$user_email)) {
-    die("*** $0:\n".
-        "    Cannot determine email info for you!\n");
+# Map invoking user to object.
+my $this_user = User->ThisUser();
+if (! defined($this_user)) {
+    fatal("You ($UID) do not exist!");
 }
+my $user_name  = $this_user->name();
+my $user_email = $this_user->email();
 
 #
 # This script always does the right thing, so it does not matter who
@@ -215,9 +208,12 @@ foreach my $uid (@userlist) {
     my $groupargument;
     my $project;
 
-    $query_result = DBQueryFatal("select webonly from users ".
-				 "where uid='$uid' and webonly=1");
-    if ($query_result->numrows) {
+    my $user = User->Lookup($uid);
+    fatal("Could not map user $uid to object")
+	if (!defined($user));
+    my $uid_idx = $user->uid_idx();
+
+    if ($user->webonly()) {
 	print "Skipping $uid; webonly account!\n";
 	next;
     }
@@ -231,7 +227,8 @@ foreach my $uid (@userlist) {
     $query_result =
 	DBQueryFatal("select g.unix_name from group_membership as m ".
 		     "left join groups as g on m.pid=g.pid and m.gid=g.gid ".
-		     "where m.uid='$uid' and m.pid=m.gid and m.trust!='none'");
+		     "where m.uid_idx='$uid_idx' and m.pid=m.gid and ".
+		     "      m.trust!='none'");
 
     if (!$query_result->numrows) {
 	#
@@ -240,13 +237,8 @@ foreach my $uid (@userlist) {
 	# (non-fatal) since there can be group members not approved,
 	# and this is called from the editgroups web page.
 	#
-	$query_result =
-	    DBQueryFatal("select status from users ".
-			 "where uid='$uid' and webonly=0 ".
-			 " and status='" . USERSTATUS_ACTIVE . "'");
-
-	if (!$query_result->numrows) {
-	    print "Skipping $uid; not in any groups!\n";
+	if ($user->status() ne USERSTATUS_ACTIVE()) {
+	    print "Skipping $uid; not an active user yet!\n";
 	    next;
 	}
 	push(@groupnames, "guest");
@@ -269,8 +261,8 @@ foreach my $uid (@userlist) {
     $query_result =
 	DBQueryFatal("select g.unix_name from group_membership as m ".
 		     "left join groups as g on m.pid=g.pid and m.gid=g.gid ".
-		     "where m.uid='$uid' and m.pid!=m.gid ".
-		     " and m.trust!='none'");
+		     "where m.uid_idx='$uid_idx' and m.pid!=m.gid and ".
+		     "      m.trust!='none'");
 
     while (@db_row = $query_result->fetchrow_array() ) {
 	    my $groupname = $db_row[0];
diff --git a/tbsetup/sfskey_update.in b/tbsetup/sfskey_update.in
index ac4c31f551..5dbc2ddd20 100644
--- a/tbsetup/sfskey_update.in
+++ b/tbsetup/sfskey_update.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2003 University of Utah and the Flux Group.
+# Copyright (c) 2000-2003, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use English;
@@ -145,7 +145,7 @@ else {
 #
 my $query_result =
     DBQueryFatal("select sfs.pubkey from users as u ".
-		 "left join user_sfskeys as sfs on sfs.uid=u.uid ".
+		 "left join user_sfskeys as sfs on sfs.uid_idx=u.uid_idx ".
 		 "where u.status='active' and u.webonly=0");
 
 while (my ($pubkey) = $query_result->fetchrow_array()) {
diff --git a/tmcd/tmcd.c b/tmcd/tmcd.c
index 88978ab30f..95c82584d8 100644
--- a/tmcd/tmcd.c
+++ b/tmcd/tmcd.c
@@ -1797,7 +1797,7 @@ COMMAND_PROTOTYPE(doaccounts)
 				 "  UNIX_TIMESTAMP(u.usr_modified), "
 				 "  u.usr_email,u.usr_shell "
 				 "from group_membership as p "
-				 "left join users as u on p.uid=u.uid "
+				 "left join users as u on p.uid_idx=u.uid_idx "
 				 "left join groups as g on p.pid=g.pid "
 				 "where p.trust!='none' "
 				 "      and u.webonly=0 "
@@ -1822,7 +1822,7 @@ COMMAND_PROTOTYPE(doaccounts)
 				 "  u.widearearoot,u.wideareajailroot, "
 				 "  u.usr_w_pswd "
 				 "from group_membership as p "
-				 "left join users as u on p.uid=u.uid "
+				 "left join users as u on p.uid_idx=u.uid_idx "
 				 "left join groups as g on "
 				 "     p.pid=g.pid and p.gid=g.gid "
 				 "where ((p.pid='%s')) and p.trust!='none' "
@@ -1845,7 +1845,7 @@ COMMAND_PROTOTYPE(doaccounts)
 			     "  u.widearearoot,u.wideareajailroot, "
 			     "  u.usr_w_pswd "
 			     "from group_membership as p "
-			     "left join users as u on p.uid=u.uid "
+			     "left join users as u on p.uid_idx=u.uid_idx "
 			     "left join groups as g on "
 			     "     p.pid=g.pid and p.gid=g.gid "
 			     "where (p.pid='%s') and p.trust!='none' "
@@ -1877,7 +1877,7 @@ COMMAND_PROTOTYPE(doaccounts)
 				 "  on m.pid=p.pid "
 				 "left join groups as g on "
 				 "  g.pid=m.pid and g.gid=m.gid "
-				 "left join users as u on u.uid=m.uid "
+				 "left join users as u on u.uid_idx=m.uid_idx "
 				 "where p.approved!=0 "
 				 "      and FIND_IN_SET('%s',pcremote_ok)>0 "
 				 "      and m.trust!='none' "
@@ -2205,7 +2205,7 @@ COMMAND_PROTOTYPE(doaccounts)
 				 "u.usr_email,u.usr_shell, "
 				 "u.widearearoot,u.wideareajailroot "
 				 "from widearea_accounts as w "
-				 "left join users as u on u.uid=w.uid "
+				 "left join users as u on u.uid_idx=w.uid_idx "
 				 "where w.trust!='none' and "
 				 "      u.status='active' and "
 				 "      node_id='%s' "
@@ -3192,7 +3192,8 @@ COMMAND_PROTOTYPE(domounts)
 	 */
 #ifdef  NOSHAREDEXPTS
 	res = mydb_query("select u.uid from users as u "
-			 "left join group_membership as p on p.uid=u.uid "
+			 "left join group_membership as p on "
+			 "     p.uid_idx=u.uid_idx "
 			 "where p.pid='%s' and p.gid='%s' and "
 			 "      u.status='active' and "
 			 "      u.webonly=0 and "
@@ -3202,7 +3203,8 @@ COMMAND_PROTOTYPE(domounts)
 	res = mydb_query("select distinct u.uid from users as u "
 			 "left join exppid_access as a "
 			 " on a.exp_pid='%s' and a.exp_eid='%s' "
-			 "left join group_membership as p on p.uid=u.uid "
+			 "left join group_membership as p on "
+			 "     p.uid_idx=u.uid_idx "
 			 "where ((p.pid='%s' and p.gid='%s') or p.pid=a.pid) "
 			 "       and u.status='active' and "
 			 "       u.webonly=0 and "
diff --git a/utils/firstuser.in b/utils/firstuser.in
index 2762202b6c..1ccfa60a36 100755
--- a/utils/firstuser.in
+++ b/utils/firstuser.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -w
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2006 University of Utah and the Flux Group.
+# Copyright (c) 2000-2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 #
@@ -14,6 +14,7 @@ use Getopt::Std;
 use lib '@prefix@/lib';
 use libdb;
 use libtestbed;
+use User;
 
 my $tbadmin    = '@TBADMINGROUP@';
 my $ELABINELAB = @ELABINELAB@;
@@ -72,9 +73,9 @@ if (defined($opts{e})) {
     $protouser_email = $opts{e};
 }
 
-my $result = DBQueryFatal("select * from users where uid='$protouser'");
-if ($result->num_rows()) {
-    die "This script has already been run, there is no need to run it again\n";
+my $user = User->Lookup('$protouser');
+if (defined($user)) {
+    die("This script has already been run, no need to run it again!\n");
 }
 
 if ($UID != 0) {
diff --git a/utils/opsdb_control.in b/utils/opsdb_control.in
index ad83d9c147..9720b9841a 100644
--- a/utils/opsdb_control.in
+++ b/utils/opsdb_control.in
@@ -199,14 +199,12 @@ sub AddUser(@)
 	die("Bad data in uid: $target_uid");
     }
 
-    my $query_result =
-	DBQueryFatal("select mailman_password ".
-		     "from users where uid='$target_uid'");
-
+    # Map target user to object.
+    my $target_user = User->Lookup($target_uid);
     fatal("No such user in DB: $target_uid!")
-	if (!$query_result->numrows);
+	if (!defined($user));
 
-    my ($password) = $query_result->fetchrow_array();
+    my $password = $target_user->mailman_password();
     fatal("No password defined for $target_uid!")
 	if (!defined($password) || $password eq "");
 
@@ -242,12 +240,10 @@ sub DelUser(@)
 	die("Bad data in uid: $target_uid");
     }
 
-    my $query_result =
-	DBQueryFatal("select mailman_password ".
-		     "from users where uid='$target_uid'");
-
+    # Map target user to object.
+    my $target_user = User->Lookup($target_uid);
     fatal("No such user in DB: $target_uid!")
-	if (!$query_result->numrows);
+	if (!defined($user));
 
     print "Removing user '$target_uid' from mysql database on $CONTROL.\n";
     my $retval = DoOpsStuff("deluser $target_uid");
@@ -539,7 +535,7 @@ sub AddExpDB(@)
     my $users_result =
 	DBQueryFatal("select distinct g.uid ".
 		     "  from group_membership as g ".
-		     "left join users as u on u.uid=g.uid ".
+		     "left join users as u on u.uid_idx=g.uid_idx ".
 		     "where (u.status='active' or u.status='frozen') and ".
 		     "      g.trust!='none' and ".
   		     "      g.pid='$pid' and g.gid='$gid'");
@@ -766,7 +762,7 @@ sub AddTempDB(@)
     my $users_result =
 	DBQueryFatal("select distinct g.uid ".
 		     "  from group_membership as g ".
-		     "left join users as u on u.uid=g.uid ".
+		     "left join users as u on u.uid_idx=g.uid_idx ".
 		     "where (u.status='active' or u.status='frozen') and ".
 		     "      g.trust!='none' and ".
   		     "      g.pid='$pid' and g.gid='$gid'");
@@ -963,7 +959,7 @@ sub Initialize()
     my $users_result =
 	DBQueryFatal("select distinct g.uid ".
 		     "  from group_membership as g ".
-		     "left join users as u on u.uid=g.uid ".
+		     "left join users as u on u.uid_idx=g.uid_idx ".
 		     "where u.status='active' or u.status='frozen' ".
 #  		     "  and (g.pid='testbed' or g.pid='emulab-ops' or ".
 #		     "       g.pid='tbres' or g.pid='utahstud')" .
diff --git a/wiki/addwikiuser.in b/wiki/addwikiuser.in
index 7f865c2771..e58fc6cebc 100644
--- a/wiki/addwikiuser.in
+++ b/wiki/addwikiuser.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2005, 2006 University of Utah and the Flux Group.
+# Copyright (c) 2005, 2006, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use English;
@@ -49,6 +49,7 @@ $| = 1;
 use lib "@prefix@/lib";
 use libdb;
 use libtestbed;
+use User;
 
 # Protos
 sub fatal($);
@@ -107,6 +108,12 @@ else {
     die("Bad data in user: $user.");
 }
 
+# Map target user to object.
+my $target_user = User->Lookup($user);
+if (! defined($target_user)) {
+    fatal("$user does not exist!");
+}
+
 #
 # We need to serialize this script to avoid a trashed map file. Use
 # a dummy file in /var/tmp, opened for writing and flock'ed. 
@@ -182,15 +189,10 @@ utime $now, $now, $lockfile;
 # Look in the DB to see if there is already a wikiname defined. If
 # we use that. Otherwise have to form one from the user name. Ick.
 #
-my $query_result =
-    DBQueryFatal("select wikiname,usr_name,usr_email,usr_pswd ".
-		 "from users where uid='$user'");
-
-if (!$query_result->numrows) {
-    fatal("No such user $user in the DB!");
-}
-my ($wikiname,$usr_name,$usr_email,$usr_pswd) =
-    $query_result->fetchrow_array();
+my $wikiname  = $target_user->wikiname();
+my $usr_name  = $target_user->name();
+my $usr_email = $target_user->email();
+my $usr_pswd  = $target_user->pswd();
 
 if (!defined($wikiname)) {
     # In update mode, do nothing if no wikiname.
@@ -221,19 +223,17 @@ if (!defined($wikiname)) {
     }
 
     #
-    # Make sure that no other user has the same wikiname but a different
-    # email address. 
+    # Make sure that no other user has the same wikiname.
     #
-    $query_result =
-	DBQueryFatal("select uid,usr_name from users ".
-		     "where wikiname='$wikiname' and usr_email!='$usr_email'");
+    fatal("The wikiname for $user ($wikiname) is already in use!")
+	if (User->LookupByWikiName($wikiname));
 
-    if ($query_result->numrows) {
-	fatal("The wikiname for $user ($wikiname) is already in use!");
-    }
     print "Selecting wikiname '$wikiname' for user $user\n";
 
-    DBQueryFatal("update users set wikiname='$wikiname' where uid='$user'");
+    my %update_args = ("wikiname" => $wikiname);
+
+    fatal("Could not update wikiname for $target_user")
+	if ($target_user->Update(\%update_args) != 0);
 }
 
 #
diff --git a/wiki/delwikiuser.in b/wiki/delwikiuser.in
index fbedb14d44..a3c14601a7 100644
--- a/wiki/delwikiuser.in
+++ b/wiki/delwikiuser.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2005, 2006 University of Utah and the Flux Group.
+# Copyright (c) 2005, 2006, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use English;
@@ -48,6 +48,7 @@ $| = 1;
 use lib "@prefix@/lib";
 use libdb;
 use libtestbed;
+use User;
 
 # Protos
 sub fatal($);
@@ -178,14 +179,11 @@ utime $now, $now, $lockfile;
 # Look in the DB to see if there is already a wikiname defined. If
 # we use that. Otherwise have to form one from the user name. Ick.
 #
-my $query_result =
-    DBQueryFatal("select wikiname ".
-		 "from users where uid='$user'");
-
-if (!$query_result->numrows) {
-    fatal("No such user $user in the DB!");
+my $target_user = User->Lookup($user);
+if (! defined($target_user)) {
+    fatal("$user does not exist!");
 }
-my ($wikiname) = $query_result->fetchrow_array();
+my $wikiname = $target_user->wikiname();
 
 if (!defined($wikiname)) {
     print "There is no wikiname defined in the DB. ".
diff --git a/wiki/setwikigroups.in b/wiki/setwikigroups.in
index b9e9dfde69..6a7f382afc 100644
--- a/wiki/setwikigroups.in
+++ b/wiki/setwikigroups.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2005, 2006 University of Utah and the Flux Group.
+# Copyright (c) 2005, 2006, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use English;
@@ -47,6 +47,7 @@ $| = 1;
 use lib "@prefix@/lib";
 use libdb;
 use libtestbed;
+use User;
 
 # Protos
 sub fatal($);
@@ -102,6 +103,13 @@ else {
     die("Bad data in user: $user.");
 }
 
+# Map target user to object.
+my $target_user = User->Lookup($user);
+if (! defined($target_user)) {
+    fatal("$user does not exist!");
+}
+my $uid_idx = $target_user->uid_idx();
+
 #
 # This script always does the right thing, so no permission checks.
 # In fact, all it does is call over to ops to run a script over there.
@@ -111,7 +119,7 @@ else {
 my $query_result =
     DBQueryFatal("select p.pid,g.wikiname,p.trust from group_membership as p ".
 		 "left join groups as g on g.pid=p.pid and g.gid=p.gid ".
-		 "where uid='$user' and p.pid=g.gid and trust!='none'");
+		 "where uid_idx='$uid_idx' and p.pid=g.gid and trust!='none'");
 
 while (my ($pid,$wikiname,$trust) = $query_result->fetchrow_array()) {
     if (!defined($wikiname)) {
@@ -129,18 +137,16 @@ while (my ($pid,$wikiname,$trust) = $query_result->fetchrow_array()) {
     }
 }
 
-# Admin users ... TBAdmin() test does not work for this test ...
-$query_result =
-    DBQueryFatal("select wikiname,admin from users where uid='$user'");
-my ($wikiname,$isadmin) = $query_result->fetchrow_array();
-if ($isadmin) {
-    push(@glist, "TWikiAdmin");
-}
+my $wikiname = $target_user->wikiname();
 if (!defined($wikiname)) {
     print "There is no wikiname defined in the DB. ".
 	"Must not have a wiki account!\n";
     exit(0);
 }
+
+if ($target_user->admin()) {
+    push(@glist, "TWikiAdmin");
+}
 exit(0)
     if (! @glist);
 
diff --git a/www/dbdefs.php3.in b/www/dbdefs.php3.in
index ab4bab3aaf..303304ca50 100644
--- a/www/dbdefs.php3.in
+++ b/www/dbdefs.php3.in
@@ -53,6 +53,7 @@ define("TBDB_USERSTATUS_NEWUSER",	"newuser");
 define("TBDB_USERSTATUS_UNAPPROVED",	"unapproved");
 define("TBDB_USERSTATUS_UNVERIFIED",	"unverified");
 define("TBDB_USERSTATUS_FROZEN",	"frozen");
+define("TBDB_USERSTATUS_ARCHIVED",	"archived");
 
 #
 # Type of new account.
diff --git a/www/news-rss.php3 b/www/news-rss.php3
index 9b4c3f0c33..52c4f3db60 100644
--- a/www/news-rss.php3
+++ b/www/news-rss.php3
@@ -1,7 +1,7 @@
 <?php
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2005 University of Utah and the Flux Group.
+# Copyright (c) 2005, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 include("defs.php3");
@@ -10,7 +10,7 @@ header("Content-type: text/xml");
 
 $query_result=
     DBQueryFatal("SELECT subject, author, body, msgid, ".
-    		 "date, usr_name, usr_email " .
+    		 "date, usr_name " .
 		 "FROM webnews ".
                  "LEFT JOIN users on webnews.author = users.uid " .
                  "WHERE archived=0 " .
diff --git a/www/plabstats.php3 b/www/plabstats.php3
index 8f3bfb5f9d..3b59937754 100644
--- a/www/plabstats.php3
+++ b/www/plabstats.php3
@@ -1,7 +1,7 @@
 <?php
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2003, 2006 University of Utah and the Flux Group.
+# Copyright (c) 2000-2003, 2006, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 include("defs.php3");
@@ -80,7 +80,7 @@ $query_result =
 		 " from experiment_resources as r ".
 		 "left join experiment_stats as s on r.exptidx=s.exptidx ".
 		 "left join testbed_stats as t on t.rsrcidx=r.idx ".
-		 "left join users as u on u.uid=t.uid ".
+		 "left join users as u on u.uid_idx=t.uid_idx ".
 		 "where r.plabnodes!=0 and t.exitcode=0 and ".
 		 "     (t.action='start' or t.action='swapin' or ".
 		 "      t.action='swapout') $wclause ".
diff --git a/www/tbauth.php3 b/www/tbauth.php3
index 682956c6cf..881ecba8e3 100644
--- a/www/tbauth.php3
+++ b/www/tbauth.php3
@@ -1,7 +1,7 @@
 <?php
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2006 University of Utah and the Flux Group.
+# Copyright (c) 2000-2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 #
@@ -108,6 +108,7 @@ function GETLOGIN() {
 # 
 function GETUID() {
     global $TBNAMECOOKIE;
+    $status_archived = TBDB_USERSTATUS_ARCHIVED;
 
     if (isset($_GET['nocookieuid'])) {
 	$uid = $_GET['nocookieuid'];
@@ -126,7 +127,9 @@ function GETUID() {
 	# Map this to an index (from a uid).
 	#
 	$query_result =
-	    DBQueryFatal("select uid_idx from users where uid='$safe_uid'");
+	    DBQueryFatal("select uid_idx from users ".
+			 "where uid='$safe_uid' and ".
+			 "      status!='$status_archived'");
     
 	if (! mysql_num_rows($query_result))
 	    return FALSE;
@@ -412,6 +415,15 @@ function LoginStatus() {
     if ($admin && $adminon) {
     	putenv("HTTP_WITH_TB_ADMIN_PRIVS=1");
     }
+    #
+    # This environment variable is likely to become the new method for
+    # specifying the credentials of the invoking user. Still thinking
+    # about this, but the short story is that the web interface should
+    # not invoke so much stuff as the user, but rather as a neutral user
+    # with implied credentials. 
+    #
+    putenv("HTTP_INVOKING_USER=" . $CHECKLOGIN_USER->webid());
+    
     # XXX Temporary.
     if ($stud) {
 	$EXPOSEARCHIVE = 1;
diff --git a/www/user_defs.php b/www/user_defs.php
index 354e601e55..df21506bd7 100644
--- a/www/user_defs.php
+++ b/www/user_defs.php
@@ -1,7 +1,7 @@
 <?php
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2006 University of Utah and the Flux Group.
+# Copyright (c) 2006, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 
@@ -74,9 +74,12 @@ class User
     # Backwards compatable lookup by uid. Will eventually flush this.
     function &LookupByUid($uid) {
 	$safe_uid = addslashes($uid);
+	$status_archived = TBDB_USERSTATUS_ARCHIVED;
 
 	$query_result =
-	    DBQueryWarn("select uid_idx from users where uid='$safe_uid'");
+	    DBQueryWarn("select uid_idx from users ".
+			"where uid='$safe_uid' and ".
+			"      status!='$status_archived'");
 
 	if (!$query_result || !mysql_num_rows($query_result)) {
 	    return null;
@@ -91,10 +94,12 @@ class User
     # locally unique.
     function &LookupByEmail($email) {
 	$safe_email = addslashes($email);
+	$status_archived = TBDB_USERSTATUS_ARCHIVED;
 
 	$query_result =
 	    DBQueryWarn("select uid_idx from users ".
-			"where LCASE(usr_email)=LCASE('$safe_email')");
+			"where LCASE(usr_email)=LCASE('$safe_email') and ".
+			"      status!='$status_archived'");
 
 	if (!$query_result || !mysql_num_rows($query_result)) {
 	    return null;
@@ -109,10 +114,12 @@ class User
     # locally unique.
     function &LookupByWikiName($wikiname) {
 	$safe_wikiname = addslashes($wikiname);
+	$status_archived = TBDB_USERSTATUS_ARCHIVED;
 
 	$query_result =
 	    DBQueryWarn("select uid_idx from users ".
-			"where wikiname='$safe_wikiname'");
+			"where wikiname='$safe_wikiname' and ".
+			"      status!='$status_archived'");
 
 	if (!$query_result || !mysql_num_rows($query_result)) {
 	    return null;
-- 
GitLab