Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

executable file 408 lines (343 sloc) 14.732 kB
#!/usr/bin/env python
#
# Performs a number of PaySwarm client commands.
import sys
sys.path.insert(0, "lib")
from argparse import ArgumentParser
import hashlib
import json
import os
import time
import payswarm
import payswarm.config
USAGE = """%(prog)s [OPTIONS] COMMAND ...
Performs a variety of PaySwarm client operations such as registering
with a PaySwarm Authority, generating public/private keys, digitally
signing and registering assets for sale, registering listings,
establishing Payment Sessions and performing purchases.
Examples:
%(prog)s config [Configure a number of PaySwarm defaults]
%(prog)s register LISTING [Register a listing to be purchased]
%(prog)s purchase LISTING [Perform a purchase using the client]
************** %(prog)s command line options **************"""
DEFAULTS = \
{ \
"authority-url": "https://dev.payswarm.com/", \
"config-url": "https://dev.payswarm.com/client-config", \
"listings-url": "http://listings.dev.payswarm.com/" \
}
def _parse_options():
"""Get options from command line and return them."""
parser = ArgumentParser(usage=USAGE)
parser.add_argument(
"--authority-url", action="store", dest="authority-url",
default=DEFAULTS["authority-url"],
help="The PaySwarm Authority URL. "
"[Default: %(default)s]")
parser.add_argument(
"--config-url", action="store", dest="config-url",
default=None,
help="The PaySwarm Authority client configuration URL. "
"[Default: {AUTHORITY_URL}client-config]")
parser.add_argument(
"--listings-url", action="store", dest="listings-url",
default=DEFAULTS["listings-url"],
help="URL for Web Service that stores assets and listings. "
"[Default: %(default)s]")
parser.add_argument(
"-c", "--config", action="store",
default=None,
help="The configuration file to read in addition to the regular "
"configuration files. "
"[Default: %(default)s]")
options, args = parser.parse_args()
options.args = args
# Check to make sure that there are enough options
if not options.args:
sys.stderr.write( \
"ERROR: You must provide at least one command to execute.\n")
parser.print_help()
sys.exit(1)
return options
def _ask(question, config=None, section=None, name=None):
"""Asks a question on the command line using a default value if one exists.
question - the question to ask on the command line.
config - the configuration to read the default values from.
section - the section containing the option to store.
name - the name of the option to store."""
rval = ""
default = ""
# Check to see if there is a default value for the option
if config != None and config.has_option(section, name):
default = config.get(section, name)
# Attempt to get the option from the command line
while not rval:
if default:
sys.stdout.write(question + " [Default: %s] " % default)
sys.stdout.flush()
rval = sys.stdin.readline().strip()
if not rval:
rval = default
else:
sys.stdout.write(question + " ")
sys.stdout.flush()
rval = sys.stdin.readline().strip()
if not rval:
sys.stdout.write("Warning: You must enter something.\n")
sys.stdout.flush()
return rval
def _configure(config, options):
"""Performs basic PaySwarm client configuration.
Asks a number of questions on the command line, contacts the PaySwarm
Authority and configures all OAuth tokens and public/private keys.
"""
sys.stdout.write("""
In order to fully configure this PaySwarm client, you will need to use a
Web Browser. Go to the following URL and create an Application profile:
https://dev.payswarm.com/profile/applications
""")
# Get the registration token and secret
client_id = _ask("Application client ID?",
config, "application", "client-id")
secret = _ask("Application secret?",
config, "application", "client-secret")
payswarm.config.set_oauth_credentials(config, client_id, secret)
# Retrieve the basic PaySwarm endpoints
sys.stdout.write( \
"Retrieving basic OAuth web service endpoints...\n")
payswarm.config.set_basic_endpoints(config)
# Authorize the registration token
client = payswarm.PaySwarmClient(config, client_id, secret)
url = client.generate_registration_url()
sys.stdout.write( \
"Go to the following URL and authorize your application:\n\n"
" %s\n\n" % url)
verifier = _ask("Enter the verification key:")
client.complete_registration(verifier)
# Retrieve the specialized application endpoints
sys.stdout.write( \
"Retrieving PaySwarm web service endpoints...\n")
payswarm.config.set_application_endpoints(client, config)
# Retrieve the application preferences
sys.stdout.write( \
"Retrieving application owner's PaySwarm account details...\n")
payswarm.config.set_application_preferences(client, config)
sys.stdout.write("Generating public/private keypair...\n")
sys.stdout.flush()
payswarm.config.generate_keys(config)
sys.stdout.write("Registering public key with PaySwarm Authority...\n")
sys.stdout.flush()
client.register_public_key()
sys.stdout.write("Configuration complete.\n")
payswarm.config.save(config, options)
def _register(config, options, lfilename):
"""Registers a given listing on the listing service.
config - the configuration object to get the listing service URL from.
lfilename - the filename containing the listing data.
"""
# Read the asset and listing data from the given file
try:
lfile = open(lfilename, "r")
ldata = json.loads(lfile.read())
asset = ldata[0]
listing = ldata[1]
# Register the loaded asset
sys.stdout.write("Registering asset:")
signed_asset = payswarm.storage.register_asset(config, asset)
sys.stdout.write(" %s\n" % signed_asset["id"])
# Register the loaded listing
sys.stdout.write("Registering listing:")
signed_listing = payswarm.storage.register_listing(\
config, signed_asset, listing)
sys.stdout.write(" %s\n" % signed_listing["id"])
except ValueError, e:
sys.stderr.write("Error: Failed to parse %s as JSON-LD\n" % lfilename)
sys.stderr.write(str(e) + "\n")
def _establish_payment_session(config, client, options):
"""Requests a new Payment Session with the PaySwarm Authority.
client - the OAuth client to use to establish the session.
Returns True if a session was established, False otherwise.
"""
rval = False
# Authorize the payment token, thus establishing a payment session
client.clear_token()
url = client.generate_payment_session_url()
sys.stdout.write( \
"Go to the following URL and authorize the Payment Session:\n\n"
" %s\n\n" % url)
verifier = _ask("Enter the verification key:")
client.authorize_payment_session(verifier)
payswarm.config.save(config, options)
rval = True
return rval
def _purchase(config, options, lfilename):
"""Performs PaySwarm purchase given a config and listing filename.
config - the configuration to use when configuring the PaySwarm client.
lfilename - the name of the listing file to use when generating the
purchase request.
"""
client_id = config.get("application", "client-id")
client_secret = config.get("application", "client-secret")
client = payswarm.PaySwarmClient(config, client_id, client_secret)
# ensure that there is an active payment session in place
if not config.has_option("client", "payment-token") or \
not config.has_option("client", "payment-token-secret"):
_establish_payment_session(config, client, options)
else:
client.set_token(config.get("client", "payment-token"),
config.get("client", "payment-token-secret"))
# read the listing information from the file
lfile = open(lfilename, "r")
ldata = json.loads(lfile.read())
asset = payswarm.storage.fetch(config, ldata[0])
listing = payswarm.storage.fetch(config, ldata[1])
# calculate the listing hash from the retrieved listing
# FIXME: The issue with calculating the listing hash in this way is
# that an invalid listing hash will never be a possibility. The
# listing hash will always be valid, even if it is changed without
# the client's knowledge, which could result in a purchase of
# something that the client didn't intend to purchase.
listing_url = listing["id"]
listing_hash = payswarm.util.hash(listing)
# attempt to purchase the listing up to 3 times
attempts = 0
max_attempts = 3
purchase_success = False
while not purchase_success and attempts < max_attempts:
try:
purchase = client.purchase(listing_url, listing_hash)
purchase_success = True
# display the item purchased and remaining balance
if purchase["authorized"] == "true":
sys.stdout.write("Purchase of '%s' successful.\n" % \
(asset["dc:title"],))
sys.stdout.write("$%s remaining for session.\n" % \
(purchase["balance"],))
except Exception, e:
sys.stderr.write("Error: %s" % e)
attempts += 1
# if there was an error, re-establish a payment session
if attempts < max_attempts:
_establish_payment_session(config, client, options)
def _establish_system_session(config, client, options):
"""Requests a new system session with the PaySwarm Authority.
client - the OAuth client to use to establish the session.
Returns True if a session was established, False otherwise.
"""
rval = False
# Authorize the token, thus establishing a session
client.clear_token()
url = client.generate_system_session_url()
sys.stdout.write( \
"Go to the following URL and authorize the system session:\n\n"
" %s\n\n" % url)
verifier = _ask("Enter the verification key:")
client.authorize_system_session(verifier)
payswarm.config.save(config, options)
rval = True
return rval
def _system(config, options, command):
"""Performs a PaySwarm system service call.
config - the configuration to use when configuring the PaySwarm client.
command - the command to call.
params - optional params for the command.
"""
client_id = config.get("application", "client-id")
client_secret = config.get("application", "client-secret")
client = payswarm.PaySwarmClient(config, client_id, client_secret)
# ensure that there is an active session in place
if not config.has_option("client", "system-token") or \
not config.has_option("client", "system-token-secret"):
_establish_system_session(config, client, options)
else:
client.set_token(config.get("client", "system-token"),
config.get("client", "system-token-secret"))
command_url = config.get("general", "authority-url") + "system/" + command
# attempt to make system call
attempts = 0
max_attempts = 3
success = False
while not success and attempts < max_attempts:
try:
result = client.call(command_url, "error")
try:
print json.dumps(result, sort_keys=True, indent=None)
except:
print result
success = True
except Exception, e:
sys.stderr.write("Error: %s" % e)
attempts += 1
# if there was an error, re-establish a payment session
if attempts < max_attempts:
_establish_system_session(config, client, options)
if __name__ == "xxx__main__":
# Get the options from the command line
options = _parse_options()
# Get configuration options from /etc/payswarm1.cfg and ~/.payswarm1
config = payswarm.config.parse(DEFAULTS, options)
for command in options.args:
if command == "config":
_configure(config, options)
elif command == "test":
print 'test'
elif command == "system":
_system(config, options, options.args[-1])
elif command == "register":
if options.args[-1].endswith(".jsonld"):
_register(config, options, options.args[-1])
else:
sys.stderr.write("Error: You must specify a listing file "
"ending in .jsonld to register\n")
elif command == "purchase":
if options.args[-1].endswith(".jsonld"):
_purchase(config, options, options.args[-1])
else:
sys.stderr.write("Error: You must specify a listing file "
"ending in .jsonld for the purchase.\n")
elif command == "receipt":
pass
class App(object):
def __init__(self, config_plugin):
self._plugins = {}
self._config_plugin = config_plugin
self.add_plugin(config_plugin)
def add_plugin(self, plugin):
self._plugins[plugin.get_name()] = plugin
def load_plugins(self):
pythonCfg = self._config_plugin.main_config.get('python', {})
for path in pythonCfg.get('pluginPath', []):
sys.path.insert(0, path)
for name in pythonCfg.get('plugins', []):
# import by name
m = __import__(name)
m.init_plugin(app)
def run(self):
# setup arg parser
parser = ArgumentParser()
subparsers = parser.add_subparsers(
title='commands',
description='Valid comands')
# setup parsers
for p in self._plugins.values():
p.before_args_parsed(parser, subparsers)
# parse args
args = parser.parse_args()
# handle parsed args
for p in self._plugins.values():
p.after_args_parsed(args)
# call action
args.func(args)
if __name__ == "__main__":
# bootstrap config with file configs
config_plugin = payswarm.config.Files()
# create app with the config plugin
app = App(config_plugin)
# add core plugins
app.add_plugin(config_plugin)
#app.add_plugin(payswarm.keys.Keys())
app.add_plugin(payswarm.storage.Storage())
# load plugins
app.load_plugins()
# plugins should now be loaded, do real run
app.run()
Jump to Line
Something went wrong with that request. Please try again.