All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

Commit 46e16fab authored by Leigh B Stoller's avatar Leigh B Stoller

Continue switch to URNs, further deprecating UUIDs. Add urn argument

to all of the API functions, and prefer that to any UUID argument.
There are a lot of little changes.

At this point, the CH and SA will no longer accept certificates that
do not have URNs in them. The CH will send email to the email address
listed in the certificate.
parent ee615600
......@@ -26,6 +26,7 @@ use emutil qw(TBGetUniqueIndex);
use English;
use overload ('""' => 'Stringify');
use XML::Simple;
use Date::Parse;
# Configure variables
my $TB = "@prefix@";
......@@ -63,11 +64,6 @@ sub Lookup($$)
elsif ($token =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/) {
$uuid = $token;
}
elsif ($token =~ /^P([\w]+)$/) {
# Only SAs are looked up this way.
# This will be flushed after URNs are fully pushed out.
return GeniAuthority->LookupByPrefix($1);
}
elsif ($token =~ /^[\w\.]*$/) {
$query_result =
DBQueryWarn("select uuid from geni_authorities ".
......@@ -180,6 +176,22 @@ sub version($) { return field($_[0], "version"); }
sub cert($) { return $_[0]->{'CERT'}->cert(); }
sub GetCertificate($) { return $_[0]->{'CERT'}; }
#
# Expired?
#
sub IsExpired($)
{
my ($self) = @_;
my $expires = $self->expires();
return 1
if (!defined($expires) || $expires eq "");
my $when = strptime($expires);
return ($when < time());
}
#
# Delete from the DB.
#
......@@ -205,36 +217,39 @@ sub Delete($)
}
#
# Check to see if there is an existing authority with the same prefix.
# Check to see if there is an existing authority with the same urn.
#
sub CheckExisting($$$$)
sub CheckExisting($$)
{
my ($class, $type, $uuid, $hrn) = @_;
my ($class, $certificate) = @_;
my $urn = $certificate->urn();
my ($prefix) = ($uuid =~ /^\w+\-\w+\-\w+\-\w+\-(\w+)$/);
$type = lc($type);
my (undef, undef, $type) = GeniHRN::Parse($urn);
return -1
if (!defined($type));
my $safe_urn = DBQuoteSpecial($urn);
my $query_result =
DBQueryWarn("select uuid,type from geni_authorities ".
"where uuid_prefix='$prefix'");
DBQueryWarn("select urn,type from geni_authorities ".
"where urn=$safe_urn");
return -1
if (!$query_result);
return 0
if (!$query_result->numrows);
while (my ($DBuuid,$DBtype) = $query_result->fetchrow_array()) {
while (my ($DBurn,$DBtype) = $query_result->fetchrow_array()) {
# Look for an exact match, which means its just a replacement.
next
if ($uuid eq $DBuuid && $type eq $DBtype);
if ($urn eq $DBurn && $type eq $DBtype);
# Same uuid, different type.
# Same urn, different type.
return 1
if ($uuid eq $DBuuid && $type ne $DBtype);
if ($urn eq $DBurn && $type ne $DBtype);
# Different uuid, same type.
# Different urn, same type.
return 1
if ($uuid ne $DBuuid && $type eq $DBtype);
if ($urn ne $DBurn && $type eq $DBtype);
}
return 0;
}
......@@ -312,56 +327,20 @@ sub Version($)
}
#
# Does the uuid prefix match.
# Check that the authority is the issuer of the given certificate.
# This check is not quite complete yet.
#
sub PrefixMatch($$)
sub CheckValidIssuer($$)
{
my ($self, $uuid) = @_;
my ($self, $certificate) = @_;
my ($hisauthority, undef, undef) = GeniHRN::Parse($self->urn());
my ($herauthority, undef, undef) = GeniHRN::Parse($certificate->urn());
return 0
if (!ref($self));
my $uuid_prefix = $self->uuid_prefix();
if ($uuid =~ /^\w+\-\w+\-\w+\-\w+\-(\w+)$/) {
return 1
if ("$uuid_prefix" eq "$1");
}
return 0;
}
#
# Find an authority by looking for the prefix. This will eventually go
# away when we fully switch to URNs
#
# Note tha only SAs are looked up this way.
#
sub LookupByPrefix($$)
{
my ($class, $uuid) = @_;
my $prefix;
if ($uuid =~ /^\w+\-\w+\-\w+\-\w+\-(\w+)$/) {
$prefix = $1;
}
elsif ($uuid =~ /^(\w+)$/) {
$prefix = $1;
}
else {
print STDERR "Could no parse uuid for prefix\n";
return undef;
}
my $query_result =
DBQueryWarn("select uuid from geni_authorities ".
"where uuid_prefix='$prefix' and type='sa'");
return undef
if (! $query_result || !$query_result->numrows);
($uuid) = $query_result->fetchrow_array();
if (! (defined($hisauthority) && defined($herauthority) &&
$hisauthority eq $herauthority));
return GeniAuthority->Lookup($uuid);
return 1;
}
#
......
......@@ -60,7 +60,6 @@ sub GetVersion()
sub GetCredential($)
{
my ($argref) = @_;
my $uuid = $argref->{'uuid'};
my $cred = $argref->{'credential'};
my $type = $argref->{'type'};
my $gid = $argref->{'gid'} || $argref->{'cert'};
......@@ -69,8 +68,7 @@ sub GetCredential($)
# The caller has to be known to us, but how are they known to us?
# Probably need a web interface?
#
my $caller_uuid = $ENV{'GENIUUID'};
my $caller_authority = GeniAuthority->Lookup($ENV{'GENIUUID'});
my $caller_authority = GeniAuthority->Lookup($ENV{'GENIURN'});
if (!defined($caller_authority)) {
if (!defined($gid)) {
return GeniResponse->Create(GENIRESPONSE_REFUSED,
......@@ -90,8 +88,6 @@ sub GetCredential($)
"Could not parse certificate")
if (!defined($certificate));
print STDERR "GetCredential(): " . $certificate->DN() . "\n";
if (! ($certificate->uuid() =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/)) {
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Improper format for uuid");
......@@ -104,30 +100,38 @@ sub GetCredential($)
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not find URL in the certificate")
if (!defined($url));
if ($certificate->hrn() =~ /^unknown/i) {
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Please define PROTOGENI_DOMAIN");
}
}
my $urn = $certificate->urn();
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not find URN in the certificate")
if (!defined($urn));
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Malformed URN in the certificate")
if (!GeniHRN::IsValid($urn));
my ($auth, $type, $id) = GeniHRN::Parse($urn);
#
# Check for an existing authority.
#
if (GeniAuthority->CheckExisting("sa", $certificate->uuid(),
$certificate->hrn()) != 0) {
if (GeniAuthority->CheckExisting($certificate) != 0) {
print STDERR "Attempt to register existing slice authority\n";
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Slice Authority already exists");
"Authority already exists");
}
SENDMAIL($TBOPS, "New ProtoGeni Authority",
$certificate->asText());
$caller_authority = GeniAuthority->Create($certificate, $url, "sa");
$caller_authority = GeniAuthority->Create($certificate, $url, $id);
if (!defined($caller_authority)) {
print STDERR "Could not create new geni authority\n";
print STDERR "Could not create new authority\n";
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create new GeniAuthority");
"Could not create new authority");
}
}
......@@ -139,17 +143,19 @@ sub GetCredential($)
#
# This credential is for access to this authority.
#
my $authority = GeniAuthority->Lookup($ENV{'MYUUID'});
my $authority = GeniAuthority->Lookup($ENV{'MYURN'});
if (!defined($authority)) {
print STDERR "Could not find local authority object\n";
return GeniResponse->Create(GENIRESPONSE_ERROR);
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Who are you?");
}
my $credential =
GeniCredential->CreateSigned($authority,
$caller_authority,
$GeniCredential::LOCALMA_FLAG);
return GeniResponse->Create(GENIRESPONSE_ERROR)
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create signed credential")
if (!defined($credential));
return GeniResponse->Create(GENIRESPONSE_SUCCESS,
......@@ -173,48 +179,31 @@ sub Resolve($)
my $cred = $argref->{'credential'};
my $uuid = $argref->{'uuid'};
my $hrn = $argref->{'hrn'};
my $urn = $argref->{'urn'};
my $type = $argref->{'type'};
my $urn = undef;
if (! (defined($uuid) || defined($hrn))) {
return GeniResponse->MalformedArgsResponse();
}
if (defined($uuid) && defined($hrn)) {
if (! (defined($uuid) || defined($hrn) || defined($urn))) {
return GeniResponse->MalformedArgsResponse();
}
$urn = $uuid if (defined($uuid) && GeniHRN::IsValid( $uuid ));
$urn = $hrn if (defined($hrn) && GeniHRN::IsValid( $hrn ));
# URN always takes precedence and all items at the clearinghouse
# now have URNs in their certificates.
if (defined($urn)) {
my ($auth,$t,$id) = GeniHRN::Parse( $urn );
my $manager_urn = GeniHRN::Generate($auth, "authority", "cm");
my $authority = GeniAuthority->Lookup($manager_urn);
# print STDERR "$authority, $urn\n";
return GeniResponse->Create(GENIRESPONSE_DBERROR)
if (!defined($authority));
my ($translated) = $authority->hrn() =~ /^([-\w]+)\..*/;
# print STDERR "$auth, $t, $id, $translated\n";
if ($t eq "node") {
$type = "Component";
}
elsif ($t eq "authority") {
$type = $id;
}
else {
$type = $t;
}
$hrn = $translated . "." . $id;
# print STDERR "$auth, $t, $id, $hrn, $type\n";
$uuid = undef;
return GeniResponse->MalformedArgsResponse()
if (!GeniHRN::IsValid($urn));
$hrn = $uuid = undef;
}
elsif (defined($uuid) && GeniHRN::IsValid($uuid)) {
$urn = $uuid;
$uuid = $hrn = undef;
}
elsif (defined($hrn) && GeniHRN::IsValid($hrn)) {
$urn = $hrn;
$hrn = $uuid = undef;
}
if (defined($uuid) && !($uuid =~ /^[-\w]*$/)) {
elsif (defined($uuid) && !($uuid =~ /^[-\w]*$/)) {
return GeniResponse->MalformedArgsResponse();
}
if (defined($hrn) && !($hrn =~ /^[-\w\.]*$/)) {
elsif (defined($hrn) && !($hrn =~ /^[-\w\.]*$/)) {
return GeniResponse->MalformedArgsResponse();
}
if (! (defined($type) && ($type =~ /^(SA|CM|MA|Component|Slice|User)$/i))){
......@@ -224,33 +213,17 @@ sub Resolve($)
if (! defined($cred)) {
return GeniResponse->MalformedArgsResponse();
}
my $credential = GeniCredential->CreateFromSigned($cred);
if (!defined($credential)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniCredential object");
}
my $lookup_token = ($uuid || $hrn);
my $lookup_token = ($urn || $uuid || $hrn);
#
# Make sure the credential was issued to the caller.
#
if ($credential->owner_uuid() ne $ENV{'GENIUUID'}) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"This is not your credential!");
}
#
# And that the target of the credential is this registry.
#
my $authority = GeniAuthority->Lookup($ENV{'MYUUID'});
my $authority = GeniAuthority->Lookup($ENV{'MYURN'});
if (!defined($authority)) {
print STDERR "Could not find local authority object\n";
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
if ($credential->target_uuid() ne $authority->uuid()) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN,
undef, "This is not your registry!");
}
my $credential = CheckCredential($cred, $authority);
return $credential
if (GeniResponse::IsResponse($credential));
$credential->HasPrivilege( "authority" ) or
$credential->HasPrivilege( "resolve" ) or
return GeniResponse->Create( GENIRESPONSE_FORBIDDEN, undef,
......@@ -266,11 +239,11 @@ sub Resolve($)
# Return a blob.
my $blob = { "uid" => $user->uid(),
"hrn" => $user->hrn(),
"urn" => $user->urn() || '',
"uuid" => $user->uuid(),
"email" => $user->email(),
"gid" => $user->cert(),
"name" => $user->name(),
"sa_uuid" => $user->sa_uuid(),
};
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
}
......@@ -286,6 +259,7 @@ sub Resolve($)
# Return a blob.
my $blob = { "gid" => $component->cert(),
"url" => $component->url(),
"urn" => $component->urn(),
"manager_gid" => $manager->cert(),
};
......@@ -300,6 +274,7 @@ sub Resolve($)
# Return a blob.
my $blob = { "gid" => $authority->cert(),
"url" => $authority->url(),
"urn" => $authority->urn(),
"type" => $authority->type(),
};
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
......@@ -313,6 +288,7 @@ sub Resolve($)
# Return a blob.
my $blob = { "gid" => $manager->cert(),
"url" => $manager->url(),
"urn" => $manager->urn(),
"type" => $manager->type(),
};
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
......@@ -324,6 +300,7 @@ sub Resolve($)
# Return a blob.
my $blob = { "gid" => $authority->cert(),
"url" => $authority->url(),
"urn" => $authority->urn(),
"type" => $authority->type(),
};
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
......@@ -337,10 +314,11 @@ sub Resolve($)
# Return a blob.
my $blob = { "hrn" => $slice->hrn(),
"urn" => $slice->urn() || '',
"uuid" => $slice->uuid(),
"creator_uuid" => $slice->creator_uuid(),
"creator_urn" => $slice->creator_urn() || '',
"gid" => $slice->cert(),
"sa_uuid" => $slice->sa_uuid(),
};
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
}
......@@ -358,7 +336,8 @@ sub Register($)
my $info = $argref->{'info'};
my $type = $argref->{'type'};
if (! (defined($type) && ($type =~ /^(SA|MA|CM|SES|Component|Slice|User)$/i))){
if (! (defined($type) &&
($type =~ /^(SA|MA|CM|SES|Component|Slice|User)$/i))){
return GeniResponse->MalformedArgsResponse();
}
$type = lc($type);
......@@ -375,32 +354,16 @@ sub Register($)
if (! defined($info)) {
return GeniResponse->MalformedArgsResponse();
}
my $credential = GeniCredential->CreateFromSigned($cred);
if (!defined($credential)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniCredential object");
}
#
# Make sure the credential was issued to the caller.
#
if ($credential->owner_uuid() ne $ENV{'GENIUUID'}) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"This is not your credential!");
}
#
# And that the target of the credential is this registry.
#
my $authority = GeniAuthority->Lookup($ENV{'MYUUID'});
my $authority = GeniAuthority->Lookup($ENV{'MYURN'});
if (!defined($authority)) {
print STDERR "Could not find local authority object\n";
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
if ($credential->target_uuid() ne $authority->uuid()) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN,
undef, "This is not your registry!");
}
my $credential = CheckCredential($cred, $authority);
return $credential
if (GeniResponse::IsResponse($credential));
$credential->HasPrivilege( "authority" ) or
$credential->HasPrivilege( "refresh" ) or
return GeniResponse->Create( GENIRESPONSE_FORBIDDEN, undef,
......@@ -422,6 +385,28 @@ sub Register($)
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Improper format for hrn");
}
if (! (defined($certificate->urn()) &&
GeniHRN::IsValid($certificate->urn()))) {
my $email = ($type eq "user" ?
$info->{'email'} : $certificate->email());
#
# This will go away when all users updated.
#
if (defined($email) &&
TBcheck_dbslot($email, "users", "usr_email",
TBDB_CHECKDBSLOT_ERROR)) {
print STDERR "Sending mail to $email about missing URN\n";
SENDMAIL($email, "ProtoGENI Registration Error",
"Your user certificate is out of date! \n".
"Please login to your home Emulab and generate\n".
"a new encrypted SSL certificate.\n",
$TBOPS, "BCC: protogeni-errors\@flux.utah.edu");
}
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Improper or missing URN in certificate; Please regenerate");
}
if ($type eq "user") {
my $name = $info->{'name'};
......@@ -439,28 +424,24 @@ sub Register($)
"email: ". TBFieldErrorString());
}
#
# Need to verify the UUID is permitted for the SA making the request.
# Need to verify the caller is authorized.
#
my $slice_authority = GeniAuthority->Lookup($ENV{'GENIUUID'});
my $slice_authority = GeniAuthority->Lookup($ENV{'GENIURN'});
if (!defined($slice_authority)) {
print STDERR "Could not find authority object for caller.\n";
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
if (! $slice_authority->PrefixMatch($certificate->uuid())) {
if (! $slice_authority->CheckValidIssuer($certificate)) {
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"uuid: Prefix mismatch");
"Certificate issuer is not valid ");
}
my $existing = GeniUser->Lookup($certificate->uuid());
my $existing = GeniUser->Lookup($certificate->urn());
if (defined($existing)) {
if (! ($existing->hrn() eq $certificate->hrn())) {
if ($existing->hrn() ne $certificate->hrn()) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Not allowed to change hrn");
}
if (! ($existing->sa_uuid() eq $slice_authority->uuid())) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Already registered with another SA");
}
#
# Update operation, but only name, email, and keys for now.
#
......@@ -471,16 +452,24 @@ sub Register($)
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
}
#
# XXX
# Temporary: Look for existing user with same uuid but no urn.
# We want to store the new certificate since it has a urn.
#
# What kind of uniquess requirements do we need? No one else with this
# email address? Of course, we have to allow hrn reuse, but should we
# require that for a given SA, that hrn is unique, at least to avoid
# lots of confusion?
$existing = GeniUser->Lookup($certificate->uuid());
if ($existing) {
if ($certificate->Store()) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"could not update certificate");
}
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
}
#
if (GeniUser->CheckExisting($certificate->hrn(), $email)) {
# A conflict is another user with the same hrn or email.
#
if (GeniUser->CheckConflict($certificate)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"hrn/email already registered");
"user already registered");
}
my $newuser = GeniUser->Create($certificate, $slice_authority,
$info, $keys);
......@@ -492,62 +481,65 @@ sub Register($)
"User has been registered");
}
if ($type eq "slice") {
my $creator_uuid = $info->{'creator_uuid'};
if (! ($creator_uuid =~ /^[-\w]*$/)) {
my $creator_token = $info->{'creator_uuid'} || $info->{'creator_urn'};
if (!defined($creator_token)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"creator_uuid: Invalid characters");
"Who is the creator of this slice?");
}
#
# Make sure the geni user exists.
#
my $user = GeniUser->Lookup($creator_uuid);
my $user = GeniUser->Lookup($creator_token);
if (!defined($user)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"creator_uuid: No such User");
"No such user: $creator_token");
}
#
# Need to verify the UUID is permitted for the SA making the request.
# Need to verify the caller is allowed to register the run.
#
my $slice_authority = GeniAuthority->Lookup($ENV{'GENIUUID'});
my $slice_authority = GeniAuthority->Lookup($ENV{'GENIURN'});
if (!defined($slice_authority)) {
print STDERR "Could not find authority object for caller.\n";
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
if (! $slice_authority->PrefixMatch($certificate->uuid())) {
if (! $slice_authority->CheckValidIssuer($certificate)) {
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"uuid: Prefix mismatch");
"Certificate issuer is not valid ");
}
#
# Reregistration of existing slice is okay.
#
my $existing = GeniRegistry::GeniSlice->Lookup($certificate->uuid());
my $existing = GeniRegistry::GeniSlice->Lookup($certificate->urn());
if (defined($existing)) {
if (! ($existing->cert() eq $certificate->cert())) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Not allowed to change cert");
}
if (! ($existing->sa_uuid() eq $slice_authority->uuid())) {
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
}
#
# Temporary: Look for existing slice with same uuid but no urn.
# We want to store the new certificate since it has a urn.
#
$existing = GeniSlice->Lookup($certificate->uuid());
if ($existing) {
if ($certificate->Store()) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Already registered with another SA");
"could not update certificate");
}
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
}
#
# Make sure slice hrn and uuid are unique.