Commit 85cb063b authored by Leigh B Stoller's avatar Leigh B Stoller

Two co-mingled sets of changes:

1) Implement the latest dataset read/write access settings from frontend to
   backend. Also updates for simultaneous read-only usage.

2) New configure options: PROTOGENI_LOCALUSER and PROTOGENI_GENIWEBLOGIN.

   The first changes the way that projects and users are treated at the
   CM. When set, we create real accounts (marked as nonlocal) for users and
   also create real projects (also marked as nonlocal). Users are added to
   those projects according to their credentials. The underlying experiment
   is thus owned by the user and in the project, although all the work is
   still done by the geniuser pseudo user. The advantage of this approach
   is that we can use standard emulab access checks to control access to
   objects like datasets. Maybe images too at some point.

   NOTE: Users are not removed from projects once they are added; we are
   going to need to deal with this, perhaps by adding an expiration stamp
   to the groups_membership tables, and using the credential expiration to
   mark it.

   The second new configure option turns on the web login via the geni
   trusted signer. So, if I create a sliver on a backend cluster when both
   options are set, I can use the trusted signer to log into my newly
   created account on the cluster, and see it (via the emulab classic web
   interface).

   All this is in flux, might end up being a bogus approach in the end.
parent 8741f48a
...@@ -52,6 +52,9 @@ my $TB = "@prefix@"; ...@@ -52,6 +52,9 @@ my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@"; my $TBOPS = "@TBOPSEMAIL@";
my $OURDOMAIN = "@OURDOMAIN@"; my $OURDOMAIN = "@OURDOMAIN@";
# Debugging
my $usemydevtree = 0;
# #
# Lookup by uuid. # Lookup by uuid.
# #
...@@ -83,6 +86,27 @@ sub Lookup($$;$) ...@@ -83,6 +86,27 @@ sub Lookup($$;$)
return $self; return $self;
} }
#
# Lookup by remote URN.
#
sub LookupByRemoteURN($$)
{
my ($class, $urn) = @_;
return undef
if (!GeniHRN::IsValid($urn));
my $safe_urn = DBQuoteSpecial($urn);
my $query_result =
DBQueryWarn("select uuid from apt_datasets ".
"where remote_urn=$safe_urn");
return undef
if (!$query_result || !$query_result->numrows);
my ($uuid) = $query_result->fetchrow_array();
return Lookup($class, $uuid);
}
AUTOLOAD { AUTOLOAD {
my $self = $_[0]; my $self = $_[0];
my $type = ref($self) or croak "$self is not an object"; my $type = ref($self) or croak "$self is not an object";
...@@ -108,9 +132,10 @@ sub DESTROY { ...@@ -108,9 +132,10 @@ sub DESTROY {
sub ValidBlockstoreBackend($) sub ValidBlockstoreBackend($)
{ {
my ($authority) = @_; my ($authority) = @_;
my ($domain,$subauth) = split(":", $authority);
return 1 return 1
if ($authority eq "emulab.net" || $authority eq "apt.emulab.net"); if ($domain eq "emulab.net" || $domain eq "apt.emulab.net");
return 0; return 0;
} }
...@@ -431,6 +456,8 @@ sub CreateDataset($) ...@@ -431,6 +456,8 @@ sub CreateDataset($)
"size" => $self->size(), "size" => $self->size(),
"name" => $self->dataset_id(), "name" => $self->dataset_id(),
"type" => $self->type(), "type" => $self->type(),
"read_access" => $self->read_access(),
"write_access"=> $self->write_access(),
"credentials" => [$credential->asString(), "credentials" => [$credential->asString(),
$speaksfor_credential->asString()], $speaksfor_credential->asString()],
}; };
...@@ -440,7 +467,7 @@ sub CreateDataset($) ...@@ -440,7 +467,7 @@ sub CreateDataset($)
if (defined($self->expires())); if (defined($self->expires()));
my $cmurl = $authority->url(); my $cmurl = $authority->url();
# $cmurl =~ s/protogeni/protogeni\/stoller/; $cmurl =~ s/protogeni/protogeni\/stoller/ if ($usemydevtree);
return Genixmlrpc::CallMethod($cmurl, $context, "CreateDataset", $args); return Genixmlrpc::CallMethod($cmurl, $context, "CreateDataset", $args);
} }
...@@ -471,7 +498,7 @@ sub DeleteDataset($) ...@@ -471,7 +498,7 @@ sub DeleteDataset($)
$speaksfor_credential->asString()], $speaksfor_credential->asString()],
}; };
my $cmurl = $authority->url(); my $cmurl = $authority->url();
# $cmurl =~ s/protogeni/protogeni\/stoller/; $cmurl =~ s/protogeni/protogeni\/stoller/ if ($usemydevtree);
return Genixmlrpc::CallMethod($cmurl, $context, "DeleteDataset", $args); return Genixmlrpc::CallMethod($cmurl, $context, "DeleteDataset", $args);
} }
...@@ -498,15 +525,13 @@ sub ModifyDataset($) ...@@ -498,15 +525,13 @@ sub ModifyDataset($)
my $args = { my $args = {
"name" => $self->dataset_id(), "name" => $self->dataset_id(),
"read_access" => $self->read_access(),
"write_access"=> $self->write_access(),
"credentials" => [$credential->asString(), "credentials" => [$credential->asString(),
$speaksfor_credential->asString()], $speaksfor_credential->asString()],
}; };
# This is all we can change right now.
$args->{"expires"} = emutil::TBDateStringGMT($self->expires())
if (defined($self->expires()));
my $cmurl = $authority->url(); my $cmurl = $authority->url();
# $cmurl =~ s/protogeni/protogeni\/stoller/; $cmurl =~ s/protogeni/protogeni\/stoller/ if ($usemydevtree);
return Genixmlrpc::CallMethod($cmurl, $context, "ModifyDataset", $args); return Genixmlrpc::CallMethod($cmurl, $context, "ModifyDataset", $args);
} }
...@@ -538,7 +563,7 @@ sub ExtendDataset($) ...@@ -538,7 +563,7 @@ sub ExtendDataset($)
"extend" => 1, "extend" => 1,
}; };
my $cmurl = $authority->url(); my $cmurl = $authority->url();
# $cmurl =~ s/protogeni/protogeni\/stoller/; $cmurl =~ s/protogeni/protogeni\/stoller/ if ($usemydevtree);
return Genixmlrpc::CallMethod($cmurl, $context, "ModifyDataset", $args); return Genixmlrpc::CallMethod($cmurl, $context, "ModifyDataset", $args);
} }
...@@ -569,7 +594,7 @@ sub DescribeDataset($) ...@@ -569,7 +594,7 @@ sub DescribeDataset($)
$speaksfor_credential->asString()], $speaksfor_credential->asString()],
}; };
my $cmurl = $authority->url(); my $cmurl = $authority->url();
# $cmurl =~ s/protogeni/protogeni\/stoller/; $cmurl =~ s/protogeni/protogeni\/stoller/ if ($usemydevtree);
return Genixmlrpc::CallMethod($cmurl, $context, "DescribeDataset", $args); return Genixmlrpc::CallMethod($cmurl, $context, "DescribeDataset", $args);
} }
......
...@@ -887,8 +887,11 @@ sub CheckDatasets($$$) ...@@ -887,8 +887,11 @@ sub CheckDatasets($$$)
} }
my $dataset = APT_Dataset->Lookup("$pid/$id"); my $dataset = APT_Dataset->Lookup("$pid/$id");
if (!defined($dataset)) { if (!defined($dataset)) {
$$pmsg = "Persistent dataset '$pid/$id' does not exist"; $dataset = APT_Dataset->LookupByRemoteURN($leaseurn);
return 1; if (!defined($dataset)) {
$$pmsg = "Persistent dataset '$pid/$id' does not exist";
return 1;
}
} }
# #
...@@ -898,20 +901,15 @@ sub CheckDatasets($$$) ...@@ -898,20 +901,15 @@ sub CheckDatasets($$$)
# settings. # settings.
# #
my ($d_authority) = GeniHRN::Parse($dataset->aggregate_urn()); my ($d_authority) = GeniHRN::Parse($dataset->aggregate_urn());
if ($d_authority ne $authority) { my ($domain,$subauth) = split(":", $authority);
if ($domain ne $d_authority) {
$$pmsg = "Persistent dataset '$pid/$id' in not on $authority"; $$pmsg = "Persistent dataset '$pid/$id' in not on $authority";
return 1; return 1;
} }
# #
# Basic permission checks, very little support at the moment. # XXX Need basic frontend permission checks?
# Just make sure that the profile and the dataset are in the
# same pid.
# #
if ($ppid ne $dataset->pid()) {
$$pmsg = "Not allowed to use dataset '$pid/$id' in profile";
return 1;
}
} }
} }
return 0; return 0;
......
...@@ -91,6 +91,7 @@ $| = 1; ...@@ -91,6 +91,7 @@ $| = 1;
# Load the Testbed support stuff. # Load the Testbed support stuff.
use lib "@prefix@/lib"; use lib "@prefix@/lib";
use EmulabConstants;
use libtestbed; use libtestbed;
use libaudit; use libaudit;
use APT_Profile; use APT_Profile;
...@@ -98,6 +99,7 @@ use APT_Instance; ...@@ -98,6 +99,7 @@ use APT_Instance;
use APT_Geni; use APT_Geni;
use APT_Dataset; use APT_Dataset;
use User; use User;
use Project;
use OSinfo; use OSinfo;
use emutil; use emutil;
use GeniDB; use GeniDB;
...@@ -251,7 +253,7 @@ foreach my $key ("username", "email", "profile") { ...@@ -251,7 +253,7 @@ foreach my $key ("username", "email", "profile") {
# #
# Gather up args and sanity check. # Gather up args and sanity check.
# #
my ($value, $user_urn, $user_uid, $user_hrn, $user_email, my ($value, $user_urn, $user_uid, $user_hrn, $user_email, $project, $pid,
$sshkey, $profile, $profileid, $version, $rspecstr, $errmsg); $sshkey, $profile, $profileid, $version, $rspecstr, $errmsg);
# #
...@@ -456,6 +458,9 @@ if ($localuser) { ...@@ -456,6 +458,9 @@ if ($localuser) {
if (0) { if (0) {
fatal("Could not update ssh keys for nonlocal user"); fatal("Could not update ssh keys for nonlocal user");
} }
# Nonlocal users get the holding project.
$pid = "CloudLab";
} }
elsif (defined($sshkey) && !$emulab_user->LookupSSHKey($sshkey)) { elsif (defined($sshkey) && !$emulab_user->LookupSSHKey($sshkey)) {
# #
...@@ -492,15 +497,32 @@ if ($localuser) { ...@@ -492,15 +497,32 @@ if ($localuser) {
$emulab_user->GenEncryptedCert()) { $emulab_user->GenEncryptedCert()) {
fatal("Could not (re)generate encrypted certificate"); fatal("Could not (re)generate encrypted certificate");
} }
}
elsif (!$localuser && defined($sshkey)) { # Local users are required to select a project.
# if (! exists($xmlparse->{'attribute'}->{"pid"})) {
# Guest user; remember key. For now we accept only one key. We store fatal("No project provided for new instance");
# it simply so we can display it again for the user in the web interface. }
# We allow key reuse for existing users, see above. $project = Project->Lookup($xmlparse->{'attribute'}->{"pid"}->{"value"});
# if (!defined($project)) {
$geniuser->DeleteKeys(); fatal("Project provided does not exist");
$geniuser->AddKey($sshkey); }
if (!$project->AccessCheck($emulab_user, TB_PROJECT_CREATEEXPT)) {
fatal("No permission to create experiments in project $pid");
}
$pid = $project->pid();
}
elsif (!$localuser) {
if (defined($sshkey)) { #
# Guest user; remember key. For now we accept only one key. We store
# it simply so we can display it again for the user in the web
# interface. We allow key reuse for existing users, see
# above.
#
$geniuser->DeleteKeys();
$geniuser->AddKey($sshkey);
}
# Guest users get a holding project.
$pid = "aptguests";
} }
# There will be "internal" keys cause we pass the flag asking for them. # There will be "internal" keys cause we pass the flag asking for them.
my @sshkeys; my @sshkeys;
...@@ -511,19 +533,22 @@ if ($geniuser->GetKeyBundle(\@sshkeys, 1) < 0 || !@sshkeys) { ...@@ -511,19 +533,22 @@ if ($geniuser->GetKeyBundle(\@sshkeys, 1) < 0 || !@sshkeys) {
# Generate the extra credentials that tells the backend this experiment # Generate the extra credentials that tells the backend this experiment
# can access the datasets. # can access the datasets.
my @dataset_credentials = (); my @dataset_credentials = ();
if (defined($profile) && if (defined($profile)) {
CreateDatasetCreds($rspecstr, my $retval = CreateDatasetCreds($rspecstr,
$profile->pid(), $geniuser, $profile->pid(), $geniuser,
\$errmsg, \@dataset_credentials)) { \$errmsg, \@dataset_credentials);
fatal($errmsg); if ($retval) {
($retval < 0 ? fatal($errmsg) : UserError($errmsg));
}
} }
#
# #
# Now generate a slice registration and credential # Now generate a slice registration and credential
# #
my $safe_uid = $user_uid; $safe_uid =~ s/_/-/; my $safe_uid = $user_uid; $safe_uid =~ s/_/-/;
my $slice_id = $safe_uid . "-QV" . TBGetUniqueIndex('next_quickvm', 1); my $slice_id = $safe_uid . "-QV" . TBGetUniqueIndex('next_quickvm', 1);
my $slice_urn = GeniHRN::Generate($OURDOMAIN, "slice", $slice_id); my $slice_urn = GeniHRN::Generate("${OURDOMAIN}:${pid}", "slice", $slice_id);
my $slice_hrn = "${PGENIDOMAIN}.${slice_id}"; my $slice_hrn = "${PGENIDOMAIN}.${slice_id}";
my $SERVER_NAME = (exists($ENV{"SERVER_NAME"}) ? $ENV{"SERVER_NAME"} : ""); my $SERVER_NAME = (exists($ENV{"SERVER_NAME"}) ? $ENV{"SERVER_NAME"} : "");
...@@ -590,16 +615,22 @@ my $quickvm_uuid = (defined($quickuuid) ? $quickuuid : NewUUID()); ...@@ -590,16 +615,22 @@ my $quickvm_uuid = (defined($quickuuid) ? $quickuuid : NewUUID());
if (!defined($quickvm_uuid)) { if (!defined($quickvm_uuid)) {
fatal("Could not generate a new uuid"); fatal("Could not generate a new uuid");
} }
my $instance = APT_Instance->Create({'uuid' => $quickvm_uuid, my $blob = {'uuid' => $quickvm_uuid,
'profile_id' => $profileid, 'profile_id' => $profileid,
'profile_version' => $version, 'profile_version' => $version,
'slice_uuid' => $slice_uuid, 'slice_uuid' => $slice_uuid,
'creator' => $geniuser->uid(), 'creator' => $geniuser->uid(),
'creator_idx' => $geniuser->idx(), 'creator_idx' => $geniuser->idx(),
'creator_uuid' => $geniuser->uuid(), 'creator_uuid' => $geniuser->uuid(),
'aggregate_urn'=> $CMURN, 'aggregate_urn'=> $CMURN,
'status' => "created", 'status' => "created",
'servername' => $SERVER_NAME}); 'servername' => $SERVER_NAME
};
if (defined($project)) {
$blob->{"pid"} = $project->pid();
$blob->{"pid_idx"} = $project->pid_idx();
}
my $instance = APT_Instance->Create($blob);
if (!defined($instance)) { if (!defined($instance)) {
$slice->Delete(); $slice->Delete();
fatal("Could not create instance record for $quickvm_uuid"); fatal("Could not create instance record for $quickvm_uuid");
...@@ -796,8 +827,11 @@ sub CreateDatasetCreds($$$$$) ...@@ -796,8 +827,11 @@ sub CreateDatasetCreds($$$$$)
} }
my $dataset = APT_Dataset->Lookup("$pid/$id"); my $dataset = APT_Dataset->Lookup("$pid/$id");
if (!defined($dataset)) { if (!defined($dataset)) {
$$pmsg = "Persistent dataset '$pid/$id' does not exist"; $dataset = APT_Dataset->LookupByRemoteURN($leaseurn);
return 1; if (!defined($dataset)) {
$$pmsg = "Persistent dataset '$pid/$id' does not exist";
return 1;
}
} }
my $certificate = $dataset->GetCertificate(); my $certificate = $dataset->GetCertificate();
if (!defined($certificate)) { if (!defined($certificate)) {
......
#!/usr/bin/perl -w #!/usr/bin/perl -w
# #
# Copyright (c) 2000-2014 University of Utah and the Flux Group. # Copyright (c) 2000-2015 University of Utah and the Flux Group.
# #
# {{{EMULAB-LICENSE # {{{EMULAB-LICENSE
# #
...@@ -155,7 +155,8 @@ sub DoCreate() ...@@ -155,7 +155,8 @@ sub DoCreate()
{ {
my $usage = sub { my $usage = sub {
print STDERR "Usage: manage_dataset create ". print STDERR "Usage: manage_dataset create ".
"[-t type] [-f fstype] [-e expiration] [-p privacy] ". "[-t type] [-f fstype] [-e expiration] ".
"[-R global|project] [-W creator|project] ".
"-s size pid/name\n"; "-s size pid/name\n";
exit(-1); exit(-1);
}; };
...@@ -164,12 +165,13 @@ sub DoCreate() ...@@ -164,12 +165,13 @@ sub DoCreate()
my $errmsg; my $errmsg;
my $pid; my $pid;
my $expires; my $expires;
my $privacy = "private";
my $size; my $size;
my $type = "stdataset"; my $type = "stdataset";
my $fstype; my $fstype;
my $read_access;
my $write_access;
my $optlist = "ds:t:e:f:w:p:"; my $optlist = "ds:t:e:f:w:p:R:W:";
my %options = (); my %options = ();
if (! getopts($optlist, \%options)) { if (! getopts($optlist, \%options)) {
&$usage(); &$usage();
...@@ -187,10 +189,20 @@ sub DoCreate() ...@@ -187,10 +189,20 @@ sub DoCreate()
&$usage() &$usage()
if ($fstype !~ /^(ext2|ext3|ext4|ufs|ufs2)$/); if ($fstype !~ /^(ext2|ext3|ext4|ufs|ufs2)$/);
} }
if (defined($options{"p"})) { if (defined($options{"f"})) {
$privacy = $options{"p"}; $fstype = $options{"f"};
&$usage()
if ($fstype !~ /^(ext2|ext3|ext4|ufs|ufs2)$/);
}
if (defined($options{"R"})) {
$read_access = $options{"R"};
&$usage()
if ($read_access !~ /^(global|project)$/);
}
if (defined($options{"W"})) {
$write_access = $options{"W"};
&$usage() &$usage()
if ($privacy !~ /^(private|shared|public)$/); if ($write_access !~ /^(creator|project)$/);
} }
if (defined($options{"s"})) { if (defined($options{"s"})) {
if ($options{"s"} =~ /^(\d+)$/) { if ($options{"s"} =~ /^(\d+)$/) {
...@@ -253,12 +265,10 @@ sub DoCreate() ...@@ -253,12 +265,10 @@ sub DoCreate()
if (defined($fstype)); if (defined($fstype));
$blob->{"expires"} = TBDateStringLocal($expires) $blob->{"expires"} = TBDateStringLocal($expires)
if (defined($expires)); if (defined($expires));
if ($privacy eq "shared") { $blob->{"read_access"} = $read_access
$blob->{'shared'} = 1; if (defined($read_access));
} $blob->{"write_access"} = $write_access
elsif ($privacy eq "public") { if (defined($write_access));
$blob->{'public'} = 1;
}
my $dataset = APT_Dataset->Create($blob); my $dataset = APT_Dataset->Create($blob);
if (!defined($dataset)) { if (!defined($dataset)) {
...@@ -275,7 +285,8 @@ sub DoCreate() ...@@ -275,7 +285,8 @@ sub DoCreate()
goto failed; goto failed;
} }
$blob = $response->value(); $blob = $response->value();
$dataset->Update({"remote_uuid" => $blob->{"uuid"}}); $dataset->Update({"remote_uuid" => $blob->{"uuid"},
"remote_urn" => $blob->{"urn"}});
# #
# Okay, this is silly; there is no distinct state for resource allocation. # Okay, this is silly; there is no distinct state for resource allocation.
# It is unapproved and locked. The other side tells us its locked in the # It is unapproved and locked. The other side tells us its locked in the
...@@ -440,10 +451,10 @@ sub DoModify() ...@@ -440,10 +451,10 @@ sub DoModify()
my $usage = sub { my $usage = sub {
print STDERR "Usage: manage_dataset modify ". print STDERR "Usage: manage_dataset modify ".
"[-e expiration] [-p privacy] pid/name\n"; "[-R global|project] [-W creator|project] pid/name\n";
exit(-1); exit(-1);
}; };
my $optlist = "e:p:"; my $optlist = "R:W:";
my %options = (); my %options = ();
if (! getopts($optlist, \%options)) { if (! getopts($optlist, \%options)) {
&$usage(); &$usage();
...@@ -456,42 +467,28 @@ sub DoModify() ...@@ -456,42 +467,28 @@ sub DoModify()
if (!defined($dataset)) { if (!defined($dataset)) {
fatal("No such dataset"); fatal("No such dataset");
} }
if (defined($options{"p"})) { my $blob = {};
if (defined($options{"R"})) {
my $read_access = $options{"R"};
&$usage() &$usage()
if ($options{"p"} !~ /^(private|shared|public)$/); if ($read_access !~ /^(global|project)$/);
$blob->{'read_access'} = $read_access;
} }
if (defined($options{"e"})) { if (defined($options{"W"})) {
my $expires = str2time($options{"e"}); my $write_access = $options{"W"};
if (!defined($expires)) { &$usage()
fatal("Could not parse expiration date."); if ($write_access !~ /^(creator|project)$/);
} $blob->{'write_access'} = $write_access;
} }
if ($dataset->Lock()) { if ($dataset->Lock()) {
fatal("dataset is busy, cannot lock it"); fatal("dataset is busy, cannot lock it");
} }
if (defined($options{"p"})) { if (keys(%$blob)) {
my $privacy = $options{"p"}; if ($dataset->Update($blob)) {
my $blob = {"shared" => 0, "public" => 0};
if ($privacy eq "shared") {
$blob->{'shared'} = 1;
}
elsif ($privacy eq "public") {
$blob->{'public'} = 1;
}
if ($dataset->Update($privacy)) {
$errmsg = "Could not update privacy settings!"; $errmsg = "Could not update privacy settings!";
goto failed; goto failed;
} }
} }
if (defined($options{"e"})) {
my $expires = $options{"e"};
print "$expires\n";
if ($dataset->Update({"expires" => TBDateStringLocal($expires)})) {
$errmsg = "Could not update expiration!";
goto failed;
}
}
my $response = $dataset->ModifyDataset(); my $response = $dataset->ModifyDataset();
if ($response->code() != GENIRESPONSE_SUCCESS) { if ($response->code() != GENIRESPONSE_SUCCESS) {
if ($response->code() == GENIRESPONSE_SEARCHFAILED) { if ($response->code() == GENIRESPONSE_SEARCHFAILED) {
...@@ -502,7 +499,7 @@ sub DoModify() ...@@ -502,7 +499,7 @@ sub DoModify()
} }
goto failed; goto failed;
} }
my $blob = $response->value(); $blob = $response->value();
$dataset->Update({"expires" => TBDateStringLocal($blob->{"expires"})}); $dataset->Update({"expires" => TBDateStringLocal($blob->{"expires"})});
$dataset->Unlock(); $dataset->Unlock();
return 0; return 0;
......
#!/usr/bin/perl -wT #!/usr/bin/perl -wT
# #
# Copyright (c) 2008-2014 University of Utah and the Flux Group. # Copyright (c) 2008-2015 University of Utah and the Flux Group.
# #
# {{{GENIPUBLIC-LICENSE # {{{GENIPUBLIC-LICENSE
# #
...@@ -62,6 +62,7 @@ use emutil; ...@@ -62,6 +62,7 @@ use emutil;
use EmulabConstants; use EmulabConstants;
use libEmulab; use libEmulab;
use Lan; use Lan;
use User;
use Experiment; use Experiment;
use NodeType; use NodeType;
use English; use English;
...@@ -122,6 +123,7 @@ my $IMAGE_SETUP = "$TB/sbin/image_setup"; ...@@ -122,6 +123,7 @@ my $IMAGE_SETUP = "$TB/sbin/image_setup";
my $SHAREVLAN = "$TB/sbin/sharevlan"; my $SHAREVLAN = "$TB/sbin/sharevlan";
my $FWNAME = "fw"; my $FWNAME = "fw";
my $API_VERSION = 1; my $API_VERSION = 1;
my $PROTOGENI_LOCALUSER= @PROTOGENI_LOCALUSER@;
# #
# Tell the client what API revision we support. The correspondence # Tell the client what API revision we support. The correspondence
...@@ -5494,11 +5496,17 @@ sub CreateUserFromCertificate($) ...@@ -5494,11 +5496,17 @@ sub CreateUserFromCertificate($)
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef, return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Malformed URN"); "Malformed URN");
} }
my $urn = $certificate->urn(); my $urn = $certificate->urn();
my $email = $certificate->email();
# Local users always exist, so pass flag. # Local users always exist, so pass flag.
my $user = GeniUser->Lookup($certificate->urn(), 1); my $user = GeniUser->Lookup($certificate->urn(), 1);
if (defined($user)) { if (defined($user)) {
if ($PROTOGENI_LOCALUSER && !$user->IsLocal()) {
# Already exists as a geni user, need to make a real user.
return GeniUtil::CreateLocalUser($urn, $email);
}
# #
# See if the certificate changed. If it did, we have to update the # See if the certificate changed. If it did, we have to update the
# user record, but we want to use the old uuid since that is our