Commit 6487bb76 authored by Leigh Stoller's avatar Leigh Stoller

Checkpoint. I now have a simple xmlrpc server called from the web

server. Apache does the client certificate verification, using CA
certs in /usr/testbed/etc/genicacerts, which will hold the CA certs of
all trusted peers. Currently, it has the cert for my elabinelab
experiment for testing.
parent 5b2a255f
......@@ -11,7 +11,7 @@ SUBDIR = protogeni
include $(OBJDIR)/Makeconf
SUBDIRS = security xmlrpc
SUBDIRS = security xmlrpc lib
all: all-subdirs
......@@ -20,6 +20,7 @@ include $(TESTBED_SRCDIR)/GNUmakerules
install:
@$(MAKE) -C security install
@$(MAKE) -C xmlrpc install
@$(MAKE) -C lib install
control-install:
......
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2008 University of Utah and the Flux Group.
# All rights reserved.
#
SRCDIR = @srcdir@
TESTBED_SRCDIR = @top_srcdir@
OBJDIR = ../..
SUBDIR = protogeni/lib
include $(OBJDIR)/Makeconf
LIB_SCRIPTS = Protogeni.pm GeniDB.pm GeniUser.pm test.pl
#
# Force dependencies on the scripts so that they will be rerun through
# configure if the .in file is changed.
#
all: $(LIB_SCRIPTS)
include $(TESTBED_SRCDIR)/GNUmakerules
install: $(addprefix $(INSTALL_LIBDIR)/, $(LIB_SCRIPTS))
control-install:
clean:
rm -f *.o core $(LIB_SCRIPTS)
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2008 University of Utah and the Flux Group.
# All rights reserved.
#
package GeniDB;
use strict;
use emdbi;
use Exporter;
use vars qw(@ISA @EXPORT);
@ISA = "Exporter";
@EXPORT = qw(DBQuery DBQueryFatal DBQueryWarn DBWarn DBFatal DBErr
DBQuoteSpecial);
# Configure variables
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
# We get this back from emdbi and use it in calls.
my $dbnum;
sub DBQuery($) { return emdbi::DBQueryN($dbnum, $_[0]); }
sub DBQueryFatal($) { return emdbi::DBQueryFatalN($dbnum, $_[0]); }
sub DBQueryWarn($) { return emdbi::DBQueryWarnN($dbnum, $_[0]); }
sub DBQuoteSpecial($) { return emdbi::DBQuoteSpecialN($dbnum, $_[0]); }
sub DBWarn($;$) { emdbi::DBError($_[0], $_[1]); }
sub DBFatal($;$) { emdbi::DBFatal($_[0], $_[1]); }
sub DBErr() { return emdbi::DBErrN($dbnum); }
$emdbi::DBQUERY_DEBUG = 1;
# Default connection.
$dbnum = emdbi::NewTBDBHandle("geni")
if (!defined($dbnum));
# _Always_ make sure that this 1 is at the end of the file...
1;
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2008 University of Utah and the Flux Group.
# All rights reserved.
#
package GeniUser;
use strict;
use Exporter;
use vars qw(@ISA @EXPORT);
@ISA = "Exporter";
@EXPORT = qw ( );
# Must come after package declaration!
use lib '@prefix@/lib';
use GeniDB;
use libtestbed;
# Hate to import all this crap; need a utility library.
use libdb qw(TBGetUniqueIndex);
use English;
use overload ('""' => 'Stringify');
use vars qw();
# Configure variables
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBAPPROVAL = "@TBAPPROVALEMAIL@";
my $TBAUDIT = "@TBAUDITEMAIL@";
my $BOSSNODE = "@BOSSNODE@";
my $CONTROL = "@USERNODE@";
my $OURDOMAIN = "@OURDOMAIN@";
# Cache of instances to avoid regenerating them.
my %users = ();
my $debug = 0;
# Little helper and debug function.
sub mysystem($)
{
my ($command) = @_;
print STDERR "Running '$command'\n"
if ($debug);
return system($command);
}
#
# Lookup by idx.
#
sub Lookup($$)
{
my ($class, $token) = @_;
my $query_result;
# Look in cache first
return $users{"$token"}
if (exists($users{"$token"}));
if ($token =~ /^\d+$/) {
$query_result =
DBQueryWarn("select * from geni_users ".
"where uid_idx='$token' and status='active'");
}
else {
return undef;
}
return undef
if (!$query_result || !$query_result->numrows);
my $self = {};
$self->{'USER'} = $query_result->fetchrow_hashref();
bless($self, $class);
# Add to cache.
$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 uid($) { return field($_[0], "uid"); }
sub uuid($) { return field($_[0], "uuid"); }
sub status($) { return field($_[0], "status"); }
sub created($) { return field($_[0], "created"); }
sub archived($) { return field($_[0], "archived"); }
sub name($) { return field($_[0], "name"); }
sub email($) { return field($_[0], "email"); }
sub sa_idx($) { return field($_[0], "sa_idx"); }
#
# Lookup user given a UUID.
#
sub LookupByUUID($$)
{
my ($class, $uuid) = @_;
my $safe_uuid = DBQuoteSpecial($uuid);
my $query_result =
DBQueryFatal("select uid_idx from geni_users ".
"where uuid=$safe_uuid");
return undef
if (! $query_result || !$query_result->numrows);
my ($uid_idx) = $query_result->fetchrow_array();
return GeniUser->Lookup($uid_idx);
}
#
# Class function to create new Geni user and return object.
#
sub Create($$$)
{
my ($class, $uid, $uuid, $name, $email, $sa_idx) = @_;
my @insert_data = ();
# Every user gets a new unique index.
my $uid_idx = TBGetUniqueIndex('next_uid', 1);
# And a UUID (universally unique identifier).
$uuid = NewUUID();
if (!defined($uuid)) {
print "*** WARNING: Could not generate a UUID!\n";
return undef;
}
push(@insert_data, "uid_uuid='$uuid'");
push(@insert_data, "status='active'");
# Now tack on other stuff we need.
push(@insert_data, "created=now()");
push(@insert_data, "uid_idx='$uid_idx'");
push(@insert_data, "sa_idx=$sa_idx");
my $safe_uid = DBQuoteSpecial($uid);
my $safe_name = DBQuoteSpecial($name);
my $safe_email = DBQuoteSpecial($email);
push(@insert_data, "uid=$safe_uid");
push(@insert_data, "name=$safe_name");
push(@insert_data, "email=$safe_email");
# Insert into DB.
DBQueryWarn("insert into geni_users set " . join(",", @insert_data))
or return undef;
return GeniUser->Lookup($uid_idx);
}
#
# Delete the user, as for registration errors.
#
sub Delete($)
{
my ($self) = @_;
return 0
if (! ref($self));
my $uid_idx = $self->uid_idx();
DBQueryWarn("delete from geni_users where uid_idx='$uid_idx'")
or return -1;
return 0;
}
#
# Archive user.
#
sub Archive($)
{
my ($self) = @_;
return 0
if (! ref($self));
my $uid_idx = $self->uid_idx();
DBQueryWarn("update geni_users set status='archived' ".
"where uid_idx='$uid_idx'")
or return -1;
return 0;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2005, 2006, 2007, 2008 University of Utah and the Flux Group.
# All rights reserved.
#
package Protogeni;
use strict;
use Exporter;
use vars qw(@ISA @EXPORT @EXPORT_OK
$RESPONSE_SUCCESS $RESPONSE_BADARGS $RESPONSE_ERROR
$RESPONSE_FORBIDDEN $RESPONSE_BADVERSION $RESPONSE_SERVERERROR
$RESPONSE_TOOBIG $RESPONSE_REFUSED $RESPONSE_TIMEDOUT);
@ISA = "Exporter";
@EXPORT = qw ( );
# Must come after package declaration!
use lib '@prefix@/lib';
use libdb;
$RESPONSE_SUCCESS = 0;
$RESPONSE_BADARGS = 1;
$RESPONSE_ERROR = 2;
$RESPONSE_FORBIDDEN = 3;
$RESPONSE_BADVERSION = 4;
$RESPONSE_SERVERERROR = 5;
$RESPONSE_TOOBIG = 6;
$RESPONSE_REFUSED = 7; # Emulab is down, try again later.
$RESPONSE_TIMEDOUT = 8;
@EXPORT_OK = qw($RESPONSE_SUCCESS $RESPONSE_BADARGS $RESPONSE_ERROR
$RESPONSE_FORBIDDEN $RESPONSE_BADVERSION $RESPONSE_SERVERERROR
$RESPONSE_TOOBIG $RESPONSE_REFUSED $RESPONSE_TIMEDOUT);
#
# This is the "structure" we want to return.
#
# class Response:
# def __init__(self, code, value=0, output=""):
# self.code = code # A RESPONSE code
# self.value = value # A return value; any valid XML type.
# self.output = output # Pithy output to print
# return
#
sub MakeResponse($$;$)
{
my ($code, $value, $output) = @_;
$output = ""
if (!defined($output));
return {"code" => $code,
"value" => $value,
"output" => $output};
}
sub MalformedAargsResponse()
{
return MakeResponse($RESPONSE_BADARGS, 0, "Malformed arguments to method");
}
sub BadArgsResponse(;$)
{
my ($msg) = @_;
$msg = "Bad arguments to method"
if (!defined($msg));
return MakeResponse($RESPONSE_BADARGS, 0, $msg);
}
sub add ($$)
{
return MakeResponse(0, $_[0] + $_[1], "foo");
}
#############################################################################
package Protogeni::User;
use emdbi;
use English;
use User;
use GeniUser;
##
# Lookup a GID (UUID) and return the public key for that user.
# A GID is just a UUID that has been bound to some data, such as a public
# key.
#
# @param GID the GID of the user to lookup.
# @return the public key bound to the user GID, or error if no user.
#
sub LookupKey($)
{
my ($uuid) = @_;
if (! ($uuid =~ /^[-\w]*$/)) {
return Protogeni::MalformedArgsResponse();
}
my $user = User->LookupByUUID($uuid);
if (defined($user)) {
#
# A local Emulab user. Return that key.
#
return Protogeni::MakeResponse(0, 2);
}
return Protogeni::BadArgsResponse("No such user for GID")
if (!defined($user));
#
# We want to return the encrypted GENI certificate.
#
return Protogeni::MakeResponse(0, 1);
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -20,11 +20,11 @@ SETUID_LIBX_SCRIPTS =
# Force dependencies on the scripts so that they will be rerun through
# configure if the .in file is changed.
#
all: protogeni.py
all: protogeni.py protogeni-client.py protogeni.pl
include $(TESTBED_SRCDIR)/GNUmakerules
install: $(INSTALL_DIR)/protogeni/xmlrpc/protogeni.py
install: $(INSTALL_DIR)/protogeni/xmlrpc/protogeni.pl
control-install:
......
#! /usr/bin/env python
#
# EMULAB-COPYRIGHT
# Copyright (c) 2008 University of Utah and the Flux Group.
# All rights reserved.
#
# Permission to use, copy, modify and distribute this software is hereby
# granted provided that (1) source code retains these copyright, permission,
# and disclaimer notices, and (2) redistributions including binaries
# reproduce the notices in supporting documentation.
#
# THE UNIVERSITY OF UTAH ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
# CONDITION. THE UNIVERSITY OF UTAH DISCLAIMS ANY LIABILITY OF ANY KIND
# FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
#
import sys
import getopt
import os, os.path
import xmlrpclib
from M2Crypto.m2xmlrpclib import SSL_Transport
from M2Crypto import SSL
##
# The package version number
#
PACKAGE_VERSION = 0.1
# Default server and port
XMLRPC_SERVER = "@BOSSNODE@"
# User supplied server name.
xmlrpc_server = XMLRPC_SERVER
# The default path
path = "/protogeni/xmlrpc"
# Where to find the default certificate in the users home dir.
default_cert = "/.ssl/emulab.pem"
certificate = None;
# Debugging output.
debug = 0
# Raw XML mode
rawmode = 0
##
# Print the usage statement to stdout.
#
def usage():
print "Make a request to the Emulab XML-RPC (SSL-based) server."
print ("Usage: " + sys.argv[0]
+ " [-hV] [-s server] <method> [param ...]")
print
print "Options:"
print " -h, --help\t\t Display this help message"
print " -V, --version\t\t Show the version number"
print " -s, --server\t\t Set the server hostname"
print " -c, --cert\t\t Set the certificate to use"
print
print "Required arguments:"
print " method\t\t The method to execute on the server"
print " params\t\t\t The method arguments"
print
print "Example:"
print (" "
+ sys.argv[0]
+ " -s boss.emulab.net echo \"Hello World!\"")
return
#
# Process program arguments.
#
try:
# Parse the options,
opts, req_args = getopt.getopt(sys.argv[1:],
"dhVc:s:",
[ "help", "version", "server=",
"cert=", "path="])
# ... act on them appropriately, and
for opt, val in opts:
if opt in ("-h", "--help"):
usage()
sys.exit()
pass
elif opt in ("-V", "--version"):
print PACKAGE_VERSION
sys.exit()
pass
elif opt in ("-s", "--server"):
xmlrpc_server = val
#
# Allow port spec here too.
#
if val.find(":") > 0:
xmlrpc_server,xmlrpc_port = string.split(val, ":", 1)
pass
pass
elif opt in ("-c", "--cert"):
certificate = val
pass
elif opt in ("-d", "--debug"):
debug = 1
pass
elif opt in ("--path",):
path = val
pass
pass
pass
except getopt.error, e:
print e.args[0]
usage()
sys.exit(2)
pass
#
# Vanilla SSL CTX initialization.
#
if certificate == None:
certificate = os.environ["HOME"] + default_cert
pass
if not os.access(certificate, os.R_OK):
print "Certificate cannot be accessed: " + certificate
sys.exit(-1);
pass
ctx = SSL.Context('sslv23')
ctx.load_cert(certificate, certificate)
ctx.set_verify(SSL.verify_none, 16)
ctx.set_allow_unknown_ca(1)
# This is parsed by the Proxy object.
URI = "https://" + xmlrpc_server + ":443" + path
if debug:
print >>sys.stderr, URI
pass
# Get a handle on the server,
server = xmlrpclib.ServerProxy(URI, SSL_Transport(ctx));
# Invoke method and print response.
methodname = req_args[0]
meth = getattr(server, methodname)
req_args.pop(0);
#
# Make the call.
#
try:
response = apply(meth, req_args)
pass
except xmlrpclib.Fault, e:
print e.faultString
sys.exit(-1)
pass
print str(response)
sys.exit(0)
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2008 University of Utah and the Flux Group.
# All rights reserved.
#
#
# Simple CGI interfce to the GENI xmlrpc interface. This script is invoked
# from the web server. The certificate information is in the environment
# set up by apache.
#
use strict;
use Frontier::Responder;
# Testbed libraries.
use lib '@prefix@/lib';
use Protogeni;
#
# Turn off line buffering on output
#
$| = 1;
#
# Untaint the path
#
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# The UUID of the client certificate is in the env var SSL_CLIENT_S_DN_CN.
#
my $responder = Frontier::Responder->new( "methods" => {
"User::Lookup" => \&Protogeni::User::Lookup,
"add" => \&Protogeni::add,
},
);
print $responder->answer();
......@@ -5,25 +5,21 @@
# All rights reserved.
#
import sys
from xmlrpc import XmlRpcServer
from SimpleXMLRPCServer import CGIXMLRPCRequestHandler
class myexampleclass:
def method1 (self, meta, oneargument, anotherone):
print (meta, oneargument, anotherone)
return 0
return str(meta) + "," + str(oneargument)
def method2 (self, meta, *args):
sys.exit(0)
pass
pass
server = XmlRpcServer()
myinstance = myexampleclass()
server.register_class('myclass', myinstance)
sys.stdout.write("Content-type: text/xml\n\n")
server.execute()
server = CGIXMLRPCRequestHandler()
server.register_instance(myinstance)
server.handle_request()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment