GeniAuthority.pm.in 9.95 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
2
#!/usr/bin/perl -wT
#
3
# GENIPUBLIC-COPYRIGHT
4
# Copyright (c) 2008-2010 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# All rights reserved.
#
package GeniAuthority;

#
# Some simple ticket stuff.
#
use strict;
use Exporter;
use vars qw(@ISA @EXPORT);

@ISA    = "Exporter";
@EXPORT = qw ( );

# Must come after package declaration!
use GeniDB;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
21
use GeniRegistry;
22
use GeniHRN;
23
24
use Genixmlrpc;
use GeniResponse;
25
use emutil qw(TBGetUniqueIndex);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
26
27
28
use English;
use overload ('""' => 'Stringify');
use XML::Simple;
29
use Date::Parse;
30
use Data::Dumper;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
31
32
33
34
35
36
37
38
39
40
41
42
43

# Configure variables
my $TB		   = "@prefix@";
my $TBOPS          = "@TBOPSEMAIL@";
my $TBAPPROVAL     = "@TBAPPROVALEMAIL@";
my $TBAUDIT   	   = "@TBAUDITEMAIL@";
my $BOSSNODE       = "@BOSSNODE@";
my $OURDOMAIN      = "@OURDOMAIN@";
my $SIGNCRED	   = "$TB/sbin/signgenicred";
my $VERIFYCRED	   = "$TB/sbin/verifygenicred";

# Cache of instances to avoid regenerating them.
my %authorities    = ();
44
BEGIN { use GeniUtil; GeniUtil::AddCache(\%authorities); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
45
46

#
47
# Lookup by URN (and also UUID, for compatibility).
Leigh B. Stoller's avatar
Leigh B. Stoller committed
48
49
50
51
52
#
sub Lookup($$)
{
    my ($class, $token) = @_;
    my $query_result;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
53
    my $uuid;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
54

55
56
57
    if (GeniHRN::IsValid($token)) {
	$query_result =
	    DBQueryWarn("select uuid from geni_authorities ".
58
			"where urn='$token'");
59
60
61
62
63

	return undef
	    if (! $query_result || !$query_result->numrows);

	($uuid) = $query_result->fetchrow_array();
64
65
    }
    elsif ($token =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
66
	$uuid = $token;
67
    }
68
69
70
71
72
    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);
    }
73
74
    elsif ($token =~ /^[\w\.]*$/) {
	$query_result =
Leigh B. Stoller's avatar
Leigh B. Stoller committed
75
	    DBQueryWarn("select uuid from geni_authorities ".
76
			"where hrn='$token'");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
77
78
	return undef
	    if (! $query_result || !$query_result->numrows);
79

Leigh B. Stoller's avatar
Leigh B. Stoller committed
80
	($uuid) = $query_result->fetchrow_array();
81
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
82
83
84
85
    else {
	return undef;
    }
    # Look in cache first
Leigh B. Stoller's avatar
Leigh B. Stoller committed
86
87
    return $authorities{"$uuid"}
        if (exists($authorities{"$uuid"}));
Leigh B. Stoller's avatar
Leigh B. Stoller committed
88
89

    $query_result =
Leigh B. Stoller's avatar
Leigh B. Stoller committed
90
	DBQueryWarn("select * from geni_authorities where uuid='$uuid'");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
91
92
93
94
95
96
    
    return undef
	if (!$query_result || !$query_result->numrows);

    my $self              = {};
    $self->{'AUTHORITY'}  = $query_result->fetchrow_hashref();
97
    $self->{'version'}    = undef;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
98
99
100
101
102
    bless($self, $class);

    #
    # Grab the certificate, since we will probably want it.
    #
103
104
    my $certificate = GeniCertificate->Lookup($uuid);
    if (!defined($certificate)) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
105
	print STDERR "Could not find certificate for authority $uuid\n";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
106
107
	return undef;
    }
108
    $self->{'CERT'} = $certificate;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
109
110
    
    # Add to cache. 
Leigh B. Stoller's avatar
Leigh B. Stoller committed
111
    $authorities{$self->{'AUTHORITY'}->{'uuid'}} = $self;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
112
113
114
115
116
117
118
119
120
121
122
    
    return $self;
}

#
# Stringify for output.
#
sub Stringify($)
{
    my ($self) = @_;
    
Leigh B. Stoller's avatar
Leigh B. Stoller committed
123
    my $hrn = $self->hrn();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
124

Leigh B. Stoller's avatar
Leigh B. Stoller committed
125
    return "[GeniAuthority: $hrn]";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
126
127
128
129
130
}

#
# Create a Geni authority in the DB.
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
131
sub Create($$$$)
Leigh B. Stoller's avatar
Leigh B. Stoller committed
132
{
133
    my ($class, $certificate, $url, $type) = @_;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
134
135

    my @insert_data = ();
136
    my ($prefix) = ($certificate->uuid() =~ /^\w+\-\w+\-\w+\-\w+\-(\w+)$/);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
137

138
    my $safe_hrn    = DBQuoteSpecial($certificate->hrn());
139
    my $safe_urn    = DBQuoteSpecial($certificate->URN());
Leigh B. Stoller's avatar
Leigh B. Stoller committed
140
    my $safe_url    = DBQuoteSpecial($url);
141
    my $safe_uuid   = DBQuoteSpecial($certificate->uuid());
Leigh B. Stoller's avatar
Leigh B. Stoller committed
142
    my $safe_prefix = DBQuoteSpecial($prefix);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
143
    my $safe_type   = DBQuoteSpecial(lc($type));
Leigh B. Stoller's avatar
Leigh B. Stoller committed
144
145
146
147
    
    # Now tack on other stuff we need.
    push(@insert_data, "created=now()");
    push(@insert_data, "hrn=$safe_hrn");
148
    push(@insert_data, "urn=$safe_urn");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
149
150
    push(@insert_data, "url=$safe_url");
    push(@insert_data, "uuid=$safe_uuid");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
151
    push(@insert_data, "uuid_prefix=$safe_prefix");
152
    push(@insert_data, "type=$safe_type");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
153

154
    if ($certificate->Store() != 0) {
155
	print STDERR "Could not store certificate for new authority.\n";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
156
157
158
	return undef;
    }

159
160
    # Insert into DB. Use "replace" here since we reload the auth info
    # periodically, and do not want to cause a race by deleting it. 
161
162
163
164
    return undef
	if (!DBQueryWarn("replace into geni_authorities set " .
			 join(",", @insert_data)));

165
166
167
    # Delete from cache, since we use replace above. 
    delete($authorities{$certificate->uuid()});

168
169
    return GeniAuthority->Lookup( defined( $certificate->urn() ) ?
	$certificate->urn() : $certificate->uuid() );
Leigh B. Stoller's avatar
Leigh B. Stoller committed
170
171
172
173
}
# accessors
sub field($$) { return ((! ref($_[0])) ? -1 : $_[0]->{'AUTHORITY'}->{$_[1]}); }
sub uuid($)		{ return field($_[0], "uuid"); }
174
sub expires($)		{ return field($_[0], "expires"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
175
sub uuid_prefix($)	{ return field($_[0], "uuid_prefix"); }
176
sub urn($)		{ return field($_[0], "urn"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
177
178
sub url($)		{ return field($_[0], "url"); }
sub hrn($)		{ return field($_[0], "hrn"); }
179
sub type($)		{ return field($_[0], "type"); }
180
sub disabled($)		{ return field($_[0], "disabled"); }
181
sub version($)		{ return field($_[0], "version"); }
182
183
sub cert($)		{ return $_[0]->{'CERT'}->cert(); }
sub GetCertificate($)   { return $_[0]->{'CERT'}; }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
184

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#
# Expired?
#
sub IsExpired($)
{
    my ($self)  = @_;
    my $expires = $self->expires();

    return 1
	if (!defined($expires) || $expires eq "");
    
    my $when = strptime($expires);

    return ($when < time());
}

201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#
# Delete from the DB.
#
sub Delete($)
{
    my ($self) = @_;

    return -1
	if (! ref($self));

    if ($self->GetCertificate()->Delete() != 0) {
	print STDERR "Could not delete certificate for $self\n";
	return -1;
    }
    my $uuid = $self->uuid();
    DBQueryWarn("delete from geni_authorities ".
		"where uuid='$uuid'")
	or return -1;

220
    # Delete from cache. 
221
222
223
224
    delete($authorities{$uuid});
    return 0;
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
225
#
226
# Check to see if there is an existing authority with the same urn.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
227
#
228
sub CheckExisting($$)
Leigh B. Stoller's avatar
Leigh B. Stoller committed
229
{
230
231
    my ($class, $certificate) = @_;
    my $urn = $certificate->urn();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
232

233
234
235
236
    my (undef, undef, $type) = GeniHRN::Parse($urn);
    return -1
	if (!defined($type));
    my $safe_urn = DBQuoteSpecial($urn);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
237
238

    my $query_result =
239
240
	DBQueryWarn("select urn,type from geni_authorities ".
		    "where urn=$safe_urn");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
241
242
243
244
245
246

    return -1
	if (!$query_result);
    return 0
	if (!$query_result->numrows);

247
    while (my ($DBurn,$DBtype) = $query_result->fetchrow_array()) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
248
249
	# Look for an exact match, which means its just a replacement.
	next
250
	    if ($urn eq $DBurn && $type eq $DBtype);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
251

252
	# Same urn, different type.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
253
	return 1
254
	    if ($urn eq $DBurn && $type ne $DBtype);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
255

256
	# Different urn, same type.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
257
	return 1
258
	    if ($urn ne $DBurn && $type eq $DBtype);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
259
260
261
262
    }
    return 0;
}

263
264
265
266
267
#
# Create authority from the ClearingHouse, by looking up the info.
#
sub CreateFromRegistry($$$)
{
268
    my ($class, $type, $name) = @_;
269

270
271
272
273
    my $authority = GeniAuthority->Lookup($name);
    return $authority
	if (defined($authority) && $authority->urn());

Leigh B. Stoller's avatar
Leigh B. Stoller committed
274
275
276
277
    my $clearinghouse = GeniRegistry::ClearingHouse->Create();
    return undef
	if (!defined($clearinghouse));

278
279
    my $blob;
    return undef
280
	if ($clearinghouse->Resolve($name, $type, \$blob) != 0);
281
282
283
284
285

    my $certificate = GeniCertificate->LoadFromString($blob->{'gid'});
    return undef
	if (!defined($certificate));

286
287
288
289
290
291
292
293
294
295
296
297
298
299
    #
    # At this point, we do not support non-urn sites. They must re-register.
    #
    my $urn = $certificate->urn();
    if (!defined($urn)) {
	print STDERR "GeniAuthority::CreateFromRegistry: ".
	    "$certificate does not have a urn.\n";
	$certificate->Delete();
	return undef;
    }

    $authority = GeniAuthority->Create($certificate,
				       $blob->{'url'},
				       $blob->{'type'});
300
301
302
303
304
305
    $certificate->Delete()
	if (!defined($authority));

    return $authority;
}

306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
#
# Get Version. Ask the Authority what version it is running. 
#
sub Version($)
{
    my ($self) = @_;

    return $self->version()
	if (defined($self->version()));

    #
    # The caller had to set up the xmlrpc context.
    #
    my $response =
	Genixmlrpc::CallMethod($self->url(), undef, "GetVersion");
    
    if (!defined($response)) {
	print STDERR "*** Internal error getting version for $self\n";
	return undef;
    }
    if ($response->code() != GENIRESPONSE_SUCCESS) {
	print STDERR "Could not get version for $self Error: ";
	print STDERR "  " . $response->output() . "\n";
	return undef;
    }
331
332
333
334
335
336
337
    if (ref($response->value())) {
	$self->{'version'} = $response->value()->{'api'};
    }
    else {
	$self->{'version'} = $response->value();
    }
    return $self->{'version'};
338
339
}

340
#
341
342
# Check that the authority is the issuer of the given certificate.
# This check is not quite complete yet.
343
#
344
sub CheckValidIssuer($$)
345
{
346
    my ($self, $certificate) = @_;
347

348
349
    my ($hisauthority, undef, undef) = GeniHRN::Parse($self->urn());
    my ($herauthority, undef, undef) = GeniHRN::Parse($certificate->urn());
350
    return 0
351
352
	if (! (defined($hisauthority) && defined($herauthority) &&
	       $hisauthority eq $herauthority));
353

354
    return 1;
355
356
}

357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
#
# List all authorities.
#
sub ListAll($$)
{
    my ($class, $pref) = @_;
    my @result = ();
    @$pref = ();

    my $query = "select uuid from geni_authorities";
    my $query_result = DBQueryWarn($query);

    return -1
	if (! $query_result);
    return 0
	if (!$query_result->numrows);

    while (my ($uuid) = $query_result->fetchrow_array()) {
	my $authority = GeniAuthority->Lookup($uuid);
	if (!defined($authority)) {
	    print STDERR "Could not lookup authority $uuid\n";
	    return -1;
	}
	push(@result, $authority);
    }
    @$pref = @result;
    return 0;
}

386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
#
# 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 not 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();

    return GeniAuthority->Lookup($uuid);
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
420
421
422
# _Always_ make sure that this 1 is at the end of the file...
1;