Commit 7af99c61 authored by David Johnson's avatar David Johnson
Browse files

Merge branch 'master' of git-public.flux.utah.edu:/flux/git/emulab-devel

parents 1101021e e6598e50
......@@ -4280,10 +4280,92 @@ sub UnBindNonLocalUsers($)
my $idx = $self->idx();
DBQueryWarn("delete from nonlocal_user_bindings ".
"where exptidx='$idx'")
#
# Need to delete the pubkeys, so need a list of current bindings.
#
my $query_result =
DBQueryWarn("select uid,uid_idx from nonlocal_user_accounts ".
"where exptidx='$idx'");
return -1
if (!$query_result);
while (my ($uid, $uid_idx) = $query_result->fetchrow_array()) {
DBQueryWarn("delete from nonlocal_user_pubkeys ".
"where uid_idx='$uid_idx'")
or return -1;
DBQueryWarn("delete from nonlocal_user_accounts ".
"where uid_idx='$uid_idx'")
or return -1;
}
return 0;
}
#
# Bind nonlocal user to experiment (slice, in Geni).
#
sub BindNonLocalUser($$$$$$)
{
my ($self, $keys, $uid, $urn, $name, $email) = @_;
return -1
if (! ref($self));
my $exptidx = $self->idx();
my $safe_urn = DBQuoteSpecial($urn)
if (defined($urn));
my $safe_uid = DBQuoteSpecial($uid);
my $safe_name = DBQuoteSpecial($name);
my $safe_email = DBQuoteSpecial($email);
my $uid_idx;
#
# User may already exist, as for updating keys.
#
my $query_result =
DBQueryWarn("select uid_idx from nonlocal_user_accounts ".
"where uid=$safe_uid and exptidx='$exptidx'");
return -1
if (!$query_result);
if ($query_result->numrows) {
($uid_idx) = $query_result->fetchrow_array();
}
else {
my @insert_data = ();
$uid_idx = User->NextIDX();
push(@insert_data, "created=now()");
push(@insert_data, "uid_idx='$uid_idx'");
push(@insert_data, "unix_uid=NULL");
push(@insert_data, "exptidx='$exptidx'");
push(@insert_data, "urn=$safe_urn")
if (defined($urn));
push(@insert_data, "uid=$safe_uid");
push(@insert_data, "name=$safe_name");
push(@insert_data, "email=$safe_email");
push(@insert_data, "uid_uuid=uuid()");
# Insert into DB.
my $insert_result =
DBQueryWarn("insert into nonlocal_user_accounts set " .
join(",", @insert_data));
}
#
# Always replace the entire key set; easier to manage.
#
DBQueryWarn("delete from nonlocal_user_pubkeys ".
"where uid_idx='$uid_idx'")
or return -1;
foreach my $key (@{ $keys }) {
my $safe_key = DBQuoteSpecial($key);
DBQueryWarn("insert into nonlocal_user_pubkeys set ".
" uid=$safe_uid, uid_idx='$uid_idx', ".
" idx=NULL, stamp=now(), pubkey=$safe_key")
or return -1;
}
return 0;
}
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2005-2010 University of Utah and the Flux Group.
# Copyright (c) 2005-2011 University of Utah and the Flux Group.
# All rights reserved.
#
package User;
......@@ -542,7 +542,7 @@ sub Delete($)
or return -1;
DBQueryWarn("delete from users where uid_idx='$uid_idx'")
or return -1;
return 0;
}
......@@ -1631,243 +1631,6 @@ sub escapeshellarg($)
return $1;
}
#############################################################################
# Non-local users, as for federation/geni.
#
package User::NonLocal;
use emdb;
use User;
use libtestbed;
use English;
use overload ('""' => 'Stringify');
# Cache of instances to avoid regenerating them.
my %nonlocal_users = ();
#
# Lookup by idx.
#
sub Lookup($$)
{
my ($class, $token) = @_;
my $idx;
my $query_result;
if ($token =~ /^\d+$/) {
$idx = $token;
}
elsif ($token =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/) {
$query_result =
DBQueryWarn("select uid_idx from nonlocal_users ".
"where uid_uuid='$token'");
return undef
if (! $query_result || !$query_result->numrows);
($idx) = $query_result->fetchrow_array();
}
else {
return undef;
}
# Look in cache first
return $nonlocal_users{"$idx"}
if (exists($nonlocal_users{"$idx"}));
$query_result =
DBQueryWarn("select * from nonlocal_users where uid_idx='$idx'");
return undef
if (!$query_result || !$query_result->numrows);
my $self = {};
$self->{'USER'} = $query_result->fetchrow_hashref();
# Want to know if this is a shadow of a local user.
$self->{'SHADOW'} = User->Lookup($idx);
bless($self, $class);
# Add to cache.
$nonlocal_users{$self->{'USER'}->{'uid_idx'}} = $self;
return $self;
}
# accessors
sub field($$) { return ((! ref($_[0])) ? -1 : $_[0]->{'USER'}->{$_[1]}); }
sub uid_idx($) { return field($_[0], "uid_idx"); }
sub idx($) { return field($_[0], "uid_idx"); }
sub uid($) { return field($_[0], "uid"); }
sub uid_uuid($) { return field($_[0], "uid_uuid"); }
sub created($) { return field($_[0], "created"); }
sub name($) { return field($_[0], "name"); }
sub email($) { return field($_[0], "email"); }
sub shadow($) { return $_[0]->{'SHADOW'}; }
#
# Stringify for output.
#
sub Stringify($)
{
my ($self) = @_;
my $uid = $self->uid();
my $idx = $self->idx();
return "[NonLocalUser: $uid, IDX: $idx]";
}
#
# Class function to create a new nonlocal user and return object.
#
sub Create($$$$$$;$)
{
my ($class, $idx, $uid, $uuid, $name, $email, $sshkeys) = @_;
my @insert_data = ();
my $islocal = 0;
# Every user gets a new unique index.
if (!defined($idx) || $idx == 0) {
$idx = User->NextIDX();
}
else {
# Sanity check.
my $existing = User->Lookup($idx);
if (defined($existing)) {
if ($uuid eq $existing->uuid()) {
#
# Shadow a local user, strictly for tmcd.
#
$islocal = 1;
}
else {
print STDERR "NonLocal user with $idx exists: $existing\n";
return undef;
}
}
}
# Now tack on other stuff we need.
push(@insert_data, "created=now()");
push(@insert_data, "uid_idx='$idx'");
my $safe_uid = DBQuoteSpecial($uid);
my $safe_name = DBQuoteSpecial($name);
my $safe_email = DBQuoteSpecial($email);
my $safe_uuid = DBQuoteSpecial($uuid);
push(@insert_data, "uid=$safe_uid");
push(@insert_data, "name=$safe_name");
push(@insert_data, "email=$safe_email");
push(@insert_data, "uid_uuid=$safe_uuid");
if (defined($sshkeys)) {
foreach my $sshkey (@{ $sshkeys }) {
my $safe_sshkey = DBQuoteSpecial($sshkey);
DBQueryWarn("insert into nonlocal_user_pubkeys set ".
" uid=$safe_uid, uid_idx='$idx', ".
" idx=NULL, stamp=now(), pubkey=$safe_sshkey")
or return undef;
}
}
# Insert into DB.
if (!DBQueryWarn("insert into nonlocal_users set " .
join(",", @insert_data))) {
DBQueryWarn("delete from nonlocal_user_pubkeys where uid_idx='$idx'")
if (!$islocal);
return undef;
}
return User::NonLocal->Lookup($idx);
}
#
# Modify the keys.
#
sub ModifyKeys($$)
{
my ($self, $sshkeys) = @_;
my $idx = $self->uid_idx();
my $uid = $self->uid();
DBQueryWarn("delete from nonlocal_user_pubkeys where uid_idx='$idx'")
or return -1;
if (defined($sshkeys)) {
foreach my $sshkey (@{ $sshkeys }) {
my $safe_sshkey = DBQuoteSpecial($sshkey);
DBQueryWarn("insert into nonlocal_user_pubkeys set ".
" uid='$uid', uid_idx='$idx', ".
" idx=NULL, stamp=now(), pubkey=$safe_sshkey")
or return -1;
}
}
return 0;
}
#
# Delete the user, as for registration errors.
#
sub Delete($)
{
my ($self) = @_;
return 0
if (! ref($self));
my $idx = $self->idx();
DBQueryWarn("delete from nonlocal_user_pubkeys where uid_idx='$idx'")
or return -1;
DBQueryWarn("delete from nonlocal_user_bindings where uid_idx='$idx'")
or return -1;
DBQueryWarn("delete from nonlocal_users where uid_idx='$idx'")
or return -1;
return 0;
}
#
# Bind nonlocal user to experiment (slice, in Geni).
#
sub BindToExperiment($$)
{
my ($self, $experiment) = @_;
return -1
if (! ref($self));
my $uid = $self->uid();
my $idx = $self->idx();
my $exptidx = $experiment->idx();
# Insert into DB.
DBQueryWarn("replace into nonlocal_user_bindings set " .
" uid='$uid', uid_idx='$idx', exptidx='$exptidx'")
or return -1;
return 0;
}
sub UnBindFromExperiment($$)
{
my ($self, $experiment) = @_;
return -1
if (! ref($self));
my $uid = $self->uid();
my $idx = $self->idx();
my $exptidx = $experiment->idx();
# Insert into DB.
DBQueryWarn("delete from nonlocal_user_bindings " .
"where uid_idx='$idx' and exptidx='$exptidx'")
or return -1;
return 0;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
#
# Add perl Digest-SHA1 port.
#
use strict;
use libinstall;
use emdbi;
sub InstallUpdate($$)
{
my ($version, $phase) = @_;
if ($phase eq "pre") {
Phase "p5-Digest-SHA1", "Checking for port p5-Digest-SHA1", sub {
DoneIfPackageInstalled("p5-Digest-SHA1");
ExecQuietFatal("cd $PORTSDIR/security/p5-Digest-SHA1; ".
"make MASTER_SITE_FREEBSD=1 -DBATCH install");
};
}
return 0;
}
1;
......@@ -28,6 +28,7 @@ use emutil;
use Compress::Zlib;
use MIME::Base64;
use XML::LibXML;
use Data::Dumper;
# Disable UUID checks in GeniCredential.
$GeniCredential::CHECK_UUID = 0;
......@@ -240,35 +241,25 @@ sub CreateSliver()
auto_add_sa($cred);
}
# circulate through the users and bind them to the slice
my $caller_urn = $ENV{'GENIURN'};
my $caller_keys = undef;
foreach my $user (@$users) {
my $user_urn = $user->{'urn'};
# Skip if it is not the caller
next if ($user_urn ne $caller_urn);
$caller_keys = $user->{keys};
}
# Package the caller_keys in a list of hashes the way the CM wants
# it. Each hash has two keys ('type' and 'key'). 'type' is always
# 'ssh' for us, and 'key' is the key.
my $sliver_keys = undef;
if (defined($caller_keys)) {
$sliver_keys = [];
foreach my $key (@$caller_keys) {
# The CMV2 does not like newlines at the end of the keys.
chomp($key);
push(@$sliver_keys, {'type' => 'ssh', 'key' => $key});
}
if (@$users) {
$sliver_keys = [];
foreach my $user (@$users) {
my $user_urn = $user->{'urn'};
my @user_keys = ();
foreach my $key (@{ $user->{keys} }) {
# The CMV2 does not like newlines at the end of the keys.
chomp($key);
push(@user_keys, {'type' => 'ssh', 'key' => $key});
}
push(@{$sliver_keys}, {'urn' => $user_urn,
'keys' => \@user_keys});
}
}
# This only gets us part of the way there. For users that are not
# *this* user, we need to somehow add their keys, which also
# requires adding them as a user *without* their certificate. Not
# sure we'll be able to do that.
# Invoke CreateSliver
my $create_args = {
'slice_urn' => $slice_urn,
......@@ -276,7 +267,6 @@ sub CreateSliver()
'credentials' => $credentials,
'keys' => $sliver_keys
};
my $response = GeniCMV2::CreateSliver($create_args);
if (!ref($response)) {
# This is cause GeniCMV2::CreateSliver does a fork, and the child
......
......@@ -442,7 +442,6 @@ sub Register($)
if ($type eq "user") {
my $name = $info->{'name'};
my $email = $info->{'email'};
my $keys = undef;
if (! TBcheck_dbslot($name, "users", "usr_name",
TBDB_CHECKDBSLOT_ERROR)) {
......@@ -474,9 +473,9 @@ sub Register($)
"Not allowed to change hrn");
}
#
# Update operation, but only name, email, and keys for now.
# Update operation, but only name, email
#
if ($existing->Modify($name, $email, $keys) != 0) {
if ($existing->Modify($name, $email) != 0) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not update user");
}
......@@ -502,8 +501,7 @@ sub Register($)
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"user already registered");
}
my $newuser = GeniUser->Create($certificate, $slice_authority,
$info, $keys);
my $newuser = GeniUser->Create($certificate, $slice_authority, $info);
if (!defined($newuser)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not be registered");
......
......@@ -40,6 +40,7 @@ use emutil;
use EmulabConstants;
use libEmulab;
use Lan;
use Experiment;
use English;
use Data::Dumper;
use XML::Simple;
......@@ -49,6 +50,7 @@ use Time::Local;
use Compress::Zlib;
use File::Temp qw(tempfile);
use MIME::Base64;
use Digest::SHA1 qw(sha1_hex);
# Configure variables
my $TB = "@prefix@";
......@@ -1945,21 +1947,6 @@ sub SliverWorkAux($$$$$$$)
"Slice has expired");
}
#
# Create the user.
#
my $owner = CreateUserFromCertificate($owner_cert);
return $owner
if (GeniResponse::IsResponse($owner));
if (defined($keys)) {
$response = CheckKeys($keys);
return $response
if (GeniResponse::IsResponse($response));
$owner->Modify(undef, undef, $keys);
}
my $experiment = GeniExperiment($slice);
if (!defined($experiment)) {
$slice->UnLock();
......@@ -1971,6 +1958,23 @@ sub SliverWorkAux($$$$$$$)
my $pid = $experiment->pid();
my $eid = $experiment->eid();
#
# Create the user.
#
my $owner = CreateUserFromCertificate($owner_cert);
return $owner
if (GeniResponse::IsResponse($owner));
if (defined($keys)) {
$response = AddKeys($slice, $owner, $keys);
if (GeniResponse::IsResponse($response)) {
$slice->UnLock();
$ticket->UnLock()
if (defined($ticket));
return $response;
}
}
#
# Figure out what nodes to allocate or free.
#
......@@ -2184,16 +2188,6 @@ sub SliverWorkAux($$$$$$$)
}
}
#
# Create an emulab nonlocal user for tmcd.
#
if ($owner->BindToSlice($slice)) {
$message = "Error binding user to slice";
print STDERR "$message\n";
goto bad;
}
#
# We are actually an Aggregate, so return an aggregate of slivers,
# even if there is just one node. This makes sliceupdate easier.
......@@ -3564,13 +3558,7 @@ sub BindToSlice($)
my $user = CreateUserFromCertificate($credential->owner_cert());
return $user
if (GeniResponse::IsResponse($user));
if (defined($keys)) {
my $response = CheckKeys($keys);
return $response
if (GeniResponse::IsResponse($response));
$user->Modify(undef, undef, $keys);
}
if ($slice->Lock() != 0) {
return GeniResponse->BusyResponse();
}
......@@ -3580,11 +3568,12 @@ sub BindToSlice($)
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Error binding slice to user");
}
# Bind for existing slivers.
if ($user->BindToSlice($slice) != 0) {
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Error binding user to slice");
if (defined($keys)) {
my $response = AddKeys($slice, $user, $keys);
if (GeniResponse::IsResponse($response)) {
$slice->UnLock();
return $response;
}
}
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
......@@ -4229,7 +4218,7 @@ sub CreateUserFromCertificate($)
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create user from your certificate")
if (!defined($user));
return $user;
}
......@@ -4562,7 +4551,6 @@ sub GeniExperiment($)
my $gid = $pid;
my $hrn = $slice->hrn();
my $urn = $slice->urn();
require Experiment;
my $experiment = Experiment->Lookup($uuid);
return $experiment
......@@ -4799,27 +4787,120 @@ sub CheckTicket($)
return $ticket;
}
sub CheckKeys($)
sub AddKeys($$$)
{
my ($keys) = @_;
my ($slice, $owner, $keys) = @_;
goto bad
if (!ref($keys));
foreach my $keyref (@{ $keys }) {
goto bad
if (!ref($keyref));
my $key = $keyref->{'key'};
my $type = $keyref->{'type'};
if (! (ref($keys) && ref($keys) eq "ARRAY"));
goto bad
if (! (defined($key) && defined($type) &&
my $slice_experiment = $slice->GetExperiment();
if (!$slice_experiment) {
print STDERR "AddKeys: No experiment for $slice\n";
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
my $keychecker = sub {
my ($arg) = @_;
return -1
if (! (ref($arg) && ref($arg) eq "HASH"));
my $key = $arg->{'key'};