Permalink
Browse files

integrate mkproxy; improve READMEs

  • Loading branch information...
1 parent 2f5c37f commit 11133eb416542e4926add5d9dcf4e29c854ee49a Bryce Allen committed Feb 3, 2012
View
@@ -2,3 +2,5 @@ build
MANIFEST
dist
*.pyc
+globusonline/transfer/api_client/x509_proxy/mkproxy
+mkproxy/mkproxy
View
@@ -1,3 +1,6 @@
include README.markdown
-recursive-include examples *.py
-recursive-include ca *.cert
+include mkproxy/mkproxy.c
+include mkproxy/Makefile
+include mkproxy/README.markdown
+recursive-include globusonline/transfer/api_client/examples *.py
+recursive-include globusonline/transfer/api_client/ca *.pem
View
@@ -1,24 +1,77 @@
This package contains a client library for the Globus Online Transfer API.
+For detailed documentation of the Transfer API, see
+[https://transfer.api.globusonline.org](https://transfer.api.globusonline.org)
+
+
+Installation
+============
+
+If you downloaded the source from github, simply run:
+
+ python setup.py install
+
+There is also a package on PyPI with the latest stable version; it can be
+installed with `easy_install` or `pip`:
+
+ easy_install globusonline-transfer-api-client
+
+
+Usage
+=====
+
+Basic usage:
+
from globusonline.transfer import api_client
api = api_client.TransferAPIClient(username="myusername",
- server_ca_file="/path/to/godaddy_ca.pem",
cert_file="/path/to/client/credential",
key_file="/path/to/client/credential")
status_code, status_message, data = api.task_list()
+See the `globusonline/transfer/api_client/examples` directory for more complete
+examples. If you installed from PyPI, this will be somewhere in your Python
+path:
+
+ python -c "from globusonline.transfer import api_client; print api_client.__path__"
+
One of the best ways to learn the library is to run an interactive interpreter
with an instance of the client. The module provides a shortcut for doing this:
- python -i -m globusonline.transfer.api_client.main USERNAME -p \
- -C ca/gd-bundle_ca.cert
+ python -i -m globusonline.transfer.api_client.main USERNAME -p
>>> status_code, status_message, data = api.task_list()
>>> dir(api) # get a list of all available methods
replace USERNAME with your Globus Online username, and you will be prompted
for your password. This form of authentication should not be used in production
systems, but is useful for development and testing.
-For detailed documentation of the Transfer API, see
-[https://transfer.api.globusonline.org](https://transfer.api.globusonline.org)
+
+Changlog
+========
+
+0.10.10
+-------
+* Include CAs in the package; the `server_ca_file` parameter (and the -C
+ command line arg) are no longer required.
+* Alternate `delegate_proxy` activation implementation using a custom C
+ program called `mkproxy` instead of M2Crypto. See `mkproxy/README.markdown`
+ for details. `mkproxy` is the preferred implementations, so if both the
+ executable and M2Crypto are installed, `mkproxy` is used.
+* Moved examples to package data, so they are included in the PyPI package.
+
+0.10.9
+------
+
+* Add https proxy support, using the `HTTPS_PROXY` environment variable.
+ This has been tested in 2.6.6 and 2.7, and does not work in 2.6.1
+ (because the tunnel features was added in the middle of the 2.6.X
+ cycle). Other versions > 2.6.1 may also work, but this has not been
+ tested. Thanks to Brett Viren for this feature!
+* If you have both your key and certificate in the same file, you
+ don't have to pass it to both -c and -k when running the examples and
+ interactive client. Just pass one of them, and it will assume the
+ file contains both.
+* Added some basic usage docs to `examples/delegate_proxy_activate.py`
+* Fix example.py breakage when printing GC endpoints.
+* Import readline in main.py, for more convenient interactive testing.
@@ -20,7 +20,6 @@
ipython -- transfer_api.py USERNAME -k ~/.globus/userkey.pem \
-c ~/.globus/usercert.pem \
- -C ../gd-bundle_ca.cert
OR
@@ -43,7 +42,6 @@
import urllib
import time
import ssl
-import struct
import traceback
from urlparse import urlparse
from httplib import BadStatusLine
@@ -86,7 +84,7 @@ class TransferAPIClient(object):
the logged in user's endpoints.
"""
- def __init__(self, username, server_ca_file,
+ def __init__(self, username, server_ca_file=None,
cert_file=None, key_file=None, saml_cookie=None,
base_url=DEFAULT_BASE_URL,
timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
@@ -105,7 +103,9 @@ def __init__(self, username, server_ca_file,
@param username: username to connect to the service with.
@param server_ca_file: path to file containing one or more x509
certificates, used to verify the server
- certificate.
+ certificate. If not specified tries to choose
+ the appropriate CA based on the hostname in
+ base_url.
@param cert_file: path to file containing the x509 client certificate
for authentication.
@param key_file: path to file containg the RSA key for client
@@ -126,21 +126,27 @@ def __init__(self, username, server_ca_file,
will be raised. max_attempts=1 implies no
retrying.
"""
+ if server_ca_file is None:
+ server_ca_file = get_ca(base_url)
+ if server_ca_file is None:
+ raise InterfaceError("no CA found for base URL '%s'"
+ % base_url)
if not os.path.isfile(server_ca_file):
- raise InterfaceError("server_ca_file not found: %s"
+ raise InterfaceError("server_ca_file not found: '%s'"
% server_ca_file)
if saml_cookie and (cert_file or key_file):
- raise InterfaceError("pass either cooie or cert and key"
+ raise InterfaceError("pass either cookie or cert/key"
" files, not both.")
- if cert_file:
- if not os.path.isfile(cert_file):
- raise InterfaceError("cert_file not found: %s" % cert_file)
+ if cert_file or key_file:
if not key_file:
key_file = cert_file
- else:
- if not os.path.isfile(key_file):
- raise InterfaceError("key_file not found: %s" % key_file)
+ if not cert_file:
+ cert_file = key_file
+ if not os.path.isfile(cert_file):
+ raise InterfaceError("cert_file not found: %s" % cert_file)
+ if not os.path.isfile(key_file):
+ raise InterfaceError("key_file not found: %s" % key_file)
if max_attempts is not None:
max_attempts = int(max_attempts)
@@ -1021,13 +1027,6 @@ def process_args(args=None, parser=None):
if len(args) < 1:
parser.error("username arguments is required")
- if not options.server_ca_file:
- # Try to load the appropriate CA based on base url.
- options.server_ca_file = get_ca(options.base_url)
- if options.server_ca_file is None:
- parser.error("no CA found for base URL '%s', use -C to specify a CA"
- % options.base_url)
-
if options.password_prompt:
if options.saml_cookie or options.key_file or options.cert_file:
parser.error("use only one authentication method: -p, -k/-c, or -s")
@@ -1067,122 +1066,6 @@ def process_args(args=None, parser=None):
return options, args
-def get_random_serial():
- """
- Under RFC 3820 there are many ways to generate the serial number. However
- making the number unpredictable has security benefits, e.g. it can make
- this style of attack more difficult:
-
- http://www.win.tue.nl/hashclash/rogue-ca
- """
- return struct.unpack("<Q", os.urandom(8))[0]
-
-
-def create_proxy_from_file(issuer_cred_file, public_key, lifetime=3600):
- """
- Create a proxy of the credential in issuer_cred_file, using the
- specified public key and lifetime.
-
- @param issuer_cred_file: file containing a credential, including the
- certificate, public key, and optionally chain
- certs.
- @param public_key: the public key as a PEM string
- @param lifetime: lifetime of the proxy in seconds (default 1 hour)
- """
- with open(issuer_cred_file) as f:
- issuer_cred = f.read()
- return create_proxy(issuer_cred, public_key, lifetime)
-
-
-_begin_private_key = "-----BEGIN RSA PRIVATE KEY-----"
-_end_private_key = "-----END RSA PRIVATE KEY-----"
-
-# The issuer is required to have this bit set if keyUsage is present;
-# see RFC 3820 section 3.1.
-REQUIRED_KEY_USAGE = ["Digital Signature"]
-def create_proxy(issuer_cred, public_key, lifetime=3600):
- from M2Crypto import X509, RSA, EVP, ASN1, BIO
-
- # Standard order is cert, private key, then the chain.
- _begin_idx = issuer_cred.index(_begin_private_key)
- _end_idx = issuer_cred.index(_end_private_key) + len(_end_private_key)
- issuer_key = issuer_cred[_begin_idx:_end_idx]
- issuer_cert = issuer_cred[:_begin_idx]
- issuer_chain = issuer_cert + issuer_cred[_end_idx:]
-
- proxy = X509.X509()
- proxy.set_version(2)
- serial = get_random_serial()
- proxy.set_serial_number(serial)
-
- now = long(time.time())
- not_before = ASN1.ASN1_UTCTIME()
- not_before.set_time(now)
- proxy.set_not_before(not_before)
-
- not_after = ASN1.ASN1_UTCTIME()
- not_after.set_time(now + lifetime)
- proxy.set_not_after(not_after)
-
- pkey = EVP.PKey()
- tmp_bio = BIO.MemoryBuffer(str(public_key))
- rsa = RSA.load_pub_key_bio(tmp_bio)
- pkey.assign_rsa(rsa)
- del rsa
- del tmp_bio
- proxy.set_pubkey(pkey)
-
- issuer = X509.load_cert_string(issuer_cert)
-
- # If the issuer has keyUsage extension, make sure it contains all
- # the values we require.
- try:
- keyUsageExt = issuer.get_ext("keyUsage")
- if keyUsageExt:
- values = keyUsageExt.get_value().split(", ")
- for required in REQUIRED_KEY_USAGE:
- if required not in values:
- raise InterfaceError(
- "issuer contains keyUsage without required usage '%s'"
- % required)
- except LookupError:
- pass
-
- # hack to get a copy of the X509 name that we can append to.
- issuer_copy = X509.load_cert_string(issuer_cert)
- proxy_subject = issuer_copy.get_subject()
-
- proxy_subject.add_entry_by_txt(field="CN", type=ASN1.MBSTRING_ASC,
- entry=str(serial),
- len=-1, loc=-1, set=0)
- proxy.set_subject(proxy_subject)
- proxy.set_issuer(issuer.get_subject())
-
- # create a full proxy
- pci_ext = X509.new_extension("proxyCertInfo",
- "critical,language:Inherit all", 1)
- proxy.add_ext(pci_ext)
-
- # Clients may wish to add restrictions to the proxy that are not
- # present in the issuer. To do this, keyUsage and extendedKeyUsage
- # extensions can be added to the proxy; the effictive usage is
- # defined as the intersection of the usage. See section 4.2 of the
- # RFC. In the absense of application specific requirements, we
- # choose not to add either extension, in which case the usage of the
- # issuer(s) will be inherited as is. See the example below if you
- # wish to customize this behavior.
- #
- #ku_ext = X509.new_extension("keyUsage",
- # "Digital Signature, Key Encipherment, Data Encipherment", 1)
- #proxy.add_ext(ku_ext)
-
- issuer_rsa = RSA.load_key_string(issuer_key)
- sign_pkey = EVP.PKey()
- sign_pkey.assign_rsa(issuer_rsa)
- proxy.sign(pkey=sign_pkey, md="sha1")
- return proxy.as_pem() + issuer_chain
-
-
def create_client_from_args(args=None):
"""
Create a client instance according to options in command line
@@ -15,9 +15,9 @@
# limitations under the License.
"""
Demonstrate how to use the delegate_proxy activation method. Note that this
-method requires M2Crypto.
+method requires either M2Crypto or a custom C program called mkproxy, available from the project github (but not currently included in the PyPI package).
-In Fedora/RHEL/CentOS:
+To install M2Crypto in Fedora/RHEL/CentOS:
$ yum install m2crypto
@@ -47,7 +47,7 @@
import sys
from globusonline.transfer.api_client import create_client_from_args
-from globusonline.transfer.api_client import create_proxy_from_file
+from globusonline.transfer.api_client.x509_proxy import create_proxy_from_file
if __name__ == '__main__':
api, args = create_client_from_args()
@@ -18,9 +18,7 @@
Example run using standard globus toolkit certificate locations:
-python example.py USERNAME -k ~/.globus/userkey.pem \
- -c ~/.globus/usercert.pem \
- -C ../gd-bundle_ca.cert
+python example.py USERNAME -k ~/.globus/userkey.pem -c ~/.globus/usercert.pem
"""
import time
from datetime import datetime, timedelta
@@ -16,8 +16,7 @@
"""
Script for using the client in an interactive interpreter, e.g.
- python -i -m globusonline.transfer.api_client.main USERNAME -p \
- -C ca/gd-bundle_ca.cert
+ python -i -m globusonline.transfer.api_client.main USERNAME -p
It creates a TransferAPIClient instance called "api" with the credentials
passed on the command line, which you can use to make requests.
@@ -0,0 +1,34 @@
+# Copyright 2012 University of Chicago
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Library for creating proxy certificates. Two implementations are provided -
+one that uses M2Crypto, and another that uses a custom C program built against
+openssl.
+"""
+from globusonline.transfer.api_client.x509_proxy import mkproxy
+
+__all__ = ["create_proxy", "create_proxy_from_file"]
+
+# Favor the C program implementation if available.
+if mkproxy.get_mkproxy_path():
+ implementation = "mkproxy"
+ from globusonline.transfer.api_client.x509_proxy.mkproxy \
+ import create_proxy_from_file
+else:
+ # raises ImportError if M2Crypto is not available
+ from globusonline.transfer.api_client.x509_proxy.m2 \
+ import create_proxy_from_file
+ implementation = "m2"
+
+print "proxy implementation:", implementation
Oops, something went wrong.

0 comments on commit 11133eb

Please sign in to comment.