Index: addpubkey.in
===================================================================
RCS file: /usr/DETER/cvsroot/testbed/account/addpubkey.in,v
retrieving revision 1.1.1.3
diff -u -r1.1.1.3 addpubkey.in
--- addpubkey	2 Oct 2008 22:53:20 -0000	1.1.1.3
+++ addpubkey	3 Dec 2008 19:59:00 -0000
@@ -43,9 +43,11 @@
     print " -w      Generate new authkeys (protocol 1 and 2) file for user\n";
     print " -i      Initialize mode; generate initial key for user\n";
     print " -r      Force a regenerate of initial key for user\n";
+    print " -R      Remove key from user\n";
+    print " -C      Confirm key is assigned to user\n";
     exit(-1);
 }
-my $optlist   = "dkniwfu:rX:";
+my $optlist   = "dkniwfu:rX:RC";
 my $iskey     = 0;
 my $verify    = 0;
 my $initmode  = 0;
@@ -78,6 +80,8 @@
 my $user_dbid;
 my $user_uid;
 my $debug = 0;
+my $delete = 0;
+my $confirm = 0;
 
 #
 # Testbed Support libraries
@@ -90,7 +94,8 @@
 #
 # Function prototypes
 #
-sub ParseKey($);
+sub CheckKey($);
+sub AddKey($);
 sub InitUser();
 sub GenerateKeyFile();
 sub ParseXmlArgs($$$$$$);
@@ -169,6 +174,12 @@
 if (defined($options{"u"})) {
     $user = $options{"u"};
 }
+if (defined($options{"R"})) {
+    $delete = 1;
+}
+if (defined($options{"C"})) {
+    $confirm = 1;
+}
 if (defined($options{"X"})) {
     $xmlfile = $options{"X"};
 
@@ -190,6 +201,10 @@
     $ARGV[0] = $xmlargs{"keyfile"};
 }
 
+if ( $delete && $confirm ) {
+    usage();
+}
+
 if ($verify && $genmode) {
     usage();
 }
@@ -313,11 +328,19 @@
     AuditStart(0);
 }
 
+# The control flow is the same for add and delete.  Pick the action and follow
+# that flow.  
+my $action = undef;
+
+if ($delete ) { $action = \&DeleteKey; }
+elsif ($confirm) { $action = \&ConfirmKey; }
+else { $action = \&AddKey; }
+
 #
 # Grab the first line of the file. Parse it to see if its in the
 # format we like (openssh), either protocol 1 or 2.
 #
-if (ParseKey($keyline)) {
+if (&$action($keyline)) {
     exit 0;
 }
 # If the key was entered on the command line, then nothing more to do.
@@ -333,26 +356,23 @@
 	  "    Could not start ssh-keygen\n");
 }
 $keyline = <KEYGEN>;
-if (close(KEYGEN) && ParseKey($keyline)) {
+if (close(KEYGEN) && &$action($keyline)) {
     exit 0;
 }
 exit 1;
 
-sub ParseKey($) {
+# Make sure the key is properly formatted.  If so, return the key in a
+# canonical format, the comment, and type as a list, otherwise return undef.
+sub CheckKey($) {
     my ($keyline) = @_;
+    my $comment;
+    my $type;
 
     # Remove trailing newlines which screws the patterns below.
     # First convert dos newlines since many people upload from windoze.
     $keyline =~ s/\r/\n/g;
     $keyline =~ s/\n//g;
 
-    # Enforce a reasonable length on the key.
-    if (length($keyline) > 4096) {
-	print "Key is too long!\n";
-	print "Key: $keyline\n";
-	return 0;
-    }
-    
     if ($keyline =~ /^(\d*\s\d*\s[0-9a-zA-Z]*) ([-\w\@\.\ ]*)\s*$/) {
         # Protocol 1
 	$type    = "ssh-rsa1";
@@ -378,28 +398,159 @@
     }
 
     if (!defined($key)) {
+	return undef;
+    }
+
+    #
+    # Make up a comment field for the DB. 
+    #
+    if (!defined($comment)) {
+	$comment = "$type-${user_email}";
+    }
+    $key = "$key $comment";
+    return ($key, $comment, $type);
+}
+
+
+sub ConfirmKey($) {
+    my ($keyline) = @_;
+
+    # Enforce a reasonable length on the key.
+    if (length($keyline) > 4096) {
+	print "Key is too long!\n";
+	print "Key: $keyline\n";
+	return 0;
+    }
+
+    my ($key, $comment, $type)  = CheckKey($keyline);
+
+    if (!$key) {
 	print "Key cannot be parsed!\n";
 	print "Key: $keyline\n";
 	return 0;
     }
+    
+    # Do not enter into DB if in verify mode.
+    if ($verify && $key ) {
+	print "Key was good: $type\n";
+	return 1;
+    }
+
+    my $sth = DBQueryFatal("select uid from user_pubkeys " . 
+	"where uid='$user_uid' and pubkey='$key'");
+
+    my $chunked = "";
+
+    while (length($key)) {
+	$chunked .= substr($key, 0, 65, "");
+	if (length($key)) {
+	    $chunked .= "\n";
+	}
+    }
+    if ($sth->rows() == 1) {
+	print "SSH Public Key for '$user' confirmed:\n";
+	print "$chunked\n";
+	return 1;
+    }
+    else {
+	print "SSH Public Key for '$user' not present:\n";
+	print "$chunked\n";
+	return 0;
+    }
+}
+
+
+sub AddKey($) {
+    my ($keyline) = @_;
+
+    # Enforce a reasonable length on the key.
+    if (length($keyline) > 4096) {
+	print "Key is too long!\n";
+	print "Key: $keyline\n";
+	return 0;
+    }
+
+    my ($key, $comment, $type)  = CheckKey($keyline);
 
+    if (!$key) {
+	print "Key cannot be parsed!\n";
+	print "Key: $keyline\n";
+	return 0;
+    }
+    
     # Do not enter into DB if in verify mode.
-    if ($verify) {
+    if ($verify && $key ) {
 	print "Key was good: $type\n";
 	return 1;
     }
 
-    #
-    # Make up a comment field for the DB. 
-    #
-    if (!defined($comment)) {
-	$comment = "$type-${user_email}";
+
+    # If the key is already present, just report success.
+    if (!ConfirmKey($keyline)) {
+	DBQueryFatal("replace into user_pubkeys ".
+		     "values ('$user_uid', '$user_dbid', ".
+		     "        0, '$key', now(), '$comment')");
+
+	#
+	# Mark user record as modified so nodes are updated.
+	#
+	TBNodeUpdateAccountsByUID($user_uid);
+
+	my $chunked = "";
+
+	while (length($key)) {
+	    $chunked .= substr($key, 0, 65, "");
+	    if (length($key)) {
+		$chunked .= "\n";
+	    }
+	}
+	print "SSH Public Key for '$user' added:\n";
+	print "$chunked\n";
+	
+	# Generate new auth keys file. 
+	if ($genmode) {
+	    GenerateKeyFile();
+	}
+
+	if (! $noemail) {
+	    SENDMAIL("$user_name <$user_email>",
+		     "SSH Public Key for '$user_uid' Added",
+		     "SSH Public Key for '$user_uid' added:\n".
+		     "\n".
+		     "$chunked\n",
+		     "$TBOPS");
+	}
+    }
+    return 1;
+}
+
+sub DeleteKey($) {
+    my ($keyline) = @_;
+
+    # Enforce a reasonable length on the key.
+    if (length($keyline) > 4096) {
+	print "Key is too long!\n";
+	print "Key: $keyline\n";
+	return 0;
+    }
+
+    my ($key, $comment, $type)  = CheckKey($keyline);
+
+    if (!$key) {
+	print "Key cannot be parsed!\n";
+	print "Key: $keyline\n";
+	return 0;
+    }
+    
+    # Do not enter into DB if in verify mode.
+    if ($verify && $key ) {
+	print "Key was good: $type\n";
+	return 1;
     }
-    $key = "$key $comment";
 
-    DBQueryFatal("replace into user_pubkeys ".
-		 "values ('$user_uid', '$user_dbid', ".
-		 "        0, '$key', now(), '$comment')");
+    DBQueryFatal("delete from user_pubkeys ".
+		 "where uid='$user_uid' and uid_idx='$user_dbid' and ".
+		 "pubkey='$key'");
 
     #
     # Mark user record as modified so nodes are updated.
@@ -414,7 +565,7 @@
 	    $chunked .= "\n";
 	}
     }
-    print "SSH Public Key for '$user' added:\n";
+    print "SSH Public Key for '$user' deleted:\n";
     print "$chunked\n";
     
     # Generate new auth keys file. 
@@ -424,8 +575,8 @@
 
     if (! $noemail) {
 	SENDMAIL("$user_name <$user_email>",
-		 "SSH Public Key for '$user_uid' Added",
-		 "SSH Public Key for '$user_uid' added:\n".
+		 "SSH Public Key for '$user_uid' removed",
+		 "SSH Public Key for '$user_uid' removed:\n".
 		 "\n".
 		 "$chunked\n",
 		 "$TBOPS");

