Commit 4998b2d7 authored by Leigh Stoller's avatar Leigh Stoller

Call this commit "Snow in Corvallis" ...

The major functional change in this revision is converting from user
selected UIDs to system selected UIDs. This is controlled by the
variable $USERSELECTUIDS in defs/defs.php3.in which is now set to
zero, so system selected UIDs is the default.

The algo for creating the uid is to take the email address, strip the
@whatever from it, squeeze out dots and dashes and underlines, and
make sure any +foo tokens are removed. Then make sure it is unique by
taking the first 5 characters and then adding a 3 digit number,
derived by checking the DB to see what exists.

Since we will want to (more often) change the UID selected, there is a
new admin only menu option on the Show User page. It calls the backend
script to do the work (sbin/changeuid).

The login page now defaults to storing and showing the email address
for login, rather then the UID. It will still accept either one though
(has for a long time).

Along the way I also reorg'ed a number of pages to use the new user,
group, and project classes and moved some common functionality into
the class defs.

Also changed the way addpubkey is called, to avoid some confusion.
parent 488236f2
......@@ -16,17 +16,18 @@ use Getopt::Std;
#
sub usage()
{
print "Usage: addpubkeys [-n] [-k] <user> [<keyfile> | <key>]\n";
print " addpubkeys [-i [-f] | -w] <user>\n";
print "Usage: addpubkeys [-k | -f] [-n | -u <user>] [<keyfile> | <key>]\n";
print " addpubkeys [-i [-r] | -w] <user>\n";
print "Options:\n";
print " -k Indicates that key was passed in on the command line\n";
print " -f Indicates that key was passed in as a filename\n";
print " -n Verify key format only; do not enter into into DB\n";
print " -w Generate new authkeys (protocol 1 and 2) file for user\n";
print " -i Initialize mode; generate initial key for user\n";
print " -f Force a generate of initial key for user\n";
print " -r Force a regenerate of initial key for user\n";
exit(-1);
}
my $optlist = "kniwf";
my $optlist = "kniwfu:r";
my $iskey = 0;
my $verify = 0;
my $initmode = 0;
......@@ -110,42 +111,52 @@ if (! getopts($optlist, \%options)) {
if (defined($options{"k"})) {
$iskey = 1;
}
if (defined($options{"f"})) {
$iskey = 0;
}
if (defined($options{"n"})) {
$verify = 1;
}
if (defined($options{"i"})) {
$initmode = 1;
}
if (defined($options{"f"})) {
if (defined($options{"r"})) {
$force = 1;
}
if (defined($options{"w"})) {
$genmode = 1;
}
if (defined($options{"u"})) {
$user = $options{"u"};
}
if ($verify && $genmode) {
usage();
}
if ($initmode || $genmode) {
if (@ARGV != 1) {
usage();
}
}
elsif (@ARGV == 2) {
$keyfile = $ARGV[1];
usage()
if (@ARGV != 1);
$user = $ARGV[0];
}
else {
usage();
}
$user = $ARGV[0];
usage()
if (@ARGV != 1);
usage()
if (!$verify && !defined($user));
$keyfile = $ARGV[0];
}
#
# Untaint the arguments.
#
if ($user =~ /^([-\w_]+)$/i) {
$user = $1;
}
else {
fatal("Tainted username: $user");
if (defined($user)) {
if ($user =~ /^([-\w_]+)$/i) {
$user = $1;
}
else {
fatal("Tainted username: $user");
}
}
#
......
......@@ -18,9 +18,10 @@ SBIN_SCRIPTS = avail inuse showgraph if2port backup webcontrol node_status \
idletimes idlemail setsitevar audit changeuid changepid \
elabinelab_bossinit update_permissions mysqld_watchdog \
dumperrorlog
LIBEXEC_SCRIPTS = webnodelog webnfree webnewwanode webidlemail xmlconvert
LIBEXEC_SCRIPTS = webnodelog webnfree webnewwanode webidlemail xmlconvert \
webchangeuid
LIB_SCRIPTS = libdb.pm Node.pm libdb.py libadminctrl.pm Experiment.pm \
NodeType.pm Interface.pm User.pm Group.pm Project.pm
NodeType.pm Interface.pm User.pm Group.pm Project.pm
# Stuff installed on plastic.
USERSBINS = genelists.proxy dumperrorlog.proxy
......
#!/usr/bin/perl -w
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2004 University of Utah and the Flux Group.
# Copyright (c) 2004, 2006 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
......@@ -32,9 +32,6 @@ use libaudit;
use libdb;
use libtestbed;
# Be careful not to exit on transient error
$libdb::DBQUERY_MAXTRIES = 30;
#
# Turn off line buffering on output
#
......@@ -46,14 +43,6 @@ $| = 1;
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:/usr/sbin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# We don't want to run this script unless its the real version.
#
#if ($EUID != 0) {
# die("*** $0:\n".
# " Must be setuid! Maybe its a development version?\n");
#}
#
# This script is setuid, so please do not run it as root. Hard to track
# what has happened.
......@@ -187,3 +176,6 @@ DBQueryFatal("delete from login where uid='$olduid'");
#
print "Updating users table ...\n";
DBQueryFatal("update users set uid='$newuid' where uid='$olduid'");
exit(0);
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2003, 2005, 2006 University of Utah and the Flux Group.
# All rights reserved.
#
include("defs.php3");
include("showstuff.php3");
#
# Only admin users ...
#
$uid = GETLOGIN();
LOGGEDINORDIE($uid);
$isadmin = ISADMIN($uid);
if (!$isadmin) {
USERERROR("You do not have permission to login names!", 1);
}
#
# Verify page/form arguments. Note that the target uid comes initially as a
# page arg, but later as a form argument, hence this odd check.
#
if (! isset($_POST['submit'])) {
# First page load. Default to current user.
if (! isset($_GET['target_uid']))
$target_uid = $uid;
else
$target_uid = $_GET['target_uid'];
}
else {
# Form submitted. Make sure we have a target_uid and a new_uid.
if (! isset($_POST['target_uid']) || $_POST['target_uid'] == "" ||
! isset($_POST['new_uid']) || $_POST['new_uid'] == "") {
PAGEARGERROR("Invalid form arguments.");
}
$target_uid = $_POST['target_uid'];
$new_uid = $_POST['new_uid'];
}
# Pedantic check of uid before continuing.
if ($target_uid == "" || !TBvalid_uid($target_uid)) {
PAGEARGERROR("Invalid uid: '$target_uid'");
}
# Find user. Must be unapproved (verified user). Any other state is too hard.
if (! ($user = User::LookupByUid($target_uid))) {
USERERROR("The user $target_uid is not a valid user", 1);
}
if ($user->status() != TBDB_USERSTATUS_UNAPPROVED) {
USERERROR("The user $target_uid must be ".
"unapproved (but verified) to change!", 1);
}
function SPITFORM($user, $new_uid, $error)
{
global $TBDB_UIDLEN;
$target_uid = $user->uid();
#
# Standard Testbed Header.
#
PAGEHEADER("Change login UID for user");
if ($error) {
echo "<center>
<font size=+1 color=red>$error</font>
</center><br>\n";
}
else {
echo "<center>
<font size=+1>
Please enter the new UID for user '$target_uid'<br><br>
</font>
</center>\n";
}
echo "<table align=center border=1>
<form action=changeuid.php method=post>
<tr>
<td>New UID:</td>
<td><input type=text
name=\"new_uid\"
value=\"$new_uid\"
size=$TBDB_UIDLEN
maxlength=$TBDB_UIDLEN></td>
</tr>
<tr>
<td align=center colspan=2>
<b><input type=submit value=\"Change UID\"
name=submit></b>
</td>
</tr>
<input type=hidden name=target_uid value=$target_uid>
</form>
</table>\n";
echo "<br><br>\n";
echo "<center>\n";
SHOWUSER($target_uid);
echo "</center>\n";
PAGEFOOTER();
return;
}
#
# If not clicked, then put up a form.
#
if (! isset($_POST['submit'])) {
SPITFORM($user, "", null);
return;
}
# Sanity checks
$error = null;
if (!TBvalid_uid($new_uid)) {
$error = "UID: " . TBFieldErrorString();
}
elseif (User::LookupByUid($new_uid) || posix_getpwnam($new_uid)) {
$error = "UID: Already in use. Pick another";
}
if ($error) {
SPITFORM($user, $new_uid, $error);
return;
}
#
# Standard Testbed Header.
#
PAGEHEADER("Change login UID for user");
# Okay, call out to backend to change.
STARTBUSY("Changing UID");
#
# Run the backend script.
#
SUEXEC($uid, $TBADMINGROUP,
"webchangeuid $target_uid $new_uid", SUEXEC_ACTION_USERERROR);
# Stop the busy indicator and zap to user page.
STOPBUSY();
PAGEREPLACE("showuser.php3?target_uid=$new_uid");
#
# Standard Testbed Footer
#
PAGEFOOTER();
?>
......@@ -36,6 +36,7 @@ $EXPOSELINKTEST = 1;
$EXPOSESTATESAVE= 0;
$EXPOSEARCHIVE = 0;
$EXPOSETEMPLATES= 0;
$USERSELECTUIDS = 0;
$TBMAILADDR_OPS = "@TBOPSEMAIL_NOSLASH@";
$TBMAILADDR_WWW = "@TBWWWEMAIL_NOSLASH@";
......@@ -81,6 +82,7 @@ if ($TBSCRATCH_DIR) {
$TBAUTHCOOKIE = "HashCookie" . $TBCOOKIESUFFIX;
$TBNAMECOOKIE = "MyUidCookie" . $TBCOOKIESUFFIX;
$TBEMAILCOOKIE = "MyEmailCookie" . $TBCOOKIESUFFIX;
$TBLOGINCOOKIE = "LoginCookie" . $TBCOOKIESUFFIX;
$HTTPTAG = "http://";
......@@ -161,6 +163,8 @@ function TBERROR ($message, $death, $xmp = 0) {
global $session_interactive, $session_errorhandler;
$script = urldecode($_SERVER['REQUEST_URI']);
CLEARBUSY();
TBMAIL($TBMAIL_OPS,
"WEB ERROR REPORT",
"\n".
......@@ -190,6 +194,8 @@ function USERERROR($message, $death) {
global $TBMAILADDR;
global $session_interactive, $session_errorhandler;
CLEARBUSY();
if (! $session_interactive) {
if ($session_errorhandler)
$session_errorhandler($message, $death);
......@@ -339,7 +345,7 @@ function ADDPUBKEY($uid, $cmdandargs) {
# nobody. We will get audit email in case we need to track what has
# happened.
#
if (! HASREALACCOUNT($uid)) {
if (!$uid || !HASREALACCOUNT($uid)) {
$uid = "nobody";
}
return SUEXEC($uid, "nobody", $cmdandargs, 0);
......
......@@ -7,6 +7,7 @@
class Group
{
var $group;
var $project;
#
# Constructor by lookup on unique index.
......@@ -22,7 +23,8 @@ class Group
$this->group = NULL;
return;
}
$this->group = mysql_fetch_array($query_result);
$this->group = mysql_fetch_array($query_result);
$this->project = null;
}
# Hmm, how does one cause an error in a php constructor?
......@@ -82,6 +84,19 @@ class Group
return 0;
}
#
# Load the project for a group lazily.
#
function LoadProject() {
$pid_idx = $this->pid_idx();
if (! ($project = Project::Lookup($pid_idx))) {
TBERROR("Group::LoadProject: Could not load project $pid_idx!", 1);
}
$this->project = $project;
return 0;
}
# accessors
function field($name) {
return (is_null($this->group) ? -1 : $this->group[$name]);
......@@ -108,6 +123,17 @@ class Group
global $TBBASE, $TBMAIL_APPROVAL, $TBMAIL_AUDIT, $TBMAIL_WWW;
global $MIN_UNIX_GID;
#
# Check that we can guarantee uniqueness of the unix group name.
#
$query_result =
DBQueryFatal("select gid from groups ".
"where unix_name='$unix_name'");
if (mysql_num_rows($query_result)) {
TBERROR("Could not form a unique Unix group name: $unix_name!", 1);
}
# Every group gets a new unique index.
$gid_idx = TBGetUniqueIndex('next_gid');
......@@ -228,6 +254,69 @@ class Group
return 0;
}
#
# Notify leaders of new (and verified) group member.
#
function NewMemberNotify($user) {
global $TBWWW, $TBMAIL_APPROVAL, $TBMAIL_AUDIT, $TBMAIL_WWW;
if (! $this->project) {
$this->LoadProject();
}
$project = $this->project;
$pid = $project->pid();
$gid = $project->gid();
$leader = $project->Leader();
$leader_name = $leader->name();
$leader_email = $leader->email();
$leader_uid = $leader->uid();
$allleaders = TBLeaderMailList($pid, $gid);
$joining_uid = $user->uid();
$usr_title = $user->title();
$usr_name = $user->name();
$usr_affil = $user->affil();
$usr_email = $user->email();
$usr_addr = $user->addr();
$usr_addr2 = $user->addr2();
$usr_city = $user->city();
$usr_state = $user->state();
$usr_zip = $user->zip();
$usr_country = $user->country();
$usr_phone = $user->phone();
$usr_URL = $user->URL();
TBMAIL("$leader_name '$leader_uid' <$leader_email>",
"$joining_uid $pid Project Join Request",
"$usr_name is trying to join your group $gid in project $pid.\n".
"\n".
"Contact Info:\n".
"Name: $usr_name\n".
"Emulab ID: $joining_uid\n".
"Email: $usr_email\n".
"User URL: $usr_URL\n".
"Job Title: $usr_title\n".
"Affiliation: $usr_affil\n".
"Address 1: $usr_addr\n".
"Address 2: $usr_addr2\n".
"City: $usr_city\n".
"State: $usr_state\n".
"ZIP/Postal Code: $usr_zip\n".
"Country: $usr_country\n".
"Phone: $usr_phone\n".
"\n".
"Please return to $TBWWW,\n".
"log in, and select the 'New User Approval' page to enter your\n".
"decision regarding $usr_name's membership in your project.\n\n".
"Thanks,\n".
"Testbed Operations\n",
"From: $TBMAIL_APPROVAL\n".
"Cc: $allleaders\n".
"Bcc: $TBMAIL_AUDIT\n".
"Errors-To: $TBMAIL_WWW");
return 0;
}
#
# Check if user is a member of this group.
#
......
This diff is collapsed.
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2003 University of Utah and the Flux Group.
# Copyright (c) 2000-2003, 2006 University of Utah and the Flux Group.
# All rights reserved.
#
require("defs.php3");
......@@ -74,6 +74,8 @@ if (($known_uid = GETUID()) != FALSE) {
#
# Spit out the form.
#
# The uid can be an email address, and in fact defaults to that now.
#
function SPITFORM($uid, $key, $referrer, $failed, $adminmode, $simple, $view)
{
......@@ -105,10 +107,11 @@ function SPITFORM($uid, $key, $referrer, $failed, $adminmode, $simple, $view)
echo "<table align=center border=1>
<form action='${TBBASE}/login.php3${pagearg}' method=post>
<tr>
<td>Username:</td>
<td>Email Address:<br>
<font size=-2>(or UserName)</font></td>
<td><input type=text
value=\"$uid\"
name=uid size=20></td>
name=uid size=30></td>
</tr>
<tr>
<td>Password:</td>
......@@ -131,7 +134,7 @@ function SPITFORM($uid, $key, $referrer, $failed, $adminmode, $simple, $view)
</table>\n";
echo "<center><h2>
<a href='password.php3'>Forgot your username or password?</a>
<a href='password.php3'>Forgot your password?</a>
</h2></center>\n";
}
......@@ -139,9 +142,17 @@ function SPITFORM($uid, $key, $referrer, $failed, $adminmode, $simple, $view)
# If not clicked, then put up a form.
#
if (! isset($login)) {
# Allow page arg to override what we think is the UID to log in as.
SPITFORM((isset($vuid) ? $vuid : $known_uid),
$key, $referrer, 0, $adminmode, $simple, $view);
# Allow page arg to override what we think is the UID to log in as.
# Use email address now, for the login uid. Still allow real uid though.
if (isset($vuid)) {
# For login during verification step, from email message.
$login_id = $vuid;
}
else {
$login_id = REMEMBERED_ID();
}
SPITFORM($login_id, $key, $referrer, 0, $adminmode, $simple, $view);
PAGEFOOTER($view);
return;
}
......
This diff is collapsed.
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2003, 2005 University of Utah and the Flux Group.
# Copyright (c) 2000-2003, 2005, 2006 University of Utah and the Flux Group.
# All rights reserved.
#
include("defs.php3");
......@@ -31,7 +31,7 @@ if ($simple) {
# Must use https!
if (!isset($SSL_PROTOCOL)) {
PAGEHEADER("Forgot Your Username or Password?", $view);
PAGEHEADER("Forgot Your Password?", $view);
USERERROR("Must use https:// to access this page!", 1);
}
......@@ -40,7 +40,7 @@ if (!isset($SSL_PROTOCOL)) {
#
if (($known_uid = GETUID()) != FALSE) {
if (CHECKLOGIN($known_uid) & CHECKLOGIN_LOGGEDIN) {
PAGEHEADER("Forgot Your Username or Password?", $view);
PAGEHEADER("Forgot Your Password?", $view);
echo "<h3>
You are logged in. You must already know your password!
......@@ -58,7 +58,7 @@ function SPITFORM($email, $phone, $failed, $simple, $view)
{
global $TBBASE;
PAGEHEADER("Forgot Your Username or Password?", $view);
PAGEHEADER("Forgot Your Password?", $view);
if ($failed) {
echo "<center>
......@@ -94,8 +94,6 @@ function SPITFORM($email, $phone, $failed, $simple, $view)
<td align=center colspan=2>
<b><input type=submit value=\"Reset Password\"
name=reset></b>
<b><input type=submit value=\"Mail my Username\"
name=tellme></b>
</td>
</tr>\n";
......@@ -168,34 +166,6 @@ if (preg_replace("/[^0-9]/", "", $phone) !=
TBUserInfo($uid, $uid_name, $uid_email);
#
# If just telling the user his account uid, send it and be done.
#
if (isset($tellme)) {
PAGEHEADER("Forgot Your Username?", $view);
TBMAIL("$uid_name <$uid_email>",
"Login ID requested by '$uid'",
"\n".
"Your Emulab login ID is '$uid'. Please use this ID when logging\n".
"in at ${TBBASE}.\n".
"\n".
"The request originated from IP: " . $_SERVER['REMOTE_ADDR'] . "\n".
"\n".
"Thanks,\n".
"Testbed Operations\n",
"From: $TBMAIL_OPS\n".
"Bcc: $TBMAIL_AUDIT\n".
"Errors-To: $TBMAIL_WWW");
echo "<br>
An email message has been sent to your account. In it you will find
your login ID.\n";
PAGEFOOTER();
exit(0);
}
#
# Yep. Generate a random key and send the user an email message with a URL
# that will allow them to change their password.
......@@ -209,7 +179,7 @@ setcookie($TBAUTHCOOKIE, $keyA, 0, "/",
$TBAUTHDOMAIN, $TBSECURECOOKIES);
# It is okay to spit this now that we have sent the cookie.
PAGEHEADER("Forgot Your Username or Password?", $view);
PAGEHEADER("Forgot Your Password?", $view);
DBQueryFatal("update users set ".
" chpasswd_key='$key', ".
......
......@@ -24,6 +24,7 @@ class Project
return;
}
$this->project = mysql_fetch_array($query_result);
$this->group = null;
}
# Hmm, how does one cause an error in a php constructor?
......@@ -102,7 +103,6 @@ class Project
function num_ron() { return $this->field("num_ron"); }
function why() { return $this->field("why"); }
function control_node() { return $this->field("control_node"); }
function unix_gid() { return $this->field("unix_gid"); }
function approved() { return $this->field("approved"); }
function inactive() { return $this->field("inactive"); }
function date_inactive() { return $this->field("date_inactive"); }
......@@ -116,6 +116,16 @@ class Project
function linked_to_us() { return $this->field("linked_to_us"); }
function cvsrepo_public(){ return $this->field("cvsrepo_public"); }
function unix_gid() {
$group = $this->LoadGroup();
return $group->unix_gid();
}
function unix_name() {
$group = $this->LoadGroup();
return $group->unix_name();
}
#
# Class function to create new project and return object.
......@@ -190,17 +200,14 @@ class Project
TBERROR("Project::LoadGroup: Could not load group $gid_idx!", 1);
}
$this->group = $group;
return 0;
return $group;
}
#
# Add *new* member to project group; starts out with trust=none.
#
function AddNewMember($user) {
if (! $this->group) {
$this->LoadGroup();
}
$group = $this->group;
$group = $this->LoadGroup();
return $group->AddNewMember($user);
}
......@@ -209,10 +216,7 @@ class Project
# Check if user is a member of this project (well, group)
#
function IsMember($user) {
if (! $this->group) {
$this->LoadGroup();
}
$group = $this->group;
$group = $this->LoadGroup();
return $group->IsMember($user);
}
......
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2003, 2005 University of Utah and the Flux Group.
# Copyright (c) 2000-2003, 2005, 2006 University of Utah and the Flux Group.
# All rights reserved.
#
include("defs.php3");
......@@ -270,7 +270,7 @@ if (isset($formfields[usr_key]) &&
#
$formfields[usr_key] = ereg_replace("[\n]", "", $formfields[usr_key]);
$usr_key = $formfields[usr_key];
$addpubkeyargs = "-k $target_uid '$usr_key' ";
$addpubkeyargs = "-k '$usr_key' ";
}
}
......@@ -291,7 +291,7 @@ if (isset($_FILES['usr_keyfile']) &&
$errors["PubKey File"] = "Invalid characters";
}
else {
$addpubkeyargs = "$target_uid $localfile";
$addpubkeyargs = "$localfile";
chmod($localfile, 0644);
}
}
......@@ -325,7 +325,7 @@ if (count($errors)) {
# Okay, first run the script in verify mode to see if the key is
# parsable. If it is, then do it for real.
#
if (ADDPUBKEY($uid, "webaddpubkey -n $addpubkeyargs")) {
if (ADDPUBKEY($uid, "webaddpubkey -n -u $target_uid $addpubkeyargs")) {
$errors["Pubkey Format"] = "Could not be parsed. Is it a public key?";
SPITFORM($formfields, $errors);
PAGEFOOTER();
......@@ -334,7 +334,7 @@ if (ADDPUBKEY($uid, "webaddpubkey -n $addpubkeyargs")) {
#
# Insert key, update authkeys files and nodes if appropriate.
#
ADDPUBKEY($uid, "webaddpubkey $addpubkeyargs");
ADDPUBKEY($uid, "webaddpubkey -u $target_uid $addpubkeyargs");
#
# Redirect back, avoiding a POST in the history.
......
......@@ -221,6 +221,8 @@ if (!$wikionly && ($isadmin || !strcmp($uid, $target_uid))) {
}
if ($isadmin) {
SUBMENUSECTION("Admin Options");
if (!strcmp(TBUserStatus($target_uid), TBDB_USERSTATUS_FROZEN)) {
WRITESUBMENUBUTTON("Thaw User",
"freezeuser.php3?target_uid=$target_uid&action=thaw");
......@@ -235,6 +237,11 @@ if ($isadmin) {
WRITESUBMENUBUTTON("SU as User",
"suuser.php?target_uid=$target_uid");
if ($userstatus == TBDB_USERSTATUS_UNAPPROVED) {
WRITESUBMENUBUTTON("Change UID",
"changeuid.php?target_uid=$target_uid");
}
if (! strcmp($userstatus, TBDB_USERSTATUS_NEWUSER) ||
! strcmp($userstatus, TBDB_USERSTATUS_UNVERIFIED)) {
WRITESUBMENUBUTTON("Resend Verification Key",
......
......@@ -73,6 +73,19 @@ function GENHASH() {
return bin2hex($hash);
}
#
# Return the value of what we told the browser to remember for the login.
# Currently, this is an email address, stored in a long term cookie.
#
function REMEMBERED_ID() {
global $TBEMAILCOOKIE;
if (isset($_COOKIE[$TBEMAILCOOKIE])) {