Project

General

Profile

« Previous | Next » 

Revision 9530

Added by Chris Jones over 8 years ago

Add a setAuthToken() method. when the HTTP_AUTHORIZATION environment variable is set, set the value as the `auth_token_header` instance variable in the Metacat instance passed in. This requires that the Apache installation includes an HTTP rewrite rule to pass the header on to an CGI processing the request. Call this method whenever we instantiate a Metacat object.

Also, add two methods that will be used for parsing the JWT authentication token: getSigningCertificate() and getTokenInfo(). The former tries to download the appropriate certificate used to sign the token coming in from the request, based on the Metacat D1Client.CN_URL property. With the appropriate PEM-encoded certificate converted to a DER-encode certificate, we use Crypt::JWT->decode_jwt() to decode the token. This is a finicky method in that it fails when the token has expired, rather than just decoding it and returning the token claims, expired or not. So, in getTokenInfo(), on failure, we just set the isValid field to 0 with everything else being empty. I know, kludgy.

refs https://github.nceas.ucsb.edu/KNB/arctic-data/issues/42

View differences:

src/perl/register-dataset.cgi
44 44
use File::Temp;
45 45
use File::Copy;
46 46
use Fcntl qw(:flock);
47
use Crypt::JWT qw(decode_jwt);
48
use Crypt::X509;
49
use IO::Socket::SSL;
47 50
use strict;
48 51

  
49 52
#debug("running register-dataset.cgi");
......
66 69
$properties->load(*METACAT_PROPERTIES);
67 70

  
68 71
# local directory configuration
69
my $skinsDir     = "${workingDirectory}/../style/skins";
70
my $templatesDir = abs_path("${workingDirectory}/../style/common/templates");
71
my $tempDir      = $properties->getProperty('application.tempDir');
72
my $dataDir      = $properties->getProperty('application.datafilepath');
73
my $metacatDir   = "/var/metacat";
72
my $skinsDir      = "${workingDirectory}/../style/skins";
73
my $templatesDir  = abs_path("${workingDirectory}/../style/common/templates");
74
my $tempDir       = $properties->getProperty('application.tempDir');
75
my $dataDir       = $properties->getProperty('application.datafilepath');
76
my $metacatDir    = "/var/metacat";
77
my $cnUrl         = $properties->getProperty('D1Client.CN_URL');
78
my $cn            = "server";
79
my $pem_file_path = "/tmp/server.pem";
80
my $der_file_path = "/tmp/server.der";
74 81

  
82
# Signing certificate file name is based on the CN environment
83
if ( $cnUrl && $tempDir ) { 
84
    my @parts = split(/\//, $cnUrl);
85
    $cn = $parts[2];
86
    $pem_file_path = $tempDir . "/" . $cn . ".pem";
87
    $der_file_path = $tempDir . "/" . $cn . ".der"; 
88
           
89
}
90

  
75 91
# url configuration
76 92
my $server = $properties->splitToTree( qr/\./, 'server' );
77 93
my $protocol = 'http://';
......
455 471

  
456 472
# Create a metacat object
457 473
my $metacat = Metacat->new($metacatUrl);
474
setAuthToken($metacat);
458 475

  
459 476
if ( !$error ) {
460 477

  
......
1460 1477
	my $input = shift;
1461 1478
	my ( $docid, $fileHash ) = datafileInfo($input);
1462 1479
	my $metacat = Metacat->new($metacatUrl);
1480
    setAuthToken($metacat);
1463 1481

  
1464 1482
	my ( $username, $password ) = getCredentials();
1465 1483
	my $response = $metacat->login( $username, $password );
......
2438 2456

  
2439 2457
	# create metacat instance
2440 2458
	my $metacat = Metacat->new($metacatUrl);
2441
	my $httpMessage;
2459
    setAuthToken($metacat);
2460
    my $httpMessage;
2442 2461
	my $doc;
2443 2462
	my $xmldoc;
2444 2463
	my $findType;
......
3496 3515

  
3497 3516
	# create metacat instance
3498 3517
	my $metacat = Metacat->new($metacatUrl);
3518
    setAuthToken($metacat);
3499 3519
	my $docid   = $FORM::docid;
3500 3520

  
3501 3521
	# Login to metacat
......
3624 3644
		my $password = $FORM::password;
3625 3645

  
3626 3646
		my $metacat = Metacat->new($metacatUrl);
3647
        setAuthToken($metacat);
3627 3648
		my $returnVal = $metacat->login( $username, $password );
3628 3649
		debug(
3629 3650
"Login was $returnVal for login attempt to $metacatUrl, with $username"
......
3758 3779
	
3759 3780
	#debug("getting user info for session id: $sessionId");
3760 3781
	my $metacat = Metacat->new($metacatUrl);
3761
	
3782
	setAuthToken($metacat);
3783
    
3762 3784
	my ( $username, $password ) = getCredentials();
3763 3785
	$metacat->login( $username, $password );
3764 3786
	
......
3872 3894

  
3873 3895
sub getReviewHistoryHTML {
3874 3896
	my $metacat = Metacat->new($metacatUrl);
3897
	setAuthToken($metacat);
3875 3898
	my ( $username, $password ) = getCredentials();
3876 3899
	$metacat->login( $username, $password );
3877 3900
	my $parser = XML::LibXML->new();
......
3917 3940

  
3918 3941
	my $errorMessage = '';
3919 3942
	my $metacat      = Metacat->new($metacatUrl);
3943
	setAuthToken($metacat);
3920 3944

  
3921 3945
	print "Content-type: text/html\n\n";
3922 3946

  
......
4092 4116
	my $errorMessage = '';
4093 4117
	my $userDN       = '';
4094 4118
	my $metacat      = Metacat->new($metacatUrl);
4119
	setAuthToken($metacat);
4095 4120
	my $docid        = $FORM::docid;
4096 4121

  
4097 4122
	print "Content-type: text/html\n\n";
......
4229 4254
sub handleModRevise() {
4230 4255
	my $errorMessage = '';
4231 4256
	my $metacat      = Metacat->new($metacatUrl);
4257
	setAuthToken($metacat);
4232 4258
	my $docid        = $FORM::docid;
4233 4259

  
4234 4260
	print "Content-type: text/html\n\n";
......
5441 5467
	$projects[6] = \@row7;
5442 5468
	return \@projects;
5443 5469
}
5470

  
5471
################################################################################
5472
#
5473
# Set the incoming HTTP Authorization header as an instance variable in the 
5474
# given Metacat object
5475
#
5476
################################################################################
5477
sub setAuthToken() {
5478
    my $metacat = shift;
5479
    
5480
    eval { $metacat->isa('Metacat'); };
5481
    
5482
    if ( ! $@ ) {
5483
        # Set the auth_token_header if available
5484
        if ( $ENV{'HTTP_AUTHORIZATION'}) {
5485
            $metacat->set_options( 
5486
                auth_token_header => $ENV{'HTTP_AUTHORIZATION'});
5487
        }
5488
        
5489
    } else {
5490
        debug('Not an instance of Metacat.' .
5491
        'Pass a Metacat object only to setAuthToken()');
5492
    }
5493
}
5494

  
5495
################################################################################
5496
#
5497
# Downloads the server certificate used to sign tokens, and converts it from PEM
5498
# format to DER format. Writes both to the Metacat temp directory if configured.
5499
#
5500
################################################################################
5501
sub getSigningCertificate() {
5502
        
5503
    open(my $pem_cert_file, ">", $pem_file_path)
5504
        or die "\nCould not open PEM certificate file: $!\n";
5505

  
5506
    # Attempts to use IO::Socket::SSL->peer_certificate()
5507
    # and Net::SSLeay->get_peer_certificate() 
5508
    # return an unparseable cert (it seems).  
5509
    # Settle for the openssl command instead.
5510
    # my $client = IO::Socket::SSL->new('cn-stage.test.dataone.org:443')
5511
    #    or die "error=$!, ssl_error=$SSL_ERROR";
5512
    #$signing_cert = $client->peer_certificate();
5513
    #print $signing_cert;
5514
    #$client->close();
5515

  
5516
    my @lines = `openssl s_client -connect $cn:443 -showcerts 2>/dev/null`;
5517

  
5518
    my $count = 0;
5519
    my ($start_line_number, $end_line_number);
5520

  
5521
    # Find the start line of the first cert
5522
    for my $line (@lines) {
5523
        $count = $count + 1;
5524
        if ( $line =~ /BEGIN/) {
5525
            $start_line_number = $count;
5526
            last;
5527
        
5528
        }    
5529
    }
5530

  
5531
    # Find the end line of the first cert
5532
    $count = 0;
5533
    for my $line (@lines) {
5534
        $count = $count + 1;
5535
        if ( $line =~ /END/) {
5536
            $end_line_number = $count;
5537
            last;
5538
        
5539
        }    
5540
    }
5541

  
5542
    # print the cert to a PEM file
5543
    $count = 0;
5544
    for my $line (@lines) {
5545
        $count = $count + 1;
5546
        if ( $count >= $start_line_number && $count <= $end_line_number) {
5547
            print $pem_cert_file $line;
5548
        
5549
        }    
5550
    }
5551

  
5552
    close($pem_cert_file);
5553

  
5554
    # Convert the PEM to DER
5555
    my @convert_der_args = ("openssl", "x509", 
5556
        "-in", $pem_file_path, "-inform", "PEM",
5557
        "-out", $der_file_path, "-outform", "DER");
5558
    system(@convert_der_args);
5559
    
5560
}
5561

  
5562
################################################################################
5563
#
5564
# Returns details about the parsed token
5565
#
5566
################################################################################
5567
sub getTokenInfo() {
5568
    # Initialize the token info hash
5569
    my $token_info = {
5570
            userId      => '',
5571
            issuedAt    => '',
5572
            iat         => '',
5573
            sub         => '',
5574
            consumerKey => '',
5575
            exp         => '',
5576
            ttl         => '',
5577
            fullName    => '',
5578
            isValid     => 0
5579
    };
5580

  
5581
    my $der_cert_file;
5582
    my $signing_cert;
5583
    
5584
    # If we don't already have the CN signing cert, get it
5585
    if ( ! -e $der_file_path )   {
5586
        getSigningCertificate();
5587
    
5588
    }
5589
    
5590
    # Read the DER-encoded certificate
5591
    open($der_cert_file, "<", $cn . ".der")
5592
        or die "\nCould not open DER certificate file: $!\n";
5593
    binmode($der_cert_file);
5594
    read($der_cert_file, $signing_cert, 4096)
5595
        or die "Problem reading the DER-encoded server cert: $!\n" if $!;
5596
    close($der_cert_file);
5597
    
5598
    my $cert = Crypt::X509->new(cert=>$signing_cert);
5599
    
5600
    # Decode the token using Crypt::JWT 
5601
    eval{ $token_info = decode_jwt(token=>$token, key=>$cert); };
5602
    if ( ! $@ ) { 
5603
        $$token_info{isValid} = 1;
5604
    }
5605
    
5606
    return $token_info;
5607
         
5608
}
5609

  

Also available in: Unified diff